I want to upload an image via Kartik widget. After submitting the form, the $_FILE['Product'] has the data about the image but getInstance($model, 'images') returns null. Tried with images[], also null.
This is what I'm trying to var_dump in the controller:
public function actionCreate() { $model = new Product(); if ($model->load(Yii::$app->request->post())) { var_dump(UploadedFile::getInstance($model, 'images[]'));die;And this is my model Product:
<?php
namespace app\models;
use backend\models\CActiveRecord;
use Yii;
use omgdef\multilingual\MultilingualQuery;
use omgdef\multilingual\MultilingualBehavior;
use yii\web\UploadedFile;
/** * This is the model class for table "product". * * @property int $id * @property int $category_id * @property int $quantity * @property double $price * @property int $sort * * @property Productlang[] $productlangs */
class Product extends CActiveRecord
{ public $images; public static function find() { return new MultilingualQuery(get_called_class()); } public function behaviors() { $allLanguages = []; foreach (Yii::$app->params['languages'] as $title => $language) { $allLanguages[$title] = $language; } return [ 'ml' => [ 'class' => MultilingualBehavior::className(), 'languages' => $allLanguages, //'languageField' => 'language', //'localizedPrefix' => '', //'requireTranslations' => false', //'dynamicLangClass' => true', //'langClassName' => PostLang::className(), // or namespace/for/a/class/PostLang 'defaultLanguage' => Yii::$app->params['languageDefault'], 'langForeignKey' => 'product_id', 'tableName' => "{{%productLang}}", 'attributes' => [ 'title', 'description', 'meta_title', 'meta_desc', 'url' ] ], ]; } /** * @inheritdoc */ public static function tableName() { return 'product'; } /** * @inheritdoc */ public function rules() { $string = $this->multilingualFields(['description', 'url']); $string_59 = $this->multilingualFields(['meta_title']); $string_255 = $this->multilingualFields(['meta_desc', 'title']); $string[] = 'description'; $string[] = 'url'; $string_59[] = 'meta_title'; $string_255[] = 'meta_desc'; $string_255[] = 'title'; return [ [['quantity', 'price', 'title', 'meta_title', 'meta_desc'], 'required'], [['category_id', 'quantity', 'sort'], 'integer'], [$string, 'string'], [$string_59, 'string', 'max' => 59], [$string_255, 'string', 'max' => 255], [['price'], 'number'], ['images', 'file'] ]; } /** * @inheritdoc */ public function attributeLabels() { return [ 'id' => 'ID', 'category_id' => 'Category ID', 'quantity' => 'Quantity', 'price' => 'Price', 'sort' => 'Sort', ]; } public function upload() { if ($this->validate()) { foreach ($this->image as $file) { $file->saveAs(\Yii::getAlias("@images") . "/products/" . $this->id . "_" . $this->image->baseName . '.' . $this->image->extension); } return true; } else { return false; } }
}Tried with rules ['images', 'safe'] also ['images', 'file'] ( think the second one is not right because the attribute is an array, right ? ). The form is <?php $form = ActiveForm::begin(['options' => ['multipart/form-data']]); ?>.
Finally my input:
<?= $form->field($model, 'images[]')->widget(FileInput::class, [ 'showMessage' => true, ]) ?>Full controller action:
public function actionCreate() { $model = new Product(); if ($model->load(Yii::$app->request->post())) { foreach (Yii::$app->params['languages'] as $language){ if(Yii::$app->params['languageDefault'] != $language){ $title_lang = "title_$language"; $model->$title_lang = Yii::$app->request->post('Product')["title_$language"]; $description_lang = "description_$language"; $model->$description_lang = Yii::$app->request->post('Product')["description_$language"]; $meta_title_lang = "meta_title_$language"; $model->$meta_title_lang = Yii::$app->request->post('Product')["meta_title_$language"]; $meta_desc_lang = "meta_desc_$language"; $model->$meta_desc_lang = Yii::$app->request->post('Product')["meta_desc_$language"]; } } if($model->save()){ $model = $this->findModel($model->id, true); //Make urls foreach (Yii::$app->params['languages'] as $language) { if (Yii::$app->params['languageDefault'] != $language) { $url_lang = "url_$language"; $title_lang = "title_$language"; $model->$url_lang = $model->constructURL( $model->$title_lang, $model->id ); }else{ $model->url = $model->constructURL( $model->title, $model->id ); } } //Upload Images $model->images = UploadedFile::getInstance($model, 'images'); if (!($model->upload())) { Yii::$app->session->setFlash('error', Yii::t('app', 'Some problem with the image uploading occure!')); return $this->redirect(['create']); } if($model->update() !== false){ return $this->redirect(['view', 'id' => $model->id]); }else{ Yii::$app->session->setFlash('error', Yii::t('app', 'Something went wrong. Please, try again later!')); return $this->redirect(['create']); } } } return $this->render('create', [ 'model' => $model, ]); } 9 1 Answer
What looks like you are trying to upload a single image you should remove the [] from the input field name from the ActiveForm field declaration, and from models rules.
Single File
<?= $form->field($model, 'images')->widget(FileInput::class, [ 'showMessage' => true, 'pluginOptions' => [ 'showCaption' => false , 'showRemove' => false , 'showUpload' => false , 'showPreview' => false , 'browseClass' => 'btn btn-success btn-block' , 'browseIcon' => '<i></i> ' , 'browseLabel' => 'Select Profile Image' ] , 'options' => ['accept' => 'image/*' ] , ]) ?>and from the following line
UploadedFile::getInstance($model, 'images');Multiple Files
For multiple files you need to add 'options' => ['multiple' => true] for the field and change the attribute name to images[]
<?= $form->field($model, 'images[]')->widget(FileInput::class, [ 'showMessage' => true, 'pluginOptions' => [ 'showCaption' => false , 'showRemove' => false , 'showUpload' => false , 'showPreview' => false , 'browseClass' => 'btn btn-success btn-block' , 'browseIcon' => '<i></i> ' , 'browseLabel' => 'Select Profile Image' ] , 'options' => ['accept' => 'image/*' ,'multiple'=>true] , ]) ?>and for receiving the uploaded files you should not specify the attribute as an array just change getInstance to getInstances and then try printing, it will show you all the images use foreach() to save all of them.
UploadedFile::getInstances($model, 'images');I personally prefer to use a separate model for file uploading rather than using the ActiveRecord model.
Note: When using multiple files upload, you can also specify the 'maxFiles'=>1000 inside you model rules to limit the number of files to be uploaded
EDIT
For troubleshooting your code you should comment out the actionCreate from the controller and replace with the one i added below
public function actionCreate() { $model = new Product(); if ( $model->load ( Yii::$app->request->post () ) ) { foreach ( Yii::$app->params['languages'] as $language ) { if ( Yii::$app->params['languageDefault'] != $language ) { $title_lang = "title_$language"; $model->$title_lang = Yii::$app->request->post ( 'Product' )["title_$language"]; $description_lang = "description_$language"; $model->$description_lang = Yii::$app->request->post ( 'Product' )["description_$language"]; $meta_title_lang = "meta_title_$language"; $model->$meta_title_lang = Yii::$app->request->post ( 'Product' )["meta_title_$language"]; $meta_desc_lang = "meta_desc_$language"; $model->$meta_desc_lang = Yii::$app->request->post ( 'Product' )["meta_desc_$language"]; } } $transaction = Yii::$app->db->beginTransaction (); try { if ( !$model->save () ) { throw new \Exception ( implode ( "<br />" , \yii\helpers\ArrayHelper::getColumn ( $model->errors , 0 , false ) ) ); } //Make urls foreach ( Yii::$app->params['languages'] as $language ) { if ( Yii::$app->params['languageDefault'] != $language ) { $url_lang = "url_$language"; $title_lang = "title_$language"; $model->$url_lang = $model->constructURL ( $model->$title_lang , $model->id ); } else { $model->url = $model->constructURL ( $model->title , $model->id ); } } //save the new urls if ( !$model->save () ) { throw new \Exception ( implode ( "<br />" , \yii\helpers\ArrayHelper::getColumn ( $model->errors , 0 , false ) ) ); } //Upload Images $model->images = UploadedFile::getInstances ( $model , 'images' ); $model->upload (); //commit the transatction to save the record in the table $transaction->commit (); Yii::$app->session->setFlash ( 'success' , 'The model saved successfully.' ); return $this->redirect ( [ 'view' , 'id' => $model->id ] ); } catch ( \Exception $ex ) { $transaction->rollBack (); Yii::$app->session->setFlash ( 'error' , Yii::t ( 'app' , $ex->getMessage () ) ); } } return $this->render ( 'create' , [ 'model' => $model , ] );
}And comment out the upload() function of your model and add below function
public function upload() { $skipped = []; foreach ( $this->images as $file ) { if ( !$file->saveAs ( \Yii::getAlias ( "@images" ) . "/products/" . $this->id . "_" . $this->image->baseName . '.' . $this->image->extension ) ) { $skipped[] = "File " . $file->baseName . " was not saved."; } } if ( !empty ( $skipped ) ) { Yii::$app->session->setFlash ( 'error' , implode ( "<br>" , $skipped ) ); }
}And for the ActiveForm make sure your input matches the following
$form->field($model, 'images[]')->widget(FileInput::class, [ 'showMessage' => true, 'pluginOptions' => [ 'showCaption' => false , 'showRemove' => false , 'showUpload' => false , 'showPreview' => false , 'browseClass' => 'btn btn-success btn-block' , 'browseIcon' => '<i></i> ' , 'browseLabel' => 'Select Profile Image' ] , 'options' => ['accept' => 'image/*','multiple'=>true ] ,
]) ; 7