Unique key violation upon edit - php

I am trying to update a blog post but I am getting unique key error from database part then I went without using model and directly accessing ORM but then again no success.
This is my routes spesific to edit
Route::get('/getedit/{slug}', array('as' => 'getedit', 'uses' => 'AdminController#getEdit'))->before('auth');
Route::post('/postedit', array('as' => 'postedit', 'uses' => 'AdminController#postEdit'))->before('auth');
Controller
public function getEdit($slug)
{
$article = Post::where('slug', '=' , $slug)
->firstOrFail();
return View::make('admin.edit', array(
'title' => $article->title,
'mainarticle' => $article->article,
'slug' => $article->slug,
'category' => $article->category
));
}
// Updates articles to database
public function postEdit()
{
$rules = [
'title' => 'required',
'article' => 'required',
'slug' => 'required|unique:posts,slug,9',
'category' => 'required'
];
$input = Input::all();
$validator = Validator::make($input, $rules);
if ($validator->fails()) {
return Redirect::route('getedit')
->withErrors($validator);
// withInput not defined
}
else
{
$slug = $input['slug'];
/*$affectedRows = Post::where('slug', '=', $slug)->update([
'title' => $input['title'],
'article' => $input['article'],
'slug' => $input['slug'],
'category' => $input['category']
]);*/
/*$affectedRows = Post::where('slug', '=', $slug)->firstOrFail();
$affectedRows->title = $input['title'];
$affectedRows->article = $input['article'];
$affectedRows->slug = $input['slug'];
$affectedRows->category = $input['category'];
$affectedRows->save();*/
$post = DB::table('posts')->where('slug', '=', $slug)->update([
'title' => $input['title'],
'article' => $input['article'],
'slug' => $input['slug'],
'category' => $input['category']
]);
if ($post) {
return Redirect::route('dashboard')
->with('flash_message','Article Successfully Inserted');
}
else
{
return Redirect::route('dashboard')
->with('flash_message','Error updating data');
}
}
}
My model is just creating object of database (I am accidentally following fat controller and thin model approach as I am just trying the framework).
I have tried using Post::find(1)->update($data); method but that is returning unique violation and my current approach is just executing else statement which is triggered upon update failure.
Note: I am new to Laravel and trying this for the first time.

When you update a post, you'd rather send a POST (or better PATCH/PUT- http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html) request to given resource.
That said, you would include edited row key in the url, and change your method to something like this:
// route
Route::post('/postedit/{id}', array('as' => 'postedit', 'uses' => 'AdminController#postEdit'))
->before('auth');
// controller
public function postEdit($id)
{
// if no posts with $id found, throws exception - catch it and eg. show 404
$post = Post::findOrFail($id);
$rules = [
'title' => 'required',
'article' => 'required',
'slug' => 'required|unique:posts,slug,'.$id, // to ignore this row in unique check
'category' => 'required'
];
// validate
$post->fill($input)->save(); // fill() in order to use mass-assignement check
// alternatively you can just update:
// $post->update($input);
// but then make sure $input has only elements corresponding to the table columns
Additionally, read about route grouping, so you don't need to add before('auth') to those routes separately.

You should check your database table indexes. You should make sure that only slug has unique index.
I see that you are checking unique for slug but you hardcoded 9 in the rule:
'slug' => 'required|unique:posts,slug,9',
It should be:
'slug' => 'required|unique:posts,slug,'.$id,
where $id id of post you try to edit.
You should include such id in your form as hidden element and not search records with slug that you have because it seems you can edit your slug and you may edit the wrong record or edit nothing.

Related

Yii2 GridView Filters behaving not working properly

Yii2 Gridiview filter not working properly. Selecting one filter have impact on other filters. Changing one filter (dropdown) auto-select the values of other filters (dropdowns). This problem also exists in URL as well, changing one filter appends the other filters in URL as well and result shown as combined. but in reality only one filter should be applied which is being changed.
// Search Model, adding dummy table names
public function search($params)
{
$query = Model::find()->with('model_b');
if (empty($params['sort'])) {
$query->orderBy("group, " . Model::getSortByType() . ', "title"');
}
$dataProvider = new ActiveDataProvider([
'query' => $query,
'sort' => [
'attributes' => [
'code',
'title',
'updated_at'
]
]
]);
$this->load($params);
if (!$this->validate()) {
// uncomment the following line if you do not want to return any records when validation fails
// $query->where('0=1');
return $dataProvider;
}
// grid filtering conditions
$query->andFilterWhere([
'type' => $this->type,
'price_type' => $this->price_type,
'status' => $this->status,
'terms_related' => $this->terms_related,
'required' => $this->required,
'group' => $this->group,
]);
$query->andFilterWhere(['ilike', 'title', $this->title]);
$query->andFilterWhere(['is_qr' => $this->is_qr]);
return $dataProvider;
}
//Controller
public function actionIndex()
{
$searchModel = new ModelSearch();
$dataProvider = $searchModel->search(Yii::$app->request->queryParams);
return $this->render("index", [
"searchModel" => $searchModel,
"dataProvider" => $dataProvider,
]);
}
// In View, the filter I change
[
'attribute' => 'is_qr',
'format' => 'boolean',
'filter' => [1 => 'TRUE', 0 => 'FALSE'],
'content' => function ($service) { return ((int) $service->is_qr === 1) ? 'TRUE' : 'FALSE'; }
],
// the filter being changed with above filter
[
'attribute' => 'terms_related',
'filter' => array(0 => 'FALSE', 1 => 'TRUE'),
'content' => function ($service) { return ((int) $service->terms_related === 1) ? 'TRUE' : 'FALSE'; }
]
Observations:
Consider I have 5 filters in a GridView.
Action 1: I changed a filter, only that filter is applied first time but after page reload, other filters are being populated with values with "0". Because on selecting one filter, all filters are being pushed in URL with empty values other than selected one. And filters with empty values are being applied to rest of the filters with "0" value
Problem
The problem is, once I select a filter, gridview sends all possible filters in URL. The filters I did not select, have empty values.
Yii::$app->request->queryParams
This has all filters and the filters other than I selected have empty values, and
$this->load($params);
in search() deals empty values as 0. So, filters that I have not touched are being populated with "0" value.
I have found the solution, it is a custom solution but works for me.
I created a Trait
trait ParamsTrimable
{
public function trimParams($params, $modelClass)
{
$modelClass = basename(str_replace('\\', '/', $modelClass));
if ($params[$modelClass]) {
$params[$modelClass] = array_filter($params[$modelClass], function ($value) {
return ($value !== '');
});
}
return $params;
}
}
And before
$this->load($params);
I called trait's function i.e.
$params = $this->trimParams($params, static::class);
$this->load($params);
Reason behind the trait solution is, this problem may occur in other listings as well. To fix, we only need to use trait and call the function to remove empty values from params.

Update only the value of one field in controller in Laravel 6

My project requires that the admin has to approve the item uploaded by the user in order for this item to be seen in the website as in sort of validation or some kind of keeping everything under control.
so in the Item table, I have a field called (status) and has a default value = (denied).
$table->enum('status',['available','denied'])->default('denied');
The admin sees all items and I want a button next to each item called approve if the admin clicks it the status change from denied to approved, how can I create a function in the controller that changes only the status field? one besides the default edit and update function that is already working in my controller.
public function edit($itemid)
{
$where = array('itemid' => $itemid);
$data['item_info'] = Item::where($where)->first();
return view('items.edititem', $data);
}
public function update(Request $request, $itemid)
{
$request->validate([
'name' => 'required',
'description' => 'required',
'state' => 'required',
'price' => 'required',
'charityfee' => 'required',
'status' => 'required',
'category' => 'required',
]);
$update = [
'name' => $request->name, 'description' => $request->description,
'state' => $request->state, 'price' => $request->price,
'charityfee' => $request->charityfee, 'status' => $request->status,
'category' => $request->category, 'itemphoto' => $request->itemphoto
];
Item::where('itemid', $itemid)->update($update);
return Redirect::to('profile')
->with('success', 'Great! item updated successfully');
}
Although I tried this following code, an error appeared that the function I have called is not defined:(
public function editstatus($itemid)
{
Item::where('itemid', $itemid)->update(array('itemid' => 'available'));
}
function in controller
<td>approve</td>
the code in the view
Your problem is on editstatus function.
you want to update status => 'available' but you code update itemid => 'available'. that why it error.
you code should change to below:
public function editstatus($itemid)
{
Item::where('itemid', $itemid)->update(array('status' => 'available'));
}
I'm pretty sure your $where variable is wrong, You want to compare a variable with a field just get rid of the '=>'. You are basically doing an assigment operation and not a comparison.
You can have it this way
$data['item_info'] = Item::where($itemid, 'item_id')->first();

Laravel request validation image required in create but not required in update

ProductsRequest.php code:
public function rules()
{
return [
'name' => 'required
|min:'.trans('validation_standards.names.min').'
|max:'.trans('validation_standards.names.max').'
|unique:products,name,'.$this -> product_id,
'barcode' => 'size:'.trans('validation_standards.barcode.size').'
|unique:products,barcode,'.$this -> product_id,
'category_id' => 'required
|exists:categories,id',
'seasons_id' => 'required
|exists:seasons,id',
// REQUIRED IMAGE ONLY IN STORE
'image' => 'required
|image|mimes:'.trans('validation_standards.images.extensions').'
|max:'.trans('validation_standards.images.file_size'),
'description' => 'nullable
|min:'.trans('validation_standards.descriptions.min').'
|max:'.trans('validation_standards.descriptions.max'),
];
}
These rules apply for both store and update methods.
Problem is:
I want the image to be required only on store and not required in update, since user can just update the product basic info without choosing a new image for the product every time he update the product.
What I have tried:
I have tried to create two different ProductsRequest one for store and other for update but I know that this achievement is not the best achievement because my code must be DRY.
Use required_without rules
If primary key and element with name is id exist in your array
'image' => 'required_without:id`
If primary key and element with name is product_id exist in your array
'image' => 'required_without:product_id`
You can get more detail from laravel validation
You can do this in your ProductsRequest file;
public function rules()
{
if(request()->isMethod('put')) // could be patch as well
{
// Update rules here - Don't require image here
return [
'name' => 'required
|min:'.trans('validation_standards.names.min').'
|max:'.trans('validation_standards.names.max').'
|unique:products,name,'.$this->product_id,
'barcode' => 'size:'.trans('validation_standards.barcode.size').'
|unique:products,barcode,'.$this->product_id,
'category_id' => 'required|exists:categories,id',
'seasons_id' => 'required|exists:seasons,id',
// REQUIRED IMAGE ONLY IN STORE
'image' => 'required|image|mimes:'.
trans('validation_standards.images.extensions').'
|max:'.trans('validation_standards.images.file_size'),
'description' => 'nullable
|min:'.trans('validation_standards.descriptions.min').'
|max:'.trans('validation_standards.descriptions.max'),
];
}else{
// store rules here - require image here
return [
'name' => 'required
|min:'.trans('validation_standards.names.min').'
|max:'.trans('validation_standards.names.max').'
|unique:products,name,'.$this->product_id,
'barcode' => 'size:'.trans('validation_standards.barcode.size').'
|unique:products,barcode,'.$this->product_id,
'category_id' => 'required|exists:categories,id',
'seasons_id' => 'required|exists:seasons,id',
// REQUIRED IMAGE ONLY IN STORE
'image' => 'image|mimes:'.
trans('validation_standards.images.extensions').'
|max:'.trans('validation_standards.images.file_size'),
'description' => 'nullable
|min:'.trans('validation_standards.descriptions.min').'
|max:'.trans('validation_standards.descriptions.max'),
];
}
}
}
public function rules()
{
$image = request()->isMethod('put') ? 'nullable|mimes:jpeg,jpg,png,gif,svg|max:8000' : 'required|mimes:jpeg,jpg,png,gif,svg|max:8000';
return [
'image' => $image,
];
}
If your route is something like this one (which should be)
Route::post('products/{id}/update', 'ProductController#updateProduct')->name('products.update');
And you will call this route as
route('products.update', $product->id);
$product->id will be available in your form request and you can validate your form request for both create and update like this.
public function rules() {
return [
// All other rules
'image' => $this->id == null ? 'required|image|mimes:'.
trans('validation_standards.images.extensions').'
|max:'.trans('validation_standards.images.file_size') :
'image|mimes:'.trans('validation_standards.images.extensions').'
|max:'.trans('validation_standards.images.file_size')
];
}
Just this few lines can solve your problems...
You have to check there image have or not, like this.
Rules in a private or protected function
private function validateRequest($request)
{
//This is for Update without required image, this will check that In DB image have or not
$product = Product::find($request->product_id);
$rules = [];
if ($product) :
if ($product->product_image == null):
$rules['product_image'] = 'required|image|max:1999';
endif;
//This is for regular validation
else :
$rules = [
'category_id' => 'required|integer|not_in:-- Select Category --',
'product_image' => 'required|image|max:1999',
];
endif;
return $rules;
}

Saving foreign key user_id to posts table

I'm developing a laravel application with user and post model and i'm getting an error of:
Field 'user_id' doesn't have a default value
I have set up relationships for both models. Post belongs to user and a user can have as many posts. The user_id is not being saved to the post table in the database.
Post Controller:
class PostController extends Controller
{
public function postCreatePost(Request $request){
$this->validate($request, [
'body' => 'required'
]);
$post = new Post([
'body' => $request->input('body')
]);
$post->save();
return redirect()->route('dashboard');
}
Route:
Route::post('/createpost', [
'uses' => 'PostController#postCreatePost',
'as' => 'post.create'
]);
You need to specify user_id:
$post = new Post([
'body' => $request->input('body'),
'user_id' => auth()->user()->id
]);
Or you could use relationship:
$user = auth()->user();
$user->posts()->create(['body' => $request->input('body')]);
Also, don't forget to add user_id to the $fillable array in the Post model.
The reason is you are not giving a user_id when saving a post.
One solution is.
$post = new Post([
'body' => $request->input('body'),
'user_id' => $your_use_id
]);

Laravel custom validation rule

How can i make a custom validation rule to an input which value must be an integer and starting with 120?
I already read about making custom messages but didnt understand about rules.
I want to use a regex to validate the data. ^120\d{11}$ here is my regex.
I'm new in Laravel that's why cant now imagine how to do that.
A custom validation to use it in $this->validate($request, []);
Now i'm validating data like so:
$this->validate($request, [
'user_id' => 'integer|required',
'buy_date' => 'date',
'guarantee' => 'required|unique:client_devices|number',
'sn_imei' => 'required|unique:client_devices',
'type_id' => 'integer|required',
'brand_id' => 'integer|required',
'model' => 'required'
]);
The input that i want to add custom validation is guarantee
The quickest neatest way is an inline validator in your controller action:
public function store(Request $request)
{
$validator = Validator::make($request->all(), [
'number' => [
'regex' => '/^120\d{11}$/'
],
]);
if ($validator->fails()) {
return redirect('post/create')
->withErrors($validator)
->withInput();
}
return view('welcome');
}
Where number is the name of the field being submitted in the request.
If you have a lot of validation to do, you might want to consider using a Form Request instead, as a way of consolidating a lot of validation logic.
You can create custom validations in your controller as:
$name = Input::get('field-name')
$infoValidation = Validator::make(
array( // Input array
'name' => $name,
),
array( // rules array
'name' => array("regex:/^120\d{11}$"),
),
array( // Custom messages array
'name.regex' => 'Your message here',
)
); // End of validation
$error = array();
if ($infoValidation->fails())
{
$errors = $infoValidation->errors()->toArray();
if(count($errors) > 0)
{
if(isset($errors['name'])){
$response['errCode'] = 1;
$response['errMsg'] = $errors['name'][0];
}
}
return response()->json(['errMsg'=>$response['errMsg'],'errCode'=>$response['errCode']]);
}
Hope this helps.
Since Laravel 5.5, you can make the validation directly on the request object.
public function store(Request $request)
{
$request->validate([
'guarantee' => 'regex:/^120\d{11}$/'
]);
}

Categories