How to pass a variable to custom validator - php

I've created a custom validator that resides in the AppServiceProvider. The boot method contains a DB method that should accept the first patameter passed into the validator as the table name. When I manually fill out the table name, it works but when the first parameter is passed, I run into this error:
QueryException in Connection.php line 729:
SQLSTATE[42S02]: Base table or view not found: 1146 Table 'server1.{users}'
doesn't exist (SQL: select count(*) as aggregate from `{users}` where `email` =
mail#mail.com and `STORE_ID` = 2)
Here's my service provider code:
public function boot()
{
Validator::extend('uniqueForStore', function ($attribute, $value, $parameters, $validator) {
$count = DB::table($parameters[0])->where($attribute, $value)->where('STORE_ID', config('constants.STORE_ID'))->count();
return $count === 0;
});
}
This is where the problem lies:
DB::table($parameters[0])
Here's my register user form request code:
public function rules()
{
return [
'first_name' => 'required',
'last_name' => 'required',
'email' => "uniqueForStore:{users}",
'password' => 'required|min:6'
];
}

Set your validation rules as below - just remove brackets for unique value (users):
public function rules()
{
return [
'first_name' => 'required',
'last_name' => 'required',
'email' => "uniqueForStore:users",
'password' => 'required|min:6'
];
}

Related

Laravel unique validation on multiple columns

I have 2 columns in table servers.
I have columns ip and hostname.
I have validation:
'data.ip' => ['required', 'unique:servers,ip,'.$this->id]
This working only for column ip. But how to do that it would work and for column hostname?
I want validate data.ip with columns ip and hostname.
Because can be duplicates in columns ip and hostname, when user write ip.
You can use Rule::unique to achieve your validation rule
$messages = [
'data.ip.unique' => 'Given ip and hostname are not unique',
];
Validator::make($data, [
'data.ip' => [
'required',
Rule::unique('servers')->where(function ($query) use($ip,$hostname) {
return $query->where('ip', $ip)
->where('hostname', $hostname);
}),
],
],
$messages
);
edit: Fixed message assignation
The following will work on the create
'data.ip' => ['required', 'unique:servers,ip,'.$this->id.',NULL,id,hostname,'.$request->input('hostname')]
and the following for the update
'data.ip' => ['required', 'unique:servers,ip,'.$this->id.','.$request->input('id').',id,hostname,'.$request->input('hostname')]
I'm presuming that id is your primary key in the table. Substitute it for your environment.
The (undocumented) format for the unique rule is:
table[,column[,ignore value[,ignore column[,where column,where value]...]]]
Multiple "where" conditions can be specified, but only equality can be checked. A closure (as in the accepted answer) is needed for any other comparisons.
Laravel 5.6 and above
Validation in the controller
The primary key (in my case) is a combination of two columns (name, guard_name)
I validate their uniqueness by using the Rule class both on create and on update method of my controller (PermissionsController)
PermissionsController.php
<?php
namespace App\Http\Controllers;
use App\Permission;
use Illuminate\Http\Request;
use Illuminate\Validation\Rule;
use App\Http\Controllers\Controller;
class PermissionsController extends Controller
{
/**
* Store a newly created resource in storage.
*/
public function store(Request $request)
{
request()->validate([
'name' => 'required|max:255',
'guard_name' => [
'required',
Rule::unique('permissions')->where(function ($query) use ($request) {
return $query
->whereName($request->name)
->whereGuardName($request->guard_name);
}),
],
],
[
'guard_name.unique' => __('messages.permission.error.unique', [
'name' => $request->name,
'guard_name' => $request->guard_name
]),
]);
Permission::create($request->all());
flash(__('messages.permission.flash.created'))->success();
return redirect()->route('permission.index');
}
/**
* Update the specified resource in storage.
*/
public function update(Request $request, Permission $permission)
{
request()->validate([
'name' => 'required|max:255',
'guard_name' => [
'required',
Rule::unique('permissions')->where(function ($query) use ($request, $permission) {
return $query
->whereName($request->name)
->whereGuardName($request->guard_name)
->whereNotIn('id', [$permission->id]);
}),
],
],
[
'guard_name.unique' => __('messages.permission.error.unique', [
'name' => $request->name,
'guard_name' => $request->guard_name
]),
]);
$permission->update($request->all());
flash(__('messages.permission.flash.updated'))->success();
return redirect()->route('permission.index');
}
}
Notice in the update method i added an additional query constraint [ whereNotIn('id', [$permission->id]) ] to ignore the current model.
resources/lang/en/messages.php
<?php
return [
'permission' => [
'error' => [
'unique' => 'The combination [":name", ":guard_name"] already exists',
],
'flash' => [
'updated' => '...',
'created' => '...',
],
]
]
The flash() method is from the laracasts/flash package.
Table
server
Field
id primary key
ip should be unique with hostname
hostname should be unique with ip
Here I validate for Ip and the hostname should be unique.
use Illuminate\Validation\Rule;
$ip = '192.168.0.1';
$host = 'localhost';
While Create
Validator::make($data, [
'ip' => [
'required',
Rule::unique('server')->where(function ($query) use($ip,$host) {
return $query->where('ip', $ip)->where('hostname', $host);
});
],
]);
While Update
Add ignore after RULE
Validator::make($data, [
'ip' => [
'required',
Rule::unique('server')->where(function ($query) use($ip,$host) {
return $query->where('ip', $ip)->where('hostname', $host);
})->ignore($serverid);
],
]);
This works for me for both create and update.
[
'column_1' => 'required|unique:TableName,column_1,' . $this->id . ',id,colum_2,' . $this->column_2
]
Note: tested in Laravel 6.
Try this rule:
'data.ip' => 'required|unique:servers,ip,'.$this>id.'|unique:servers,hostname,'.$this->id
With Form Requests:
In StoreServerRequest (for Create)
public function rules() {
'ip' => [
'required',
Rule::unique('server')->where(function ($query) {
$query->where('ip', $this->ip)
->where('hostname', $this->host);
})
],
}
public function messages() {
return [
'ip.unique' => 'Combination of IP & Hostname is not unique',
];
}
In UpdateServerRequest (for Update)
Just Add ignore at the end
public function rules() {
'ip' => [
'required',
Rule::unique('server')->where(function ($query) {
$query->where('ip', $this->ip)
->where('hostname', $this->host);
})->ignore($this->server->id)
],
}
This is the demo code. It would help you much better. I tried covering both insert and update scenarios.
Inside app/Http/Providers/AppServiceProvider.php
Validator::extend('uniqueOfMultiple', function ($attribute, $value, $parameters, $validator)
{
$whereData = [
[$attribute, $value]
];
foreach ($parameters as $key => $parameter) {
//At 0th index, we have table name
if(!$key) continue;
$arr = explode('-', $parameter);
if($arr[0] == 'except') {
$column = $arr[1];
$data = $arr[2];
$whereData[] = [$column, '<>', $data];
} else {
$column = $arr[0];
$data = $arr[1];
$whereData[] = [$column, $data];
}
}
$count = DB::table($parameters[0])->where($whereData)->count();
return $count === 0;
});
Inside app/Http/Requests/Something/StoreSometing.php
/**
* Get the validation rules that apply to the request.
*
* #return array
*/
public function rules()
{
return [
'name' => 'required|max:225|uniqueOfMultiple:menus,location_id-' . $this->get('location_id', 'NULL') . ',language_id-' . $this->get('language_id', 1),
'location_id' => 'required|exists:menu_location,id',
'order' => 'digits_between:0,10'
];
}
Inside app/Http/Requests/Something/UpdateSomething.php
/**
* Get the validation rules that apply to the request.
*
* #return array
*/
public function rules()
{
return [
'name' => 'required|max:225|uniqueOfMultiple:menus,location_id-' . $this->get('location_id', 'NULL') . ',language_id-' . $this->get('language_id', 'NULL') . ',except-id-' . $this->route('id', 'NULL'),
'location_id' => 'required|exists:menu_location,id',
'order' => 'digits_between:0,10'
];
}
Inside resources/lang/en/validation.php
'unique_of_multiple' => 'The :attribute has already been taken under it\'s parent.',
Here in this code, the custom validation used is uniqueOfMultiple. The first argument passed is the table_name i.e menus and all other arguments are column_name and are comma-separated. The columns are used here, name (primary column), location_id, language_id and one except-for column for the update case, except-id. The value passed for all three is - separated.
This works for me for both create and update.
in your ServerUpdateRequest or ServerCreateRequest class
public function rules()
{
return [
'column_1' => 'required|unique:TableName,column_1,' . $this->id . ',id,colum_2,' . $this->column_2 . ',colum_3,' . $this->column_3,
];
}
This command run background a aggregate Sql like this
select
count(*) as aggregate
from
`TableName`
where
`column_1` = <postedColumn1Value>
and `id` <> idValue
and `column_2` = <postedColumn2Value>
and `column_3` = <postedColumn3Value>
tested in Laravel 9. and it works
Note: if you want to see background sql for debugging (For example, to check if the request values are empty[$this->]) , especially you have to write wrong code, For example, you may enter a filed name incorrectly.
for me laravel 8 this works
$req->validate([
'house_no' => [
Rule::unique('house')
->where('house_no', $req->input('house_no'))
->where('ward_no', $req->input('ward_no'))
],
]);
The following code worked nicely for me at Laravel 8
Create:
'required|unique:TableName,column_1,' . $this->column_1 . ',id,colum_2,' . $this->column_2,
Example:
public function store(Request $request)
{
$union = auth()->user()->union_id;
$request->validate([
'holding_no' => 'required|integer|unique:holding_taxes,holding_no,' . $request->holding_no . ',id,union_id,' . $union,
]);
}
Update:
'required|unique:TableName,column_1,' . $this->id . ',id,colum_2,' . $this->column_2,
Example:
public function update(Request $request, $id)
{
$union = auth()->user()->union_id;
$request->validate([
'holding_no' => 'required|unique:holding_taxes,holding_no,' . $id . ',id,union_id,'.$union,
]);
}
Simple solution with call back query
Rule::unique('users')->where(fn ($query) => $query->where(['project_id'=> request()->project_id, 'code'=> request()->code ])),
public function store(Request $request)
{
$this->validate($request, [
'first_name' => 'required|regex:/^[\pL\s\-]+$/u|max:255|unique:contacts,first_name, NULL,id,first_name,'.$request->input('last_name','id'),
'last_name'=>'required|regex:/^[\pL\s\-]+$/u|max:255|unique:contacts,last_name',
'email' => 'required|email|max:255|unique:contacts,email',
'job_title'=>'required',
'city'=>'required',
'country'=>'required'],
[
'first_name.regex'=>'Use Alphabets Only',
'email.unique'=>'Email is Already Taken.Use Another Email',
'last_name.unique'=>'Contact Already Exist!. Try Again.',
]
);

Returning custom validation error in Laravel user validation/creation

In user creation proccess I need to query a external server for additional data and if that happens to fail i need to return an error at validation or creation. How is it best to do this? Here is the default Laravel code for registering user with some of my attemps:
protected function validator(array $data)
{
$query = queryExternalServerForAdditionalData();
if($query->success){
//add data from query to other user data?
//$data['data'] = $query->data;
return Validator::make($data, [
'name' => 'required|string|max:255',
'email' => 'required|string|email|max:255|unique:users',
'password' => 'required|string|min:6|confirmed',
]);
} else {
//somehow return an error, but how?
}
}
protected function create(array $data)
{
// or maybe do the query and error return here?
return User::create([
'name' => $data['name'],
'email' => $data['email'],
'password' => bcrypt($data['password']),
// add my additional data
// 'data' => $data['data']
]);
}
So as you can see my question is that how can i return an error from validator or create method? I tried to return redirect() with an error property added, but i got an error that something else was expected by the method calling validator/create and redirect was returned so that doesn't seem to be an option.
IMHO, the best way to do this is creating form requests.
public function rules()
{
return [
'name' => 'required|string|max:255',
'email' => 'required|string|email|max:255|unique:users|check_server_for_additional_data',
'password' => 'required|string|min:6|confirmed',
];
}
As an example, I added check_server_for_additional_data, a custom validation rule for email field (An error message will appear in the email field).
use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Facades\Validator;
class AppServiceProvider extends ServiceProvider
{
public function boot()
{
Validator::extend('check_server_for_additional_data', function ($attribute, $value, $parameters, $validator) {
return // queryExternalServerForAdditionalData()
});
}
}
Don't forget to defining the error message (resources/lang/LOCALE/validation.php)
"check_server_for_additional_data" => "Umm ohh.. Invalid!",

Laravel update password only if it is set

I am working on a laravel project with user login. The admin can create new users and edit existing users. I have got a password and a passwordConfirm field in the update-user-form. If the admin puts a new password in the form, it should check for validation and update the record in the db. If not, it shouldn't change the password (keep old one), but update the other user data (like the firstname).
If I try to send the form with an empty password and passwordConfirm field, it doesn't validate. I got a validation error, that the password must be a string and at least 6 characters long, but I don't know why. It seems like the first line of my update function will be ignored.
UserController.php
public function update(User $user, UserRequest $request) {
$data = $request->has('password') ? $request->all() : $request->except(['password', 'passwordConfirm']);
$user->update($data);
return redirect('/users');
}
UserRequest.php
public function rules() {
return [
'firstname' => 'required|string|max:255',
'lastname' => 'required|string|max:255',
'email' => 'required|string|email|max:255',
'password' => 'string|min:6',
'passwordConfirm' => 'required_with:password|same:password',
];
}
If you want to validate a field only when it is present then use sometimes validation rule in such cases.
Add sometimes validation to both password & passwordConfirm. Remove the $data line from update();
// UserController.php
public function update(User $user, UserRequest $request) {
$user->update($request->all());
return redirect('/users');
}
// UserRequest.php
public function rules() {
return [
'firstname' => 'required|string|max:255',
'lastname' => 'required|string|max:255',
'email' => 'required|string|email|max:255',
'password' => 'sometimes|required|string|min:6',
'passwordConfirm' => 'sometimes|required_with:password|same:password',
];
}
Reference - https://laravel.com/docs/5.4/validation#conditionally-adding-rules
I always do this in my projects:
//Your UserController file
public function update(User $user, UserRequest $request) {
$user->update($request->all());
return redirect('/users');
}
//Your UserRequest file
public function rules() {
$rules= [
'firstname' => 'required|string|max:255',
'lastname' => 'required|string|max:255',
'email' => 'required|string|email|max:255'
];
if($this->method()=="POST"){
$rules['password']='sometimes|required|string|min:6';
$rules['passwordConfirm']='sometimes|required_with:password|same:password';
}
return $rules;
}
So, as you can see if your method is POST it means that you want to add a new user so its going to ask for password and passwordConfirm but if your method is PATCH or PUT it means you don't need to validate password and passwordConfirm.
Hope it helps
Maybe you should try the following:
// ... more code
// Removes password field if it's null
if (!$request->password) {
unset($request['password']);
}
$request->validate([
// ... other fields,
'password' => 'sometimes|min:6'
// ... other fields,
]);
// ... more code
you should replace "has" with "filled" in your code
$data = $request->filled('password') ? $request->all() : $request->except(['password', 'passwordConfirm']);
and actually it's better if you use the expression like this
$request->request->remove('password_confirmation');
( ! $request->filled('password') ) ? $request->request->remove('password'):"";
( $request->has('password') ) ? $request->merge([ 'password' => Hash::make($request->post()['password']) ]):"";
//then you can use
$user->update($request->all());
Even better, however, you have to use separate request classes for create and update "php artisan make:request" for ex:
UserUpdateRequest.php and UserCreateRequest.php
for UserCreateRequest your rule is
'password' => 'required|confirmed|min:6',
for UserUpdateRequest your rule is
'password' => 'sometimes|nullable|confirmed|min:6',
and your controller head add this line
use App\Http\Requests\UserCreateRequest;
use App\Http\Requests\UserUpdateRequest;
and your update method must change
public function update(UserUpdateRequest $request, $id)
{
//
}
Standard way of doing this
UserRequest.php
first import Rule
use Illuminate\Validation\Rule;
in your rules array:
'password' => [Rule::requiredIf(fn () => $this->route()->method == "POST")]
Example:
public function rules()
{
return [
'name' => 'required',
'email' => ['required', 'email'],
'password' => [Rule::requiredIf(fn () => $this->route()->method == "POST"), 'confirmed'],
];
}
below php 7.4 use this way
'password' => [Rule::requiredIf(function(){
return $this->route()->method == "POST";
})]

Laravel custom validation on registration form

I'm currently struggling with a bit of validation on a registration form.
Basically when a user registers it will check if the unique code they have entered is valid and if not doesn't let them sign up.
But in my codes table which this reads from I also have an expiry date on the code.
I need to do another check after it is deemed valid that the expiry date hasn't passed, in other words it is not greater than now.
I think you can do this in the validator but I'm struggling a bit with the syntax and not sure where it should go. Here is my code:
protected function validator(array $data)
{
return Validator::make($data, [
'code' => 'required|exists:codes',
'name' => 'required|max:255',
'email' => 'required|email|max:255|unique:users',
'date_of_birth' => 'required|date',
'password' => 'required|min:6|confirmed',
'accept_terms' => 'required|accepted',
]);
}
/**
* Create a new user instance after a valid registration.
*
* #param array $data
* #return User
*/
protected function create(array $data)
{
Code::where('code', $data['code'])->increment('uses');
$data['code_id'] = Code::where('code', $data['code'])->value('id');
return User::create([
'name' => $data['name'],
'email' => $data['email'],
'date_of_birth' => $data['date_of_birth'],
'accept_terms' => $data['accept_terms'],
'code' => $data['code'],
'code_id' => $data['code_id'],
'password' => bcrypt($data['password']),
]);
}
Thanks in advance :)
As long as you're using Laravel v5.3.18 (or higher) you can use the Rule Class to save you having to define a custom rule.
This:
'code' => 'required|exists:codes',
Can be replaced with:
'code' => [
'required',
Rule::exists('codes')->where(function ($query) {
$query->where('expiry_date', '>=', Carbon::now());
}),
],
(the above is assuming expiry_date is the actual name of the column in your db table).
Documentation: https://laravel.com/docs/5.3/validation#rule-exists
Just make sure you import those facades.
Hope this helps!
You can create custom validation rule. Create service provider and register it. Then add something like this:
Validator::extend('is_code_valid', function($attribute, $value, $parameters, $validator) {
$code = Code::where('code', $value)->where('date', '>', Carbon::now())->first();
return !$code->isEmpty(); // Pass if valid code exists in DB.
});
And then use it:
'code' => 'required|is_code_valid',

Laravel: Validation unique on update

I know this question has been asked many times before but no one explains how to get the id when you're validating in the model.
'email' => 'unique:users,email_address,10'
My validation rule is in the model so how do I pass the ID of the record to the validation rule.
Here is my models/User
protected $rules_update = [
'email_address' => 'required|email|unique:users,email_address,'.$id,
'first_name' => "required",
'last_name' => "required",
'password' => "required|min:6|same:password_confirm",
'password_confirm' => "required:min:6|same:password",
'password_current' => "required:min:6"
];
models/BaseModel
protected $rules = array();
public $errors;
/*
* #data: array, Data to be validated
* #rules: string, rule name in model
*/
public function validate($data, $rules = "rules") {
$validation = Validator::make($data, $this->$rules);
if($validation->passes()) {
return true;
}
$this->errors = $validation->messages();
return false;
}
Just a side note, most answers to this question talk about email_address while in Laravel's inbuilt auth system, the email field name is just email. Here is an example how you can validate a unique field, i.e. an email on the update:
In a Form Request, you do like this:
public function rules()
{
return [
'email' => 'required|email|unique:users,email,'.$this->user->id,
];
}
Or if you are validating your data in a controller directly:
public function update(Request $request, User $user)
{
$request->validate([
'email' => 'required|email|unique:users,email,'.$user->id,
]);
}
Update:
If you are updating the signed in user and aren't injecting the User model into your route, you may encounter undefined property when accessing id on $this->user. In that case, use:
public function rules()
{
return [
'email' => 'required|email|unique:users,email,'.$this->user()->id,
];
}
A more elegant way since Laravel 5.7 is:
public function rules()
{
return [
'email' => ['required', 'email', \Illuminate\Validation\Rule::unique('users')->ignore($this->user()->id)]
];
}
P.S: I have added some other rules, i.e. required and email, in order to make this example clear for newbies.
One simple solution.
In your Model
protected $rules = [
'email_address' => 'sometimes|required|email|unique:users',
..
];
In your Controller, action:update
...
$rules = User::$rules;
$rules['email_address'] = $rules['email_address'] . ',id,' . $id;
$validationCertificate = Validator::make($input, $rules);
There is an elegant way to do this. If you are using Resource Controllers, your link to edit your record will look like this:
/users/{user}/edit OR /users/1/edit
And in your UserRequest, the rule should be like this :
public function rules()
{
return [
'name' => [
'required',
'unique:users,name,' . $this->user
],
];
}
Or if your link to edit your record look like this:
/users/edit/1
You can try this also:
public function rules()
{
return [
'name' => [
'required',
'unique:users,name,' . $this->id
],
];
}
From Laravel 5.7, this works great
use Illuminate\Validation\Rule;
Validator::make($data, [
'email' => [
'required',
Rule::unique('users')->ignore($user->id),
],
]);
Forcing A Unique Rule To Ignore A Given ID:
Test below code:
'email' => 'required|email|unique:users,email_address,'. $id .'ID'
Where ID is the primary id of the table
If i understand what you want:
'email' => 'required|email|unique:users,email_address,'. $id .''
In model update method, for exemple, should receive the $id with parameter.
Sorry my bad english.
Here is the solution:
For Update:
public function controllerName(Request $request, $id)
{
$this->validate($request, [
"form_field_name" => 'required|unique:db_table_name,db_table_column_name,'.$id
]);
// the rest code
}
That's it. Happy Coding :)
The Best Option is here try just once no need more code when unique validation on updating data
'email' => 'unique:users,email_address,' . $userId,
hereemailis field name and users is table name and email_address is table attribute name which you want unique and $userid is updating row id
public function rules()
{
switch($this->method())
{
case 'GET':
case 'DELETE':
{
return [];
}
case 'POST':
{
return [
'name' => 'required|unique:permissions|max:255',
'display_name' => 'required',
];
}
case 'PUT':
case 'PATCH':
{
return [
'name' => 'unique:permissions,name,'.$this->get('id').'|max:255',
'display_name' => 'required',
];
}
default:break;
}
}
an even simpler solution tested with version 5.2
in your model
// validator rules
public static $rules = array(
...
'email_address' => 'email|required|unique:users,id'
);
You can try this.
protected $rules_update = [
'email_address' => 'required|email|unique:users,email_address,'. $this->id,
'first_name' => "required",
'last_name' => "required",
'password' => "required|min:6|same:password_confirm",
'password_confirm' => "required:min:6|same:password",
'password_current' => "required:min:6"
];
In Laravel 8.x you can use Rule::unique method as well
Forcing A Unique Rule To Ignore A Given ID:
use Illuminate\Validation\Rule;
public function update(Request $request, Post $post)
{
$validatedData = $request->validate([
'name' => ['required', 'max:60', Rule::unique('posts')->ignore($post->id)],
]);
$post->update($validatedData);
return redirect(route('posts.index'))->with('status', 'post updated successfully');
}
Do One step in controller
Works Fine with Laravel 9
$request->validate([
'name'=>'required|unique:categories,name,'.$id,
]);
If you have a separate rules method. You can use easier the following syntax.
public function rules()
{
return [
'email' => "required|unique:users,email,{$this->id}"
];
}
$rules = [
"email" => "email|unique:users, email, '.$id.', user_id"
];
In Illuminate\Validation\Rules\Unique;
Unique validation will parse string validation to Rule object
Unique validation has pattern: unique:%s,%s,%s,%s,%s'
Corresponding with: table name, column, ignore, id column, format wheres
/**
* Convert the rule to a validation string.
*
* #return string
*/
public function __toString()
{
return rtrim(sprintf('unique:%s,%s,%s,%s,%s',
$this->table,
$this->column,
$this->ignore ?: 'NULL',
$this->idColumn,
$this->formatWheres()
), ',');
}
There is a simple and elegant way to do this. If you are passing the user_id in a body request or through a query parameter.
e.g
/update/profile?user_id=
Then in your request rules
public function rules(Request $request)
{
return [
'first_name' => 'required|string',
'last_name' => 'required|string',
'email' => ['required','email', 'string', Rule::unique('users')->ignore($request->user_id )],
'phone_number' => ['required', 'string', Rule::unique('users')->ignore($request->user_id )],
];
}
Better Still, you can pass in auth->id() in place of $request->user_id to get the login user id.
Found the easiest way, working fine while I am using Laravel 5.2
public function rules()
{
switch ($this->method()) {
case 'PUT':
$rules = [
'name' => 'required|min:3',
'gender' => 'required',
'email' => 'required|email|unique:users,id,:id',
'password' => 'required|min:5',
'password_confirmation' => 'required|min:5|same:password',
];
break;
default:
$rules = [
'name' => 'required|min:3',
'gender' => 'required',
'email' => 'required|email|unique:users',
'password' => 'required|min:5',
'password_confirmation' => 'required|min:5|same:password',
];
break;
}
return $rules;
}
i would solve that by doing something like this
public function rules()
{
return [
'name' =>
'required|min:2|max:255|unique:courses,name,'.\Request::get('id'),
];
}
Where you get the id from the request and pass it on the rule
You can also use model classpath, if you don't want to hard code the table name.
function rules(){
return [
'email' => ['required','string',
Rule::unique(User::class,'email')->ignore($this->id)]
];
}
Here $this->id is either 0 or the record Id to be updated.
Use for Laravel 6.0
use Illuminate\Validation\Rule;
public function update(Request $request, $id)
{
// Form validation
$request->validate([
'category_name' => [
'required',
'max:255',
Rule::unique('categories')->ignore($id),
]
]);
}
After researching a lot on this laravel validation topic including unique column, finally got the best approach. Please have a look
In your controller
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Validator;
class UserController extends Controller
{
public function saveUser(Request $request){
$validator = Validator::make($request->all(),User::rules($request->get('id')),User::$messages);
if($validator->fails()){
return redirect()->back()->withErrors($validator)->withInput();
}
}
}
saveUser method can be called for add/update user record.
In you model
class User extends Model
{
public static function rules($id = null)
{
return [
'email_address' => 'required|email|unique:users,email_address,'.$id,
'first_name' => "required",
'last_name' => "required",
'password' => "required|min:6|same:password_confirm",
'password_confirm' => "required:min:6|same:password",
'password_current' => "required:min:6"
];
}
public static $messages = [
'email_address.required' => 'Please enter email!',
'email_address.email' => 'Invalid email!',
'email_address.unique' => 'Email already exist!',
...
];
}
This is what I ended up doing. I'm sure there is a more efficient way of doing this but this is what i came up with.
Model/User.php
protected $rules = [
'email_address' => 'sometimes|required|email|unique:users,email_address, {{$id}}',
];
Model/BaseModel.php
public function validate($data, $id = null) {
$rules = $this->$rules_string;
//let's loop through and explode the validation rules
foreach($rules as $keys => $value) {
$validations = explode('|', $value);
foreach($validations as $key=>$value) {
// Seearch for {{$id}} and replace it with $id
$validations[$key] = str_replace('{{$id}}', $id, $value);
}
//Let's create the pipe seperator
$implode = implode("|", $validations);
$rules[$keys] = $implode;
}
....
}
I pass the $user_id to the validation in the controller
Controller/UserController.php
public function update($id) {
.....
$user = User::find($user_id);
if($user->validate($formRequest, $user_id)) {
//validation succcess
}
....
}
While updating any Existing Data Write validator as following:
'email' => ['required','email', Rule::unique('users')->ignore($user->id)]
This will skip/ignore existing user's id's unique value matching for the specific column.
Test below code:
$validator = Validator::make(
array(
'E-mail'=>$request['email'],
),
array(
'E-mail' => 'required|email|unique:users,email,'.$request['id'],
));
Since you will want to ignore the record you are updating when performing an update, you will want to use ignore as mentioned by some others. But I prefer to receive an instance of the User rather then just an ID. This method will also allow you to do the same for other models
Controller
public function update(UserRequest $request, User $user)
{
$user->update($request->all());
return back();
}
UserRequest
public function rules()
{
return [
'email' => [
'required',
\Illuminate\Validation\Rule::unique('users')->ignoreModel($this->route('user')),
],
];
}
update: use ignoreModel in stead of ignore
Very easy to do it ,
Write it at your controller
$this->validate($request,[
'email'=>['required',Rule::unique('yourTableName')->ignore($request->id)]
]);
Note : Rule::unique('yourTableName')->ignore($idParameter) , here $idParameter you can receive from get url also you can get it from hidden field.
Most important is don't forget to import Rule at the top.
If a login user want to update the email then auth() helper function will give us the login user id auth()->user()->id
Laravel helpers#method-auth
Validator::make($data, [
'email' => [
'required',
Rule::unique('users')->ignore(auth()->user()->id),
],
]);
if Admin want to change the specific user information from User list then validation will be like this :
Validator::make($data, [
'email' => [
'required',
Rule::unique('users')->ignore($request->user),
],
Laravel validation#rule-unique
$request object contain the current route related model objects. Which gives the model.
Try dd($request)
Most answers to this question refer to email_address, but in Laravel's inbuilt authentication system, the email field name is just email. Here is an example of validating a unique field, i.e. an email on the update:
Form Requests look like this:
public function rules()
{
return [
'email' => [ 'required','email', Rule::unique('users')->ignore($this->id ?? 0)]];
}
?? 0 If you use this then if hare id does not exist this request will not give you an error
Save
Whenever you access the id property of $this->user, you may encounter an undefined property if you haven't injected the User model into your route. If that is the case, use:
public function rules()
{
return [
'email' => 'required|email|unique:users,email,'.$this->user()->id ?? 0,
];
}
?? 0 If you use this then if hare id does not exist this request will not give you an error
My solution:
$rules = $user->isDirty('email') ? \User::$rules : array_except(\User::$rules, 'email');
Then in validation:
$validator = \Validator::make(\Input::all(), $rules, \User::$messages);
The logic is if the email address in the form is different, we need to validated it, if the email hasn't changed, we don't need to validate, so remove that rule from validation.
For unique rule in the controller - which obviously will be different for the store method and the update method, I usually make a function within the controller for rules which will return an array of rules.
protected function rules($request)
{
$commonRules = [
'first_name' => "required",
'last_name' => "required",
'password' => "required|min:6|same:password_confirm",
'password_confirm' => "required:min:6|same:password",
'password_current' => "required:min:6"
];
$uniqueRules = $request->id
//update
? ['email_address' => ['required', 'email', 'unique:users,email' . $request->get('id')]]
//store
: ['email_address' => ['required', 'email', 'unique:users,email']];
return array_merge($commonRules, $uinqueRules);
}
Then in the respective store and update methods
$validatedData = $request->validate($this->rules($request));
This saves from defining two different rule sets for store and update methods.
If you can afford to compromise a bit on readability, it can also be
protected function rules($request)
{
return [
'first_name' => "required",
'last_name' => "required",
'password' => "required|min:6|same:password_confirm",
'password_confirm' => "required:min:6|same:password",
'password_current' => "required:min:6",
'email_address' => ['required', 'email', 'unique:users,email' . $request->id ?: null]
];
}

Categories