Cuploadedfile Help

Hi,

this is one of my very first posts. I’ve made great use of the forum to learn the basics, I now have a problem I can’t understand and can’t solve, so some help would be appreciated.

I have an action that accepts the upload of an image file, along with a few other model fields. It works fine if I upload a file, but if the (non-required) image field is not filled out, I get an error:


Trying to get property of non-object

This occurs where I try to use the uploaded file extension in forming the new filename:


 $extension = strtolower($uploadedFile->extensionName);

OK - so there’s nothing in $uploadedFile, so there’s no extensionName property.

It response, I’ve put in some tests, but the form then complains no file was uploaded.

The code is:




	public function actionCreate()

	{

		$model=new Labels;

                // Uncomment the following line if AJAX validation is needed

		// $this->performAjaxValidation($model);


		if(isset($_POST['Labels']))

                {

                    $model->attributes=$_POST['Labels'];

                    $uploadedFile = CUploadedFile::getInstance($model,'image');

			

                        $label=$model->name;

                        if(isset($uploadedFile)){

                        $extension = strtolower($uploadedFile->extensionName);

                        $randomNum = rand(0000, 9999);

                        $filename = "{$label}-{$randomNum}.{$extension}";

                        $fullpath=Yii::app()->basePath.'/../uploads/'.$filename;

                        $model->image=$filename;

                        }

                        

                        if($model->save())

                        {

                             if(isset($uploadedFile)){

                       $uploadedFile->saveAs($fullpath);

                             }

                        

                        $this->redirect(array('view','id'=>$model->id));

                        }

                }

                if(isset($_GET['id']))

		{

                $id=$_GET['id'];

		$this->render('minicreate',array(

			'model'=>$model,

                        'winid'=>$id));

                }

                ELSE

                {

                $this->render('create',array('model'=>$model,));

                 }

	}



Any pointers would be very welcome. I have found the file upload to be very delicate and not very informative when it goes wrong, but I expect that this is caused by my implementation.

Thanks for your time.

You need to check for the file before attempting to build the string.




                    $model->attributes=$_POST['Labels'];

                    $uploadedFile = CUploadedFile::getInstance($model,'image');

                    

                    if ($uploadedFile)

                    {

                        $label=$model->name;

                        $extension = strtolower($uploadedFile->extensionName);

                        $randomNum = rand(0000, 9999);

                        $filename = "{$label}-{$randomNum}.{$extension}";

                        $fullpath=Yii::app()->basePath.'/../uploads/'.$filename;

                        $model->image=$filename;

                    }

                        

                    if($model->save() && $uploadedFile)

                    {

                        $uploadedFile->saveAs($fullpath);

                    }   

                        

                    $this->redirect(array('view','id'=>$model->id));



OK I tried that, but now it redirects to the ‘view’ happily, without having saved the model, and the $model->id is blank so I get a 404 error as the id in the URL is empty.

The whole thing looks like this now:




	public function actionCreate()

	{

		$model=new Labels;

               

		if(isset($_POST['Labels']))

                {

                    $model->attributes=$_POST['Labels'];

                    $label=$model->name;

                    $uploadedFile = CUploadedFile::getInstance($model,'image');

		                    

                    if(isset($uploadedFile))

                    {

                        $extension = strtolower($uploadedFile->extensionName);

                        $randomNum = rand(0000, 9999);

                        $filename = "{$label}-{$randomNum}.{$extension}";

                        $fullpath=Yii::app()->basePath.'/../uploads/'.$filename;

                        $model->image=$filename;

                    }

                    

                    if($model->save() && $uploadedFile)

                    {

                        $uploadedFile->saveAs($fullpath);

                     }

                  $this->redirect(array('view','id'=>$model->id));

                }

                if(isset($_GET['id']))

		{

                $id=$_GET['id'];

		$this->render('minicreate',array(

			'model'=>$model,

                        'winid'=>$id));

                }

                ELSE

                {

                $this->render('create',array('model'=>$model,));

                 }

	

        }




I’m running out of ideas, having tried lots of diagnostic changes to try and get a different (any!) result.

This seems to be working, but I’m not sure of the consequences…




$model->save(false)



Can you post your model rules? It sounds like one of your rules is not being met.

You should ideally only redirect if the save is successful. Otherwise, you should render the original form again with the errors displayed.

Have a look here for information on how to handle uploads. That example is for when the file is required, which I’m assuming yours isn’t because of the way you’re handling it.

Edit:

In answer to your question, $model->save(false) is saving the model without performing any validation, which is generally a very bad idea.

Thanks for the feedback - yes I’ve been testing without validation, and its not really the solution.

My Labels model rules are:




public function rules()

	{

		// NOTE: you should only define rules for those attributes that

		// will receive user inputs.

		return array(

			array('winery_id, name', 'required'),

			array('rating_5, price_range_id, retail_category_id', 'numerical', 'integerOnly'=>true),

			array('winery_id', 'length', 'max'=>11),

			array('name', 'length', 'max'=>80),

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

			array('thumbnail', 'length', 'max'=>50),

			// The following rule is used by search().

			// Please remove those attributes that should not be searched.

			array('id, winery_id, sub_label, name, rating_5, image, thumbnail, price_range_id, retail_category_id', 'safe', 'on'=>'search'),

		);

	}



So as far as I can see there’s no need to have an image.

Change your model save code to this:




                    if($model->save())

                    {

                        if ($uploadedFile)

                            $uploadedFile->saveAs($fullpath);


                        $this->redirect(array('view','id'=>$model->id));

                    }



so that the redirection is performed only if the model is saved successfully.

Then in your view, put this at the top:




    <? if ($model->hasErrors()): ?>

        <?= CHtml::errorSummary($model); ?>

    <? endif; ?>



to show you the actual rules that are failing.

That is back to where I started - and it reports ‘Image cannot be empty’.

I suspect it is something to do with the fact it sends two post variables, both called ‘image’. One is the file, and one is the filename in the field.

I’m stumped.

OK, I think the solution has something to do with using:




$valid=$model->validate();

						

			if($valid)

			{ ...save(false)...}



but this doesn’t seem to work first off.

Try adding


'allowEmpty'=>true

to your image file validator.

OK, where would I do that?




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



OK ! that nailed it. Thanks a million. I also had to change the order of testing with the saving/redirecting. When there was no file it failed with the old setup of testing both the save and the file together.




             

                    if($model->save())

                    {

                        if ($uploadedFile)

                            {  

                            $uploadedFile->saveAs($fullpath);

                            }

                  

                        $this->redirect(array('view','id'=>$model->id));

                  }