How can I upload a file using cakephp? is there any framework support for file uploading or should I write my own code for this ?
Edit (2015): Please see the Awesome CakePHP list for current file plugins (2.x branch here)
Original answer:
CakePHP upload plugins in active development (as of Oct 2010):
David Persson's Media Plugin [slides]
WebTechNick's CakePHP File Upload Handling Plugin [blog post]
Miles Johnson's Uploader Plugin [website]
Meio Código's MeioUpload 2.0 Behavior Plugin [website]
You could also use the File class, but I wouldn't reinvent the wheel on this one.
both is possible
for beginners this is probably the better choice:
http://www.milesj.me/resources/script/uploader-plugin
This component could help you out: http://cakeforge.org/snippet/detail.php?type=snippet&id=36.
Allows uploads to either a database or a directory using FTP. I have some experience with CakePHP, however I have not yet tried this component.
To just get going try this.
I spent two days searching for a simple way to upload files, I tried lots of methods and couldn't get any to work. This works. It is not secure, it is super basic. For me it is now a springboard. I would use this to understand the processes. Then you can build it up in complexity.
For me I struggled with trying to save $this->data - but of cause it is not like the cakePHP blog tutorial. The data you want (all the file info) is buried a couple of levels down in nested arrays so $this->data['Doc']['files'] is what you are after.
SQL
CREATE TABLE IF NOT EXISTS `docs` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(300) NOT NULL,
`type` varchar(300) NOT NULL,
`tmp_name` varchar(300) NOT NULL,
`error` tinyint(1) NOT NULL,
`size` varchar(100) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=7 ;
MODEL
<?php
class Doc extends AppModel {
}
?>
VIEW
<h1>Uploads</h1>
<table>
<tr>
<th>ID</th><th>File Name</th><th>Size</th>
</tr>
<?php foreach($files as $file): ?>
<tr>
<td><?php echo $file['Doc']['id'];?></td>
<td><?php echo $this->Html->link($file['Doc']['name'],array('controller' => 'files','action'=>'uploads',$file['Doc']['name']));?></td>
<td><?php echo number_format($file['Doc']['size']/1023,0).' KB';?></td>
</tr>
<?php endforeach;?>
</table>
<h1>Add a File</h1>
<?php
echo $this->Form->create('Doc',array('type'=>'file'));
echo $this->Form->file('File');
echo $this->Form->submit('Upload');
echo $this->Form->end();
?>
CONTROLLER
<?php
class DocsController extends AppController
{
public $helpers = array('Html','Form','Session');
public function index()
{
// -- list the files -- //
$this->set('files',$this->Doc->find('all'));
// -- Check for error -> Upload file to folder -> Add line to database -- //
if($this->request->is('post'))
{
if($this->data['Doc']['File']['error']=='0')
{
if(file_exists('files/uploads/' . $this->data['Doc']['File']['name']))
{
$this->Session->setFlash('A file called ' .$this->data['Doc']['File']['name']. ' already exists');
} else {
move_uploaded_file($this->data['Doc']['File']['tmp_name'], 'files/uploads/' . $this->data['Doc']['File']['name']);
}
$this->Doc->save($this->data['Doc']['File']);
$this->redirect(array('action'=>'index'));
}
}
}
}
?>
Related
In Typo3 I have my custom content elements (CEs) defined like so, for example:
CREATE TABLE tt_content
(
my_rich_text_relation int(11) unsigned DEFAULT '0',
);
# Custom CE for displaying rich text.
CREATE TABLE tx_mythemes_rich_text
(
uid int(11) NOT NULL auto_increment,
pid int(11) DEFAULT '0' NOT NULL,
text text,
....
);
And:
lib.myContentElement = FLUIDTEMPLATE
lib.myContentElement {
templateRootPaths.0 = EXT:my_themes/Resources/Private/Templates/ContentElements/
partialRootPaths.0 = EXT:my_themes/Resources/Private/Partials/ContentElements/
layoutRootPaths.0 = EXT:my_themes/Resources/Private/Layouts/ContentElements/
}
tt_content {
my_rich_text >
my_rich_text =< lib.myContentElement
my_rich_text {
templateName = RichText
dataProcessing {
330 = TYPO3\CMS\Frontend\DataProcessing\DatabaseQueryProcessor
330 {
table = tx_mythemes_rich_text
pidInList = this
where = uid=
where.dataWrap = |{field:mythemes_rich_text_relation}
as = richText
}
}
}
This works in my local system (Docker image) but I don't know why in our beta platform (where the context is not "development" it doesn't. When I add a new custom CE and refresh the caches, it gets displayed, but as soon as something changes (e.g. I add a new CE to the page), the Template of the CE doesn't get rendered (funny thing: the Layout does get rendered).
What can be happening? This looks so much like a cache problem.
Thanks in advance!
I have this code on my view,
<div class="text-id-box">
<?php echo CHtml::textField($model,'id'); ?>
</div>
<div class="submit-box">
<?php echo CHtml::button('Search', array(
'submit' => array('SiteController/actionSearch',
array('id'=>$id)))); ?>
</div>
and my database:
id: varchar(8)
name: varchar(20)
location: varchar(50)
i wanna search id in my database, then return name, and location value. how to build its model and controller.
any help would be appreciated, thanks.
first generate the model using gii ref (http://www.yiiframework.com/doc/guide/1.1/en/topics.gii)
then you can add following function in your controller
public function actionSearch($id)
{
$model= new Yourmodel;
$model=Yourmodel::model()->findByPk($id);
//you will get the row for particular id
// and you can access it by $model->name $model->location
// render the required page and access to model object to get the name and location.
}
As CodeIgniter comes out of the box, it supportes one controller for each unique url.
So, let's say i have the following urls in my routes.php file:
$route['default_controller'] = "homepage";
$route['404_override'] = "homepage/not_found";
$route['^products$'] = "product/list";
$route['^product/(:any)$'] = "product/details";
My urls will look like this:
product list => http://www.mywebsite.com/products
product details => http://www.mywebsite.com/product/my-custom-product
In the controller where I build the products list, I create the url for each product like this:
$this->db->from('products');
$products_result = $this->db->get();
$data['products'] = array();
foreach ($products_result->result() as $row)
{
$data['products'][] = array(
'title' => $row->title,
'image' => $row->image,
'url' => site_url('product/' . $row->url)
);
}
$this->load->view('products/list_view', $data);
But there is too much redundancy for each url. I have to write the url every time I want to echo it somewhere. If I want to change the url, I have to open each php file and replace them all.
Question: Isn't there a "method" I can use to call the controller name and its method and that "method" returns the url for that one? Something like this:
build_site_url('product/details', array('my-product-url'));
where the first argument is the controller and its method (since only one controller exists for a url pattern), and the second argument is the array of url parts.
What you're looking for is a feature called reverse routing. There is no such feature built into CodeIgniter at this time, and any library I could find was done in like 2010, so probably not the most up-to-date.
However, there are articles and pull requests out there relating to reverse routing, so if you are experienced enough, you should be able to put something together for your application.
Otherwise, your best bet may be to create helpers for your most common URLs (like your products). So you could do something like echo product_url('my-product-name');, and you would only need to adjust the URL in the helper function.
I am not sure what exactly that you desired, but you can override controller behaviour using _remap() function;
look: http://ellislab.com/codeigniter/user-guide/general/controllers.html#remapping
e.g.
public function _remap($method){
if ($method == 'product/details') {
///do what you want to do
}
You could either use a default lookup function in the product controller (described in the CI Routing docs; look at the examples).
$route['product/(:any)'] = "catalog/product_lookup";
A URL with "product" as the first segment, and anything in the second
will be remapped to the "catalog" class and the "product_lookup"
method.
Or you could use database driven routes.
Add the table product_slugs to your MySQL database:
CREATE TABLE IF NOT EXISTS `product_slugs` (
`id` bigint(20) NOT NULL auto_increment,
`slug` varchar(192) collate utf8_unicode_ci NOT NULL
PRIMARY KEY (`id`),
KEY `slug` (`slug`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=1;
Replace the code in application/config/routes.php with the one below:
$route[ 'default_controller' ] = 'main';
$route[ '404_override' ] = 'error404';
require_once( BASEPATH .'database/DB'. EXT );
$db =& DB();
$query = $db->get( 'product_slugs' );
$result = $query->result();
foreach( $result as $row )
{
$route[ $row->slug ] = 'product/detail/$1;
}
All you would have to do then is to create a record when you create a product entry and you're done:
INSERT INTO `product_slugs` (`slug`) VALUES ('name-of-the-product');
Submitting the data only reloads the page, no errors or messages is given by CakePHP.
The code follows the same/similar structure as the blog tutorial.
The view code
<?php
echo $this->Form->create('Sm');
echo $this->Form->input('recievers', array('rows' => '1'));
echo $this->Form->input('subject');
echo $this->Form->input('message');
echo $this->Form->end('SEND');
?>
Controller code
public function send() {
if ($this->request->is('sm')) {
$this->Sm->create();
if ($this->Sm->save($this->request->data)) {
$this->Session->setFlash('Sms has been added to the database');
$this->redirect(array('action' => 'index'));
} else {
$this->Session->setFlash('Unable to send sms.');
}
}
}
Model code
class Sm extends AppModel {
public $validate = array(
'subject' => array(
'rule' => 'notEmpty'
),
'message' => array(
'rule' => 'notEmpty'
),
'recievers' => array(
'rule' => 'notEmpty'
)
); }
Exported SQL
CREATE TABLE IF NOT EXISTS `sms` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`subject` varchar(150) DEFAULT NULL,
`message` text,
`sender` varchar(50) DEFAULT NULL,
`recievers` text,
`sent` datetime DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=7 ;
You've specified an incorrect request 'method';
if ($this->request->is('sm')) {
Should be:
if ($this->request->is('post')) {
I suspect you got confused by the examples on CakePHP, which use a 'post' Model. However this line is to check the type of request used to access the page, e.g. post, get, put.
By checking for the right request method, CakePHP will only insert/update the data is the form is sent/submitted, otherwise the form is just shown without updating the database
You forget to define name property inside model.
var $name = 'Sm';
Just a few minutes ago, I was facing same type of problem. My problem was more weird. In my controller I used something like this:
if( $this->MyModel->save( $this->request->data ) ) {
//positive. proceed.....
}
else {
//negative. show error
pr( $this->MyModel->validationErrors );
}
As you can see I've handled the negative case but still I could see nothing. Even in my model, I used beforeSave and afterSave to check. In my beforeSave model, I could see a perfectly formatted array ready to be saved but I afterSave was not triggering which means the data was not saved. Hence I should see error from my controller. Still no solution.
Now this is how I figured out the problem. I checked the table the data is gonna be saved into. The table had lots of columns with NOT NULL attribute. The data I was saving had some fields with NULL values. So theoretically, I should see validationErrors in the controller as a reason but unfortunately it was not showing. Setting those fields nullable solved my problem. So my suggestion to you is to check which fields might have NULL value and set those nullable and make sure the NOT NULL fields have some values.
Hope this helps. Cheers!!!
We need to create a file management interface that integrates with the clients current website built on cakephp.
The file manager must only let the users see the files that they have permissions too.
The user will be able to upload files(of any size) and have other users download those files (if permissions allow).
This is no problem for many file management systems that we can purchase but none that I can find will integrate with their current log in system. The client only wants the users to log in once to access their user CP and their files. So as far as I can see our only option is to build a file management interface ourselves.
What are some of the options that we have to allow uploads using PHP? I understand that we need to increase the php upload limit, but is there a ceiling that php/apache will allow?
The files will likely cap out at approximately 150MB if that is relevant, however there may be situations where the file sizes will be larger.
Also what are the do's and don'ts of file upload/control on a Linux server?
I suppose I have no real 'specific' questions but would like some advise on where to start and some of the typical pitfalls we will run into.
File management is quite easy, actually. Here are some suggestions that may point you in the right direction.
First of all, if this is a load balanced web server situation, you'll need to step up the complexity a little in order to put the files in one common place. If that's the case, ping me and I'll be happy to send you our super-light file server/client we use for that same situation.
There are a few variables you will want to affect in order to allow larger uploads. I recommend using apache directives to limit these changes to a particular file:
<Directory /home/deploy/project/uploader>
php_value max_upload_size "200M"
php_value post_max_size "200M"
php_value max_input_time "1800"
# this one depends on how much processing you are doing to the file
php_value memory_limit "32M"
</Directory>
Architecture:
Create a database table that stores some information about each file.
CREATE TABLE `File` (
`File_MNID` int(10) unsigned NOT NULL AUTO_INCREMENT,
`Owner_Field` enum('User.User_ID', 'Resource.Resource_ID') NOT NULL,
`Owner_Key` int(10) unsigned NOT NULL,
`ContentType` varchar(64) NOT NULL,
`Size` int(10) NOT NULL,
`Hash` varchar(40) NOT NULL,
`Name` varchar(128) NOT NULL,
PRIMARY KEY (`File_MNID`),
KEY `Owner` (`Owner_Field`,`Owner_Key`)
) ENGINE=InnoDB
What is Owner_Field and Owner_Key? A simple way to say what "entity" owns the file. In this specific case there were multiple types of files being uploaded. In your case, a simple User_ID field may be adequate.
The purpose of storing the owner is so that you can restrict who can download and delete the file. That will be crucial for protecting the downloads.
Here is a sample class that can be used to accept the file uploads from the browser. You'll need to modify it to suit, of course.
There are a few things to note about the following code. Since this is used with an Application Server and a File Server, there are a few things to "replace".
Any occurrences of App::CallAPI(...) will need to be replaced with a query or set of queries that do the "same thing".
Any occurrences of App::$FS->... will need to be replaced with the correct file handling functions in PHP such as move_uploaded_file, readfile, etc...
Here it is. Keep in mind that there are functions here which allow you to see files owned by a given user, delete files, and so on and so forth. More explanation at the bottom...
<?php
class FileClient
{
public static $DENY = '/\.ade$|\.adp$|\.asp$|\.bas$|\.bat$|\.chm$|\.cmd$|\.com$|\.cpl$|\.crt$|\.exe$|\.hlp$|\.hta$|\.inf$|\.ins$|\.isp$|\.its$| \.js$|\.jse$|\.lnk$|\.mda$|\.mdb$|\.mde$|\.mdt,\. mdw$|\.mdz$|\.msc$|\.msi$|\.msp$|\.mst$|\.pcd$|\.pif$|\.reg$|\.scr$|\.sct$|\.shs$|\.tmp$|\.url$|\.vb$|\.vbe$|\.vbs$|vsmacros$|\.vss$|\.vst$|\.vsw$|\.ws$|\.wsc$|\.wsf$|\.wsh$/i';
public static $MAX_SIZE = 5000000;
public static function SelectList($Owner_Field, $Owner_Key)
{
$tmp = App::CallAPI
(
'File.List',
array
(
'Owner_Field' => $Owner_Field,
'Owner_Key' => $Owner_Key,
)
);
return $tmp['Result'];
}
public static function HandleUpload($Owner_Field, $Owner_Key, $FieldName)
{
$aError = array();
if(! isset($_FILES[$FieldName]))
return false;
elseif(! is_array($_FILES[$FieldName]))
return false;
elseif(! $_FILES[$FieldName]['tmp_name'])
return false;
elseif($_FILES[$FieldName]['error'])
return array('An unknown upload error has occured.');
$sPath = $_FILES[$FieldName]['tmp_name'];
$sHash = sha1_file($sPath);
$sType = $_FILES[$FieldName]['type'];
$nSize = (int) $_FILES[$FieldName]['size'];
$sName = $_FILES[$FieldName]['name'];
if(preg_match(self::$DENY, $sName))
{
$aError[] = "File type not allowed for security reasons. If this file must be attached, please add it to a .zip file first...";
}
if($nSize > self::$MAX_SIZE)
{
$aError[] = 'File too large at $nSize bytes.';
}
// Any errors? Bail out.
if($aError)
{
return $aError;
}
$File = App::CallAPI
(
'File.Insert',
array
(
'Owner_Field' => $Owner_Field,
'Owner_Key' => $Owner_Key,
'ContentType' => $sType,
'Size' => $nSize,
'Hash' => $sHash,
'Name' => $sName,
)
);
App::InitFS();
App::$FS->PutFile("File_" . $File['File_MNID'], $sPath);
return $File['File_MNID'];
}
public static function Serve($Owner_Field, $Owner_Key, $File_MNID)
{
//Also returns the name, content-type, and ledger_MNID
$File = App::CallAPI
(
'File.Select',
array
(
'Owner_Field' => $Owner_Field,
'Owner_Key' => $Owner_Key,
'File_MNID' => $File_MNID
)
);
$Name = 'File_' . $File['File_MNID'] ;
//Content Header for that given file
header('Content-disposition: attachment; filename="' . $File['Name'] . '"');
header("Content-type:'" . $File['ContentType'] . "'");
App::InitFS();
#TODO
echo App::$FS->GetString($Name);
}
public static function Delete($Owner_Field, $Owner_Key, $File_MNID)
{
$tmp = App::CallAPI
(
'File.Delete',
array
(
'Owner_Field' => $Owner_Field,
'Owner_Key' => $Owner_Key,
'File_MNID' => $File_MNID,
)
);
App::InitFS();
App::$FS->DelFile("File_" . $File_MNID);
}
public static function DeleteAll($Owner_Field, $Owner_Key)
{
foreach(self::SelectList($Owner_Field, $Owner_Key) as $aRow)
{
self::Delete($Owner_Field, $Owner_Key, $aRow['File_MNID']);
}
}
}
Notes:
Please keep in mind that this class DOES NOT implement security. It assumes that the caller has an authenticated Owner_Field and Owner_Key before calling FileClient::Serve(...) etc...
It's a bit late, so if some of this doesn't make sense, just leave a comment. Have a great evening, and I hope this helps some.
PS. The user interface can be simple tables and file upload fields, etc.. Or you could be fancy and use a flash uploader...
I recommend having a look at the following community-contributed CakePHP code examples:
Uploader Plugin
WhoDidIt Behavior
Traceable Behavior
here is good choice. It requires you install a PC client and a single file php server. But it runs fast!
http://the-sync-star.com/