Custom validation using laravel 5.8 - php

Good day,
I am trying to update brand data using Laravel 5.8 and I made a custom validation function to validate the name of the brand but my problem is when I attempt an update, the validation fails and I get a message saying Opps name Is Exist Before
I need to update this validation function to link the brand id with the brand name to perform the update without showing the validation error.
Thanks in advance.
Here is my code:
public function update(Request $request, $id)
{
//prepare data for validation
request()->validate([
'name' => [
'required',
'min:2', // validate english name is exist before
function ($attribute, $value, $fail) {
$englishname=Brand::where(['name'=>$value,'deleted'=>1 ])->first();
if(false !=$englishname) {
$fail('Opps '.$attribute.' Is Exist Before.');
}
},
],
'keywords' => 'required|min:2',
'ar_name' => [
'required',
'min:2',// validate english name is exist before
function ($attribute, $value, $fail) {
$arname=Brand::where(['ar_name'=>$value,'deleted'=>1])->first();
if(false !=$arname) {
$fail('Opps '.$attribute.' Is Exist Before.');
}
},
],
'ar_keywords' => 'nullable',
'status' => 'required|integer',
],[],[
"name"=>"Brand Name",
'keywords' => 'Brand KeyWords',
'ar_name' => 'اسم الماركة',
'ar_keywords' => 'الكلمات الدليلية',
]);
// start pass data to model
$branddata=array(
'name' =>$request->name,
'keywords' =>$request->keywords,
'ar_name' =>$request->ar_name,
'ar_keywords' =>$request->ar_keywords,
'last_updated_by'=>auth()->user()->id,
'status' =>$request->status,
);
//start update data
$updateddata=Brand::where(['id'=>$id,'deleted'=>1])->update($branddata);
if (false !==Brand::create($updateddata))
{
return redirect(route("brand.edit"))->with("messageSuccess","Brand Updated Successfully");
}else{
return redirect(route("brand.edit"))->with("messageSuccess","Brand Updated Successfully");
}
}

You can use the unique validation rule from Laravel with extended attributes passed for the validation checks.
If you want to validate that the name attribute must be unique (but allows for the same model instance), you can do something like follow:
'name' => "required|string|email|unique:<table name for the Brand::class>,name,{$id}"
This validation rule will check for uniqueness of the provided name for every rows except the one with $id as its primary key.
If your $id variable is not a primary key, you can specify the column name for the variable as follows:
'name' => "required|string|email|unique:<table name for the Brand::class>,name,{$id},<column name of the id>"

Related

Laravel exists custom validation rule unable to validate user id with phpunit

I have a validation rule taken from the Laravel Documentation which checks if the given ID belongs to the (Auth) user, however the test is failing as when I dump the session I can see the validation fails for the exists, I get the custom message I set.
I have dumped and died the factory in the test and the given factory does belong to the user so it should validate, but it isn't.
Controller Store Method
$ensureAuthOwnsAuthorId = Rule::exists('authors')->where(function ($query) {
return $query->where('user_id', Auth::id());
});
$request->validate([
'author_id' => ['required', $ensureAuthOwnsAuthorId],
],
[
'author_id.exists' => trans('The author you have selected does not belong to you.'),
]);
PHPUnit Test
/**
* #test
*/
function adding_a_valid_poem()
{
// $this->withoutExceptionHandling();
$user = User::factory()->create();
$response = $this->actingAs($user)->post(route('poems.store'), [
'title' => 'Title',
'author_id' => Author::factory()->create(['name' => 'Author', 'user_id' => $user->id])->id,
'poem' => 'Content',
'published_at' => null,
]);
tap(Poem::first(), function ($poem) use ($response, $user)
{
$response->assertStatus(302);
$response->assertRedirect(route('poems.show', $poem));
$this->assertTrue($poem->user->is($user));
$poem->publish();
$this->assertTrue($poem->isPublished());
$this->assertEquals('Title', $poem->title);
$this->assertEquals('Author', $poem->author->name);
$this->assertEquals('Content', $poem->poem);
});
}
Any assistance would be most appreciated, I'm scratching my head at this. My only guess is that the rule itself is wrong somehow. All values are added to the database so the models are fine.
Thank you so much!
In your Rule::exists(), you need to specify column otherwise laravel takes the field name as column name
Rule::exists('authors', 'id')
Since column was not specified, your code was basically doing
Rule::exists('authors', 'author_id')

Input manipulation before request validation

I created request form validation in Laravel but when I want to add new input fields, it's not working. In my form, there are no inputs named town or city. I only wanted to add one in the Controller.
$request->all() // returns perfect with new inputs.
$request->validated() // returns without new inputs. because of that validation fails.
ClientController.php
public function update(ClientRequest $request, Client $client)
{
$request->merge([
'city' => explode('/', $request->post('citytown'))[0],
'town' => explode('/', $request->post('citytown'))[1]
]);
$validated = $request->validated();
Client::whereId($client->id)->update($validated);
return redirect('/clients')->with('success', 'success');
}
ClientRequest.php
public function rules()
{
return [
'email' => "required|email|max:254|unique:clients,email,{$this->route()->client->id}",
'fullname' => 'required|max:128',
'idnumber' => "required|max:11|unique:clients,idnumber,{$this->route()->client->id}",
'gender' => 'required|digits_between:1,2',
'phone' => "required|max:10|unique:clients,phone,{$this->route()->client->id}",
'adress' => 'required',
'town' => 'required',
'city' => 'required',
];
}
You are looking for prepareForValidation() method in Illuminate\Foundation\Http\FormRequest.
$this->request->set(key, value);
Example:
protected function prepareForValidation()
{
$this->request->merge([
'city' => explode('/', $this->request->get('citytown'))[0],
'town' => explode('/', $this->request->get('citytown'))[1]
]);
}
From the question I am assuming that
Your form doesn't have fields named town AND city, but you have field named citytown which is supposed to be filed by the user in format CityName/TownName
You intend to add these fields manually only in the controller code by splitting citytown field.
If this is the case you need to remove validation rules for town and city from your request file, after this you wouldn't get validation errors for these fields.
Also, you need to add proper validation rules for citytown field in rules() method of ClientRequest class.
So this is not a case of input manipulation before validation, just add validation rules for citytown field and split it in controller method.

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;
}

How to perform validation when updating a unique field using Http\Request in Laravel

I have a customers table that I use a CustomerRequest to validate the fields, through the use of the rules function. The table has an email field which is required and is unique. This works fine when I create the customer, however when I update the customer info (let's say I got their last name spelled wrong) my request fails because the email address is already in the database.
Here is my CustomerRequest:
public function rules()
{
return [
'givenname' => 'required',
'surname' => 'required',
'email' => 'required|unique:customers,email',
];
}
I would like to reuse the CustomerRequest for all of the customer vaildation, how can I go about doing this?
You need to check here for request type as well as customer id for update and then return rules according to request. Something like this
public function rules(Request $customer_request)
{
return [
'givenname' => 'required',
'surname' => 'required',
'email' => 'required|unique:customers,email,'.$customer_request->get('customer_id'),
];
}

Unique key violation upon edit

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.

Categories