why did I get this bad request (#400) error after submitting my update on my 'news' section :
Missing required parameters: id
This is my update function
php
public function actionUpdate($id)
{
$model = $this->findModel($id);
$oldFile = $model->getImageFile();
$currentImage=$model->image;
if ($model->load(Yii::$app->request->post())) {
// process uploaded image file instance
$image = $model->uploadImage();
// revert back if no valid file instance uploaded
if ($image === false) {
$model->image = $currentImage;
}
if ($model->save()) {
// upload only if valid uploaded file instance found
if ($image !== false && unlink($oldFile)) { // delete old and overwrite
$path = $model->getImageFile();
$image->saveAs($path);
}
return $this->redirect(['view', ['id'=>$model->id,'image'=>$model->image],
]);
}
} else {
return $this->render('update', [
'model'=>$model,
]);
}
}
public function actionView($id)
{
return $this->render('view', [
'model' => $this->findModel($id),
]);
}
Please note that the updating and uploading image process were actually a success (that's why I didn't show my model's method code that my actionUpdate called). If I go directly to view the recently updated page and view the content, it clearly displays the updated contents along with the image without problem. The error only occurs just right after the update is submitted. I thought I had pass the parameter after the update by this line :
return $this->redirect(['view', ['id'=>$model->id,'image'=>$model->image],
Right before that line I even echo $model->id to test and it echoed out the id.
I've looked for similar problem in StackOverflow and find someone suggested this :
php
if($model->save())
{
$lastInsertID = $model->getPrimaryKey();
return $this->redirect(['view', 'id' => $lastInsertID]);
}
But it didn't work. Any idea?
You should pass URL as flat array:
return $this->redirect(['view', 'id' => $model->id,'image' => $model->image]);
Related
I am trying to check if a book is already in the db to avoid duplication. The code below is popping up for both existing and the one not in the database.
public function actionCreate($book_id = 'book_id')
{
$checkmodel = Books::find()->where(['book_id' => $book_id])->one();
if ($checkmodel) {
Yii::$app->session->setFlash('error', 'The book has been borrowed, Please look for another one.');
return $this->redirect(Yii::$app->request->referrer);
}
$model = new Books();
if ($model->load(Yii::$app->request->post()) && $checkmodel->save()) {
Yii::$app->session->setFlash('Success','You have successfully borrowed the book');
return $this->redirect(['view' => 'book_id', $model->book_id]);
}
return $this->render('create', [
'model' => $model,
]);
}
You could avoid the check adding a proper validation rule in you model
anyway for your code you shou,d try checking for not null $checkmodel
if (!is_null($checkmodel) {
Yii::$app->session->setFlash('error', 'The book has been borrowed, Please look for another one.');
return $this->redirect(Yii::$app->request->referrer);
}
https://www.yiiframework.com/doc/api/2.0/yii-validators-uniquevalidator
https://www.yiiframework.com/doc/guide/2.0/en/input-validation
public function actionCreate($book_id = 'book_id')
{
$model = new Books();
// check if post request
if ( $model->load(Yii::$app->request->post()) ) {
// check if book_id exists in table
if ( ! Books::find()->where(['book_id' => $book_id])->one() ) {
// save new record to model
if ( ! $model->save() ) {
// set error message and redirect to 'create' page
Yii::$app->session->setFlash('Error','There was some error in processing your request');
return $this->redirect(Yii::$app->request->referrer);
}
// if model is saved successfully, redirect to 'view' page
Yii::$app->session->setFlash('Success','You have successfully borrowed the book');
return $this->redirect(['view', 'book_id' => $model->book_id]);
} else {
// if book_id exist in table, show error message
Yii::$app->session->setFlash('error', 'The book has been borrowed, Please look for another one.');
return $this->redirect(Yii::$app->request->referrer);
}
}
return $this->render('create', [
'model' => $model,
]);
}
It's always better to check "false" condition in if statements than true. Helps us write cleaner code.
Also there was a syntax error in your redirect to view statement
Trying to implement update article in my update controller it seems works, but the problem is when I only want to update the post without uploading an image the old always getting remove which is it shouldn't.
here's my store function
public function store(Post $post)
{
$post->update($this->validateRequest());
$this->storeImage($post);
return redirect('post/'.$post->id)->with('success', 'New ariticle has been posted');
}
}
here's my validation
private function validateRequest()
{
return request()->validate([
'title'=> 'required',
'content' => 'required',
'image' => 'sometimes|image|max:5000',
]);
}
here's my update function
public function update(Post $post)
{
File::delete(public_path('storage/'.$post->image));
$post->update($this->validateRequest());
$this->storeImage($post);
return redirect('post/'.$post->id)->with('success', 'This post has
been Edited');
}
}
I've tried to add File::delete to my storeImage function and delete it from my update function, it fix the problem but the old image is not removed from directory
private function storeImage($post)
{
if (request()->has('image')){
File::delete(public_path('storage/'.$post->image))
$post->update([
'image' => request()->image->store('uploads', 'public'),
]);
$image = Image::make(public_path('storage/'.$post->image))->fit(750, 300);
$image->save();
}
}
Ok since I use model binding in my controller I don't have to find the id right?
so I change my update function which is basically Akhtar munir suggested, and turn out to be something like this. The image update work, it also remove the old image when I update it. But I have found another issue, the problem is when I edit article and title it didn't change like when I update it, I hope you can take look at this is this correct?
public function update(Post $post){
$this->validateRequest();
if(request()->hasFile('image') && request('image') != ''){
$imagePath = public_path('storage/'.$post->image);
if(File::exists($imagePath)){
unlink($imagePath);
}
$image = request()->file('image')->store('uploads', 'public');
$post->update([
'title' => request()->title,
'content' => request()->content,
'image' => $image,
]);
}
}
This is what I have done in one of my method. It may help you.
public function update(Request $request, $id)
{
if (UserDocument::where('id',$id)->exists()) {
$this->validateUserDocument($request);
if ($request->hasFile('doc_file') && $request->doc_file != '') {
$doc = UserDocument::where('id',$id)->first();
// dd($doc);
$file_path = storage_path().'/app/'.$doc['doc_file'];
//You can also check existance of the file in storage.
if(Storage::exists($file_path)) {
unlink($file_path); //delete from storage
// Storage::delete($file_path); //Or you can do it as well
}
$file = $request->file('doc_file')->store('documents'); //new file path
$doc->update([
'title' => $request->title,
'doc_file' => $file //new file path updated
]);
session()->flash('success','Document updated successfully!');
return redirect()->route('userdocs');
}
session()->flash('error','Empty file can not be updated!');
return redirect()->back();
}
session()->flash('error','Record not found!');
return redirect()->back();
}
In this code, I just simply want to clearify to you that I have stored image path in database, first I have retrieved that path and with that path I have found image in my local storage, delete it first and then update it with the new one. But make sure to store image path in database in both cases ofcourse with insert and update.
So finally you can also optimize your code like this, it will do the same thing as you expect, whether image and all data or only title and content.
public function update(Post $post){
$this->validateRequest();
$data = [
'title' => request()->title,
'content' => request()->content
];
if (request()->hasFile('image') && request('image') != '') {
$imagePath = public_path('storage/'.$post->image);
if(File::exists($imagePath)){
unlink($imagePath);
}
$image = request()->file('image')->store('uploads', 'public');
$data['image'] = $image;
//$post->update($data);
}
$post->update($data);
}
Try this one
private function storeImage($post)
{
if (request()->hasFile('image')){
$image_path = "/storage/".'prev_img_name'; // prev image path
if(File::exists($image_path)) {
File::delete($image_path);
}
$post->update([
'image' => request()->image->store('uploads', 'public'),
]);
$image = Image::make(public_path('storage/'.$post->image))->fit(750, 300);
$image->save();
}
}
I'm using Yii2 basic. It doesn't seems like anything's wrong, no error message displayed, but why did my image didn't upload? The rest (title, content etc) get uploaded through the form,though
This is my model's rule and related method:
public $image;
public function init(){
Yii::$app->params['uploadPath'] = Yii::$app->basePath . '/uploads/batam/';
Yii::$app->params['uploadUrl'] = Yii::$app->urlManager->baseUrl . '/uploads/batam/';
}
public function rules()
{
return [
[['title', 'content'], 'required'],
[['content'], 'string'],
[['created_at', 'updated_at','image'], 'safe'],
[['image'], 'file','extensions'=>'jpg,png,jpeg'],
[['title'], 'string', 'max' => 255],
];
}
public function getImageFile()
{
return isset($this->image) ? Yii::$app->params['uploadPath'].$this->image : null;
}
public function uploadImage() {
$image = UploadedFile::getInstance($this, 'image');
if (empty($image)) {
return false;
}
$this->image = $image->name;
return $image;
}
This is my controller
public function actionCreate()
{
$model = new News();
if ($model->load(Yii::$app->request->post()) )
{
// process uploaded image file instance
$image = $model->uploadImage();
if($model->validate())
{
if($model->save())
{
// upload only if valid uploaded file instance found
if ($image !== false)
{
$path = $model->getImageFile();
$image->saveAs($path);
}
return $this->redirect(['view', 'id'=>$model->id]);
}
}
else{echo " validation is failed";}
}
else{
return $this->render('create', [
'model' => $model,
]);
}
}
This is the form
echo $form->field($model, 'image')->widget(FileInput::classname(), [
'options' => ['accept' => 'image/*'],
'pluginOptions' => [['previewFileType' => 'any']]
]);
I had included the enctype also at the beginning of the form
<?php $form = ActiveForm::begin(['options' => ['enctype' => 'multipart/form-data']]);
At this point inside the if ($image !== false) part of the controller , the $image and $path to be saved-as contains a seemingly correct path.
This is my $path : C:\xampp\htdocs\gbia/uploads/batam/test image 1-01.jpg and my $image also contain the object (not null). This is the var_dump of my $image :
object(yii\web\UploadedFile)#179 (5) { ["name"]=> string(19) "test image 1-01.jpg" ["tempName"]=> string(24) "C:\xampp\tmp\php3199.tmp" ["type"]=> string(10) "image/jpeg" ["size"]=> int(925184) ["error"]=> int(0) }
I think something wrong with the saveAs(), but I can't figure it out. Had googled around, look on stackoverflow and tutorials but I still can't find any answer. Can someone help? Thanks
Check your model, you have declared $image as a public variable of the class, and not as a field in the database, if you want to store the data there, it will never work, as the public property that is temporary will have preference over the database column.
public $image;
So delete this field (If it is also in the db) or generate a new column name (I suggest by the name of path).
[['content', 'path'], 'string'],
Then you need to store the path, I don't see where are you doing that in the controller or class. I suggest you to add a field in the database with the "path" name and then do like this in the controller:
$path = $model->getImageFile();
$image->saveAs($path);
$model->path = $path . $image // You must store the full path plus the file name
$model->save(); // then you save the model again
Any doubt is welcome, I have example projects that I can show you if you are unable to see the light.
My Controller:
public function actionCreate()
{
$model = new SuratMasuk(['scenario' => 'create']);
if ($model->load(Yii::$app->request->post()))
{
try
{
$picture = UploadedFile::getInstance($model, 'imageFile');
$model->imageFile = $_POST['SuratMasuk']['id_suratmasuk'].'.'.$picture->extension;
if($model->save())
{
$picture->saveAs('uploads/' . $model->id_suratmasuk.'.'.$picture->extension);
Yii::$app->getSession()->setFlash('success','Data saved!');
return $this->redirect(['view','id'=>$model->id_suratmasuk]);
}
else
{
Yii::$app->getSession()->setFlash('error','Data not saved!');
return $this->render('create', [
'model' => $model,
]);
}
}
catch(Exception $e)
{
Yii::$app->getSession()->setFlash('error',"{$e->getMessage()}");
}
}
else
{
return $this->render('create', [
'model' => $model,
]);
}
}
getting this error message when i try to save my post. and it just uploads the text not images. i've tried $model->save(false); but not working
i'm newbie on yii, i'll appreciate your help
I guess this is because you try to pass id here:
return $this->redirect(['view', 'id' => $model->id_suratmasuk]);
and since actionView almost for sure requires id as parameter you get this error because $model->id_suratmasuk is empty.
You need to set proper rules() in SuratMasuk model.
Do not use POST variables directly, this is asking for being hacked.
Do not use save(false) if you need to save anything that comes from user (validation is a must!).
Add rules() for all attributes so there will be no surprises like with this id_suratmasuk being empty.
Check result of saveAs() on UploadedFile instance - this can go false as well.
While uploading multiple file getting this error:
When I put [['file'], 'file', 'maxFiles' => 4],in model getting following error:
Call to a member function saveAs() on null
But when I put this [['file'], 'file'], in model, its uploading.
Why am I getting error?
View:
<?php echo $form->field($model,'file[]')->label(false)->widget(FileInput::classname(),
[
'options'=>['accept'=>'image/*', 'multiple'=>true],
'pluginOptions'=>['allowedFileExtensions'=>['jpg','gif','png']
]]);
?>
Controller:
public function actionCreate()
{
$model = new RoomTypes();
if ($model->load(Yii::$app->request->post()))
{
$imageName = $model->room_type;
$model->file = UploadedFile::getInstance($model, 'file');
$model->file->saveAs( 'uploads/room_img/'.$imageName.'.'.$model->file->extension);
//save the path in the db column
$model->images = 'uploads/room_img/'.$imageName.'.'.$model->file->extension;
$model->save();
return $this->redirect(['view', 'id' => $model->id]);
}
else
{
return $this->render('create', [
'model' => $model,
]);
}
}
Use getInstances instead of getInstance as according to their respective documentations, the first returns all uploaded files for a given model attribute while the second is designed to return a single one.
Then loop and save them one by one :
if ($model->load(Yii::$app->request->post())) {
$imageName = $model->room_type;
$model->imageFiles = UploadedFile::getInstances($model, 'imageFiles');
$all_files_paths = [];
foreach ($model->imageFiles as $file_instance) {
// this should hold the new path to which your file will be saved
$path = 'uploads/room_img/' . $file_instance->baseName . '.' . $file_instance->extension;
// saveAs() method will simply copy the file
// from its temporary folder (C:\xampp\tmp\php29C.tmp)
// to the new one ($path) then will delete the Temp File
$file_instance->saveAs($path);
// here the file should already exist where specified within $path and
// deleted from C:\xampp\tmp\ just save $path content somewhere or in case you need $model to be
// saved first to have a valid Primary Key to maybe use it to assign
// related models then just hold the $path content in an array or equivalent :
$all_files_pathes []= $path;
}
$model->save();
/*
after $model is saved there should be a valid $model->id or $model->primaryKey
you can do here more stuffs like :
foreach($all_files_pathes as $path) {
$image = new Image();
$image->room_id = $model->id;
$image->path = $path;
$image->save();
}
*/
return $this->redirect(['view', 'id' => $model->id]);
}
See docs for more info.