How to upload a file using a model

54 followers

The Model

First declare an attribute to store the file name in the model class (either a form model or an active record model). Also declare a file validation rule for this attribute to ensure a file is uploaded with specific extension name.

class Item extends CActiveRecord
{
    public $image;
    // ... other attributes
 
    public function rules()
    {
        return array(
            array('image', 'file', 'types'=>'jpg, gif, png'),
        );
    }
}

You can add others validation parameters as described in CFileValidator. For instance, one can add a "maxSize" restriction (the PHP ini settings will of course prevail).

The Controller

Then, in the controller class define an action method to render the form and collect user-submitted data.

class ItemController extends CController
{
    public function actionCreate()
    {
        $model=new Item;
        if(isset($_POST['Item']))
        {
            $model->attributes=$_POST['Item'];
            $model->image=CUploadedFile::getInstance($model,'image');
            if($model->save())
            {
                $model->image->saveAs('path/to/localFile');
                // redirect to success page
            }
        }
        $this->render('create', array('model'=>$model));
    }
}

CUploadedFile::saveAs() in one of the methods of CUploadedFile. You can also access directly to the file through its "tempName" property.

The View

Finally, create the action view and generate a file upload field.

$form = $this->beginWidget(
    'CActiveForm',
    array(
        'id' => 'upload-form',
        'enableAjaxValidation' => false,
        'htmlOptions' => array('enctype' => 'multipart/form-data'),
    )
);
// ...
echo $form->labelEx($model, 'image');
echo $form->fileField($model, 'image');
echo $form->error($model, 'image');
// ...
$this->endWidget();

Another syntax is to use static calls in CHtml instead of CActiveForm. The result is the same as above.

<?php echo CHtml::form('','post',array('enctype'=>'multipart/form-data')); ?>
...
<?php echo CHtml::activeFileField($model, 'image'); ?>
...
<?php echo CHtml::endForm(); ?>

Links

Total 20 comments

#13600 report it
gaurish at 2013/06/10 10:06am
include(CUploadedfile.php): failed to open stream: No such file or directory

Hi, I tried with give example. I am using Yii 1.1.13... I got the error as

include(CUploadedfile.php): failed to open stream: No such file or directory

all the files are at correct place...

#13031 report it
kiran sharma at 2013/04/29 01:23pm
@Trejder

This article was written by me..

#13021 report it
Trejder at 2013/04/29 02:51am
@outrage, @CedSha, @kiran sharma

@outrage: If model's rules are correctly defined, then if($model->save()) preforms validation and does not allow to save model, if there are any validation errors. That's how ActiveRecord works ever since and this has to work in either current or in any other version of framework. So, if you're able to upload TXT files, then you most certainly made mistake in your validators definition. I copied code from this wiki line-by-line and also wasn't able to upload TXT files. The example is OK.

@CedSha: if($model->save()) calls if($model->validate()) internally, so changing one to another won't fix anything, if validators are incorrectly defined. The only difference between these two functions is that first validates model and saves it to database, while second one only validates and does not perform any DB query. I simply can't believe that you didn't change anything except replacing if($model->save()) with if($model->validate()) and your code started to work.

@kiran sharma: Your example is wrong. You most certainly have to use if($model->validate()) instead of if($model->save()). If you use if($model->save()), model will be written to database (if it passes validation, of course), even if $model->image->saveAs('path/to/localFile'); or any other command following will fail. This way, you'll have empty (dead) entry in database, that does not reference any physical file, if for example moving file from temporary to permanent location fails.

In this or any other contexts there should be only page redirection after if($model->save()). All other operations, on database or related, must be performed before model is saved, that is after if($model->validate()), but before if($model->save()) to avoid situation, where database is updated even after error of different type, than validation error.

#11695 report it
CedSha at 2013/01/28 03:47am
Validation (Response at @outrage )

I had the same problem, after investigation I have solved that way :

...
$model->attributes=$_POST['MyForm'];
$model->filename=CUploadedFile::getInstance($model,'filename');
if ($model->validate()){
    $model->image->saveAs('path/to/localFile');

I think if your model is not an instance of CActiveRecord, then you wont be able to do the

if($model->save())

and I believe the validation step is made there. So you have to explicitly test the validation.

#10909 report it
GusDeCooL at 2012/12/02 01:23pm
File Handling Validation

Remember to check your CUploadFile, if null don't do saveAs or you will get an error

Here is sample of my code

public function actionCreate() {
        $this->layout = '//layouts/column1';
        $this->pageTitle = 'Create post for '.$this->pageTitle;
        $model = new Article;
 
        // Uncomment the following line if AJAX validation is needed
        // $this->performAjaxValidation($model);
 
        if (isset($_POST['Article'])) {
            $model->attributes = $_POST['Article'];
            $model->created = date('Y:m:d H:i:s');
            $model->group = $this->articleGroup;
            $model->fk_user_id = Yii::app()->user->id;
 
            // file handling
            $imageUploadFile = CUploadedFile::getInstance($model, 'image');
            if($imageUploadFile !== null){ // only do if file is really uploaded
                $imageFileName = mktime().$imageUploadFile->name;
                $model->image = $imageFileName;
            }           
 
            $voiceUploadFile = CUploadedFile::getInstance($model, 'voice_note');
            if($voiceUploadFile !== null) {
                $voiceFileName = mktime().$voiceUploadFile->name;
                $model->voice_note = $voiceFileName;
            }
 
 
            if ($model->save()){
                if($imageUploadFile !== null) // validate to save file
                    $imageUploadFile->saveAs(Article::FOLDER_IMAGE.$imageFileName);
 
                if($voiceUploadFile !== null)
                    $voiceUploadFile->saveAs(Article::FOLDER_VOICE.$voiceFileName);
 
                $this->redirect(array('view', 'id' => $model->id));
            }   
        }
 
        $this->render('//article/create', array(
            'model' => $model,
        ));
    }
#10200 report it
kiran sharma at 2012/10/10 02:40pm
@outrage

Sorry bro, but things work fine for me, (also tested with latest version v1.1.12) i use following rule in model,

..
array('image', 'file','types'=>'jpg, gif, png'),
..

and when i upload example.txt ( text file ) i got error like,

The file "example.txt" cannot be uploaded. Only files with these extensions are allowed: jpg, gif, png.

So, might be some mistake with your code...

#10199 report it
outrage at 2012/10/10 12:29pm
Bug?

There may be an issue following this wiki with recent versions of Yii.

When I add the following line above $model-save(), the validation doesn't work as intended. In fact, it allows upload of any file type.

$model->image=CUploadedFile::getInstance($model,'image');
#8976 report it
François Gannaz at 2012/07/11 04:42am
Do not ask for help here

@Bman900 : Just above the comment form of this page, there's an alert box that warns: "Please only use comments to help explain the above article. If you have any questions, please ask in the forum, instead."

You should follow these rules. It will benefit to you (you can explain your problem in details and more people will read your post there) and to this wiki (a lighter page with useful comments). If you get insightful answers in the forum, then please update your comment or the wiki page.

#8969 report it
Bman900 at 2012/07/10 07:34pm
Video Upload

Video files do not work, it just reloads a cleared form, is there a fix to this?

#7734 report it
devric at 2012/04/13 04:26am
Show the image in _form.php for actionUpdate

Now i got the file uploaded,

how do i show what image i currently uploaded, into the _form.php file

Only for actionUpdate

#7593 report it
Asgaroth at 2012/04/01 08:15pm
XUpload extension

You can also use XUpload extension which supports multiple file uploads

#7180 report it
AlDente at 2012/03/01 10:01am
response to #6885 / vidhi

make sure that the line $model->image=CUploadedFile::getInstance($model,'image'); is before of if($model->save()) {...}

bye

#6885 report it
vidhi at 2012/02/11 02:55am
How to solve the problem occurring with image file upload.

After performing the above given example, my form is submitted but the data is not stored in database... the DB table totally blank.. not a single entry in the form is been stored.

Please help me to solve my issue.

#6012 report it
rrbot at 2011/12/05 06:34pm
"File cannot be blank"

If you are having problems with Updating a model with a file, where you want the file to be empty but get "File cannot be blank", adjust your rule accordingly:

array('file', 'file', 'types'=>'jpg', 'allowEmpty' => true),

If you only want this to be empty on update, you could use a scenerio.

#5496 report it
devric at 2011/10/16 09:32pm
How to save in views for noob

Hi i've followed the guide and add the submit button to the view, but it does not submit, how do i submit this?

<? echo CHtml::form('', 'post', array('enctype'=>'multipart/form-data')); ?>
<? echo CHtml::activeFileField($model, 'image'); ?>
<?php echo CHtml::submitButton('Submit'); ?>
<? echo CHtml::endForm(); ?>
#5354 report it
cuongnc at 2011/10/06 11:00am
validation ajax

I can't validation ajax with field image. It error "Image cannot be blank".

#3970 report it
Kabinenkoffer at 2011/05/24 07:07am
Controller / Model

A Question/Suggestion:

If you put this line: $model->image=CUploadedFile::getInstance($model,'image');

in your model under beforeSave() and the file handling in afterSave() you can leave the controller untouched. for me this is usefull beause if you add more file uploads or change something you can just change everything in the model.

#3217 report it
ghadad at 2011/03/25 05:38pm
and dont forgot adding multipart/form-data option to update form !
<?php $form=$this->beginWidget('CActiveForm', array(
    'id'=>'company-form',
    'enableAjaxValidation'=>true,
        'htmlOptions'=>array('enctype'=>'multipart/form-data'),
));
#3137 report it
Thim at 2011/03/20 10:20am
File upload and update / error actions

The example above is great, but if you are working with a CRUD interface, then there is a need for a "UPDATE" action. If you update fields but dont upload any files the previous files will be "reset" (made empty) in the model. To prevent this you need to extend the code above.

This approach is based on the explanation from tri, see forum topic >>
http://www.yiiframework.com/forum/index.php?/topic/5419-optional-upload-file-when-update-a-record/page__view__findpost__p__27940

My example:

MODEL:

public function rules()
    {
        // NOTE: you should only define rules for those attributes that
        // will receive user inputs.
        return array(
...
            array('image', 'unsafe'),
...
}

By making the image unsafe the input values for the image field wont be set by $model->attributes=$_POST['Events'];

This allows us to use CUploadedFile data object to set the image data.

CONTROLLER:

/**
     * Updates a particular model.
     * If update is successful, the browser will be redirected to the 'view' page.
     * @param integer $id the ID of the model to be updated
     */
    public function actionUpdate($id)
    {
        $model=$this->loadModel($id);
 
        // Uncomment the following line if AJAX validation is needed
        // $this->performAjaxValidation($model);
 
        if(isset($_POST['Events']))
        {
            $model->attributes=$_POST['Events'];
 
            $file_image = CUploadedFile::getInstance($model,'image');
 
            if ( (is_object($file_flyer) && get_class($file_flyer)==='CUploadedFile'))
                $model->flyer = $file_flyer;
 
            if($model->save())
            {
                if (is_object($file_flyer))
                    $model->flyer->saveAs(Yii::app()->basePath.'/../files/flyers/'.$model->flyer);
 
 
                //$this->redirect(array('update','id'=>$model->id));
 
            }
        }
 
        $this->render('update',array(
            'model'=>$model,
        ));
    }

The first "if" check if the upload object is being loaded and filled with the uploaded file. If this is the case then the model->image gets an update otherwise the previous uploaded file remains.

The second "if" in the save will actually save the file on the webserver.

Hope this helps for those who are struggling with the file upload

#2242 report it
beninblack at 2010/11/30 07:09pm
FYI

If like me you decide to use the included "/projectname/images" folder to store your applications' images, you can change this line from the tutorial:

$model->image->saveAs('path/to/localFile');

To be this:

$model->image->saveAs(Yii::app()->basePath . '/../images/' . $model->image);
// As others have noted: $model->image at the end of the path is required,    // and is the filename of your image

The Yii::app()->basePath includes "/protected" at the end, and as the folder "images" is a sibling to "protected" so the /../ is necessary.

I'll also add that an easy way to display an uploaded image, would then be to open your view file, and use CHtml::image to generate tags for your Images with the necessary attributes, as follows:

<?php echo CHtml::image(Yii::app()->baseUrl . '/images/' . $data->image, 
$data->description,
   array("class" => "clickme", "title" => $data->title, "width"=>"300", "height"=>"220")); ?>

$data->description, and $data->title are from my Image.php model, and were perfect for the tags alt text and title, respectively.

Leave a comment

Please to leave your comment.

Write new article