Reputation: 4531
This is the situation: I'm new on Yii2 and wanted to use some file uploader widget within ActiveForm.. so far I've found this excelent one: \kartik\widget\FileInput
With this widget I can manage file upload and then, when enter in edit mode, show the previous uploaded image with the oportunite to replace it.
The problem is that if I press the "Update" button of the form without modifying the image yii says that the image "can't be empty" because I've set the 'required' rule in my model.
Upvotes: 8
Views: 22776
Reputation: 1
From Krajee:
http://webtips.krajee.com/advanced-upload-using-yii2-fileinput-widget
Create, delete, update: really easy, look no further.
(1) I've set the 'required' rule in my model too.
(2) To work on Wampserver:
Yii::$app->params['uploadPath'] = Yii::$app->basePath . '/web/uploads/';
Yii::$app->params['uploadUrl'] = Yii::$app->urlManager->baseUrl . '/uploads/';
Upvotes: -5
Reputation: 176
I have encountered another solution by creating scenarios. In your case I would modify the rules like this:
public funtion rules() {
[['image'], 'file'],
[['image'], 'required', 'on'=> 'create']
}
So the fileupload field will be required only in create action. In update action I have this code:
public function actionUpdate($id)
{
$model = $this->findModel($id);
if ($model->load(Yii::$app->request->post())) {
$newCover = UploadedFile::getInstance($model, 'image');
if (!empty($newCover)) {
$newCoverName = Yii::$app->security->generateRandomString();
unlink($model->cover);
$model->cover = 'uploads/covers/' . $newCoverName . '.' . $newCover->extension;
$newCover->saveAs('uploads/covers/' . $newCoverName . '.' . $newCover->extension);
}
if ($model->validate() && $model->save()) {
return $this->redirect(['view', 'id' => $model->post_id]);
} else {
// error saving model
}
} else {
return $this->render('update', [
'model' => $model,
]);
}
}
In the update scenario the image filed is not required but the code checks if nothing was uploaded and doesn't change the previous value.
My form file:
<?= $form->field($model, 'image')->widget(FileInput::classname(), [
'options' => ['accept'=>'image/*'],
'pluginOptions'=>[
'allowedFileExtensions'=>['jpg', 'gif', 'png', 'bmp'],
'showUpload' => true,
'initialPreview' => [
$model->cover ? Html::img($model->cover) : null, // checks the models to display the preview
],
'overwriteInitial' => false,
],
]); ?>
I think is a little more easier than a virtual field. Hope it helps!
Upvotes: 4
Reputation: 6082
Try preloading the file input field with the contents of that field. This way, you will not lose data after submitting your form.
I looked through kartik file-input widget (nice find, btw) and I came across a way to do this
// Display an initial preview of files with caption
// (useful in UPDATE scenarios). Set overwrite `initialPreview`
// to `false` to append uploaded images to the initial preview.
echo FileInput::widget([
'name' => 'attachment_49[]',
'options' => [
'multiple' => true
],
'pluginOptions' => [
'initialPreview' => [
Html::img("/images/moon.jpg", ['class'=>'file-preview-image', 'alt'=>'The Moon', 'title'=>'The Moon']),
Html::img("/images/earth.jpg", ['class'=>'file-preview-image', 'alt'=>'The Earth', 'title'=>'The Earth']),
],
'initialCaption'=>"The Moon and the Earth",
'overwriteInitial'=>false
]
]);
You may also want to relax the required
rule in your model for that field, so it does not complain on validation. You may choose to prompt the user through subtler means.
Upvotes: 3
Reputation: 4531
After an awful afternoon and a more productive night, I've encountered a solution that worked for me..
The main problem was that file input don't send its value (name of the file stored in database) when updating. It only sends the image info if browsed and selected through file input..
So, my workaround was creating another "virtual" field for managing file upload, named "upload_image". To achieve this I simple added a public property with this name to my model class: public $upload_image;
I also add the folowing validation to rules method on Model class:
public function rules()
{
return [
[['upload_image'], 'file', 'extensions' => 'png, jpg', 'skipOnEmpty' => true],
[['image'], 'required'],
];
}
Here, 'upload_image' is my virtual column. I added 'file' validation with 'skipOnEmpty' = true, and 'image' is the field on my database, that must be required in my case.
Then, in my view I configured 'upload_image' widget like follows:
echo FileInput::widget([
'model' => $model,
'attribute' => 'upload_image',
'pluginOptions' => [
'initialPreview'=>[
Html::img("/uploads/" . $model->image)
],
'overwriteInitial'=>true
]
]);
In 'initialPreview' option I asign my image name, stored in '$model->image' property returned from database.
Finally, my controller looks like follow:
public function actionUpdate($id)
{
$model = $this->findModel($id);
$model->load(Yii::$app->request->post());
if(Yii::$app->request->isPost){
//Try to get file info
$upload_image = \yii\web\UploadedFile::getInstance($model, 'upload_image');
//If received, then I get the file name and asign it to $model->image in order to store it in db
if(!empty($upload_image)){
$image_name = $upload_image->name;
$model->image = $image_name;
}
//I proceed to validate model. Notice that will validate that 'image' is required and also 'image_upload' as file, but this last is optional
if ($model->validate() && $model->save()) {
//If all went OK, then I proceed to save the image in filesystem
if(!empty($upload_image)){
$upload_image->saveAs('uploads/' . $image_name);
}
return $this->redirect(['view', 'id' => $model->id]);
}
}
return $this->render('update', [
'model' => $model,
]);
}
Upvotes: 7