[Solved] Why CHtml::activeFileField does not get file

In a form based on CActiveRecord model, I am trying to upload a file, save it in a server folder and save the user specified filename in the database record. I used CHtml::activeFileField($model,‘image’) in the form to select the file to be uploaded. When I run the form, it lets me choose the file and displays its filename on the form. However, when the form is submitted, the validation fails with the message “image cannot be blank” and “No file Chosen” against the “Choose File” button. For some strange reason, it displays the error message, “image cannot be blank” twice! I shall appreciate if someone can tell me what’s going wrong and why I am unable to upload the file. Here are my files:

Model:




<?php


class Products extends CActiveRecord


{


        public $image;


	public static function model($className=__CLASS__)

	{

		return parent::model($className);

	}


	/**

	 * @return string the associated database table name

	 */

	public function tableName()

	{

		return 'tbl_products';

	}


	/**

	 * @return array validation rules for model attributes.

	 */

	public function rules()

	{

		return array(

                        array('image', 'file', 'allowEmpty'=>false),

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


                        array('productcategory_id, product_name, price,imagefilename','required'),

			array('productcategory_id', 'numerical', 'integerOnly'=>true),

			array('product_name', 'length', 'max'=>40),

			array('price', 'length', 'max'=>10),


			array('id, productcategory_id, product_name, price', 'safe', 'on'=>'search'),

		);

	}



Controller:


	/**

	 * Creates a new model.

	 * If creation is successful, the browser will be redirected to the 'view' page.

	 */

	public function actionCreate()

	{

		$model=new Products;


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


		if(isset($_POST['Products']))  {

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


                        if ($model->validate()) {

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


                            if ( ($uploadedImage->hasError)  ) {

                                $uploaderror =  " Upload error Code: " .$uploadedImage->error ;

                                $model->imagefilename =  $model->imagefilename .  $uploaderror ; 

                            } else {

                               $uploaderror =  " No Upload error " ;

                                // Save Image file in a server folder

                                $model->imagefilename =  $model->imagefilename .  $uploadedImage->getName();

                                //$uploadedImage->saveAs( Yii::app()->request->baseUrl . '\\images\\'. $model->imagefilename);

                             }

                            if($model->save()) {

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

                            }

                      }

		}


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

			'model'=>$model,

		));

	}




View File




<div class="form">


<?php $form=$this->beginWidget('CActiveForm', array(

	'id'=>'products-form',

	'enableAjaxValidation'=>false

)); ?>


	<p class="note">Fields with <span class="required">*</span> are required.</p>


	<?php echo $form->errorSummary($model); ?>




        <?php echo CHtml::form('','post',array('enctype'=>'multipart/form-data')); ?>


        .... 

       <BR>

            <?php echo CHtml::activeLabel($model,'Choose Product Image'); ?>

                <?php echo CHtml::activeFileField($model,'image'); ?>


        <BR>

        

        <div class="row">

                <?php echo $form->labelEx($model,'imagefilename'); ?>

		<?php echo $form->textField($model,'imagefilename',array('size'=>50,'maxlength'=>50)); ?>

		<?php echo $form->error($model,'imagefilename'); ?>

	</div>	


	<div class="row buttons">

		<?php echo CHtml::submitButton($model->isNewRecord ? 'Create' : 'Save'); ?>

	</div>


<?php $this->endWidget(); ?>


</div><!-- form -->



This has already been discussed, check here - http://www.yiiframework.com/forum/index.php?/topic/11343-solved-model-not-getting-validated/

Somehow, my reply got posted twice.

Yes, I know that this topic was discussed as I started that topic. However, when I try to implement ‘file upload’ scheme discussed (which is working) in a practical situation, it does not work. I am trying to create a product record and want to upload an image file and save the filename in the record. However, when I try to validate() the model, the validation always fails with a message – “Image cannot be blank”. Surprisingly, I see the message twice. (See attached file in the previous reply).

The action code in both scenarios is similar.

The working code:


 public function actionUpload() {

      $model=new UploadForm;

      if(isset($_POST['UploadForm']))   {

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

            if ($model->validate()) {

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

                // Save Image file in a server folder

               $savefilename ='images\\'. $model->imagefilename;

                $uploadedImage->saveAs($savefilename );


                Yii::app()->user->setFlash('FileUploadSuccess',

                        $savefilename. '  has been uploaded successfully. You may upload another file.');

		$this->refresh();

Non working code:


	public function actionCreate()

	{

		$model=new Products;

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

		if(isset($_POST['Products']))  {

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

    

                      if ($model->validate()) {

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

                            $savefilename ='images\\'. $model->imagefilename;

                            $uploadedImage->saveAs($savefilename );


                            if($model->save()) {

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

                            }

                      } 

		}


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

			'model'=>$model,

		));

	}

The basic difference between the two scenarios that the first one is based on CFormModel and the second one on CActiveRecord.

Any suggestion?

Thanks in advance.

Have you read all posts on the previous topic ?

Especially - http://www.yiiframework.com/forum/index.php?/topic/11343-solved-model-not-getting-validated/page__view__findpost__p__55700

$model->validate() and $model->save() both perform validation… so for saving you can use $model->save(false) becasue the validation has already done…

As for the message "image cannot be blank" read this post - http://www.yiiframework.com/forum/index.php?/topic/11343-solved-model-not-getting-validated/page__view__findpost__p__55775

I have read all topics you have mentioned. I do use array(‘image’, ‘file’, ‘allowEmpty’=>false), in my model. When I compare my working upload form with the upload functionality I tried to implement in the Products Create form, I see the following difference:

  1. The working upload form’s model is based on CFormModel whereas that in the nonworking one is on CActiveRecord.

  2. The non working form’s view file uses beginWidget(‘CActiveForm’, whereas in the working upload view file, it is not used.

I tried to isolate the problem. First, I removed the $model->save(), but retained the $model->validate(). However, I still see the validation error, "Image cannot be blank" twice. For your reference, here are my model, controller and view files:

Model:


<?php


/**

 * This is the model class for table "tbl_products".

 */

class Products extends CActiveRecord


{

        public $image;


        /**

	 * Returns the static model of the specified AR class.

	 * @return Products the static model class

	 */

	public static function model($className=__CLASS__)

	{

		return parent::model($className);

	}


	/**

	 * @return string the associated database table name

	 */

	public function tableName()

	{

		return 'tbl_products';

	}


	/**

	 * @return array validation rules for model attributes.

	 */

	public function rules()

	{

		return array(

                        array('image', 'file', 'allowEmpty'=>false),

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


                        array('productcategory_id, product_name, price,imagefilename','required'),

			array('productcategory_id', 'numerical', 'integerOnly'=>true),

			array('product_name', 'length', 'max'=>40),

			array('price', 'length', 'max'=>10),


			array('id, productcategory_id, product_name, price', 'safe', 'on'=>'search'),

		);

	}



Controller:


	/**

	 * Creates a new model.

	 */

	public function actionCreate()

	{

		$model=new Products;

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

		if(isset($_POST['Products']))  {

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

    

                      if ($model->validate()) {

                            /*

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

                            $savefilename ='images\\'. $model->imagefilename;

                            $uploadedImage->saveAs($savefilename );


                            if($model->save(false)) {

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

                            }

                            *

                            */

                      }

		}


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

			'model'=>$model,

		));

	}



View file


<div class="form">


<?php $form=$this->beginWidget('CActiveForm', array(

	'id'=>'products-form',

	'enableAjaxValidation'=>false

)); ?>


	<p class="note">Fields with <span class="required">*</span> are required.</p>


	<?php echo $form->errorSummary($model); ?>

        <?php echo CHtml::form('','post',array('enctype'=>'multipart/form-data')); ?>


	<div class="row">

		<?php echo $form->labelEx($model,'productcategory_id'); ?>

		<?php

                    // Get product categories (id, descrition) in an array for the

                    // dropdown list data source (function is Globals.php)

                    $productCategoriesArray= getDropDownListArray('tbl_productcategory','id','category')  ;

                    echo $form->dropDownList($model,'productcategory_id',$productCategoriesArray);

                ?>

                <?php echo $form->error($model,'productcategory_id'); ?>

	</div>


	<div class="row">

		<?php echo $form->labelEx($model,'product_name'); ?>

		<?php echo $form->textField($model,'product_name',array('size'=>40,'maxlength'=>40)); ?>

		<?php echo $form->error($model,'product_name'); ?>

	</div>


	<div class="row">

		<?php echo $form->labelEx($model,'price'); ?>

		<?php echo $form->textField($model,'price',array('size'=>10,'maxlength'=>10)); ?>

		<?php echo $form->error($model,'price'); ?>

	</div>


       <BR>

            <?php echo CHtml::activeLabel($model,'Choose Product Image'); ?>

                <?php echo CHtml::activeFileField($model,'image'); ?>

        <BR>

        

        <div class="row">

                <?php echo $form->labelEx($model,'imagefilename'); ?>

		<?php echo $form->textField($model,'imagefilename',array('size'=>50,'maxlength'=>50)); ?>

		<?php echo $form->error($model,'imagefilename'); ?>

	</div>	


	<div class="row buttons">

		<?php echo CHtml::submitButton($model->isNewRecord ? 'Create' : 'Save'); ?>

	</div>

<?php $this->endWidget(); ?>


</div>

So, with a single validate() function, I see the “Image cannot be blank” validation error twice. If I comment the validate() function too, I don’t get any validation error. It implies that somehow, a single $model-validate() is generating “Image cannot be blank” twice, which looks strange. (I have also set enableAjaxValidation to false.) Now, the issue is how do workaround the problem?

Thanks for your help.

If you want to allow empty image than you should use ‘allowEmpty’=>true!

And check your HTML source I think you have two <form> statement there… first created by beginWidget(CActiveForm), second created by CHtml::form()…

Mdomba, you were correct about two <form> statement. I deleted <?php echo CHtml::form(’’,‘post’,array(‘enctype’=>‘multipart/form-data’)); ?> However, that did not help. I continue to get “Image cannot be blank” validation error twice. How can I pass the validation check ?(I do need to select the image file and upload to the server.)

Have you changed

array[color="#666600"]([/color][color="#008800"]‘image’[/color][color="#666600"],[/color] [color="#008800"]‘file’[/color][color="#666600"],[/color] [color="#008800"]‘allowEmpty’[/color][color="#666600"]=>[/color][color="#000088"]false[/color][color="#666600"]),[/color]

to

array[color="#666600"]([/color][color="#008800"]‘image’[/color][color="#666600"],[/color] [color="#008800"]‘file’[/color][color="#666600"],[/color] [color="#008800"]‘allowEmpty’[/color][color="#666600"]=>[/color][color="#000088"]true[/color][color="#666600"]),[/color]

I made the change as you suggested. Here are my current rules.


	public function rules()

	{

		return array(

                        array('cat_id, subcat_id, product_name, price','required'),

                        array('product_code','required'),

                        array('product_code','unique'),


                        array('image', 'file', 'allowEmpty'=>true),

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

                    

			array('cat_id, subcat_id, subsubcat_id', 'numerical', 'integerOnly'=>true),

			array('product_name', 'length', 'max'=>40),

			array('price', 'length', 'max'=>10),

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

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

			array('id, cat_id, subcat_id, subsubcat_id, product_name, price', 'safe', 'on'=>'search'),

		);

	}

I tried the code on a different machine on localhost as well as remote host. Though I don’t get the “Image cannot be blank” message twice, I do get this message once. So the validation is still failing.

[color="#FF0000"]Update: [/color]

If I use array(‘image’, ‘file’, ‘allowEmpty’=>true), I get validation error “Image cannot be blank” and with array(‘image’, ‘file’, ‘allowEmpty’=>false), I get the validation error twice.

Also, from the documentation of activeFileField(), it appears that I must use ‘enctype’ =>‘multipart/form-data’. However, when I use this on my form (CActiveForm), I get an error message: Property “CActiveForm.enctype” is not defined. Here is my code:


<div class="form">

<?php

    $form=$this->beginWidget('CActiveForm', array(

	'id'=>'products-form',

	'enableAjaxValidation'=>false,

        'enctype'=>'multipart/form-data',

));

    ?>



After experimenting with my model rules, I have clear picture of what is happening. Here are the rules related to image:




  array('image', 'file', 'allowEmpty'=>false),

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



So, the framework tries to validate “image” twice, and hence I get the error message “Image cannot be blank” twice. If I change the ‘allowEmpty’ to true, the validation checking code skips the first rule, and I get the error only once. When I comment the first or the second rule, I get the error only once. And, if I comment both rules, I don’t get validation errors, but then the following code fails, as the controller does not find any upload image:


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

                            $savefilename ='images\\'. $model->imagefilename;

                            $uploadedImage->saveAs($savefilename );

I have got a feeling that there is some bug somewhere and the image is not getting uploaded at all with CActiveForm. Or, perhaps, I am not specifying some parameter in my code.

Waiting for a reply from experts…

Just put all in one line

array(‘image’, ‘file’, ‘allowEmpty’=>true, ‘types’=>‘jpg,jpeg,gif,png’, ‘maxSize’=>30720)

Edit:

for enctype use




<?php $form=$this->beginWidget('CActiveForm', array(

	'id'=>'products-form',

	'enableAjaxValidation'=>false,

	'htmlOptions'=>array('enctype'=>'multipart/form-data'),

)); ?>



Mdomba,

That works!

I thank you from the bottom of my heart.

Thank you, I’m glad we found a solution for this… :D

tax14, please post your solved controller,model and view. .