Laravel request update unique field only if changed - php

I wan't to be able to validate a user email address based on certain circumstances.
For example
If a user is being created, the email address must be unique
If a user is being updated but the email address hasn't changed, ignore the unique email rule
If a user is being updated and their email address has changed, it has to be unique
I've had a little look around and I know that I can specify different rules based on the method like so
public function rules()
{
$user = User::find($this->users);
switch($this->method())
{
case 'POST':
{
return [
'user.email' => 'required|email|unique:users,email',
];
}
case 'PUT':
case 'PATCH':
{
return [
'user.email' => 'required|email,
];
}
default:break;
}
}
Is there any way to make it so that in the put/patch case, the rule checks if the email address has been changed, and if it has then it has to be unique?
If not is there a cleaner way of achieving this goal? Maybe without the switch case? Perhaps more in depth validation rules I haven't stumbled across yet?

If i understand you correctly, you want to add unique validation if email changed, if that is the case then you just need to add a condition in validation. If you check unique validation structure it look like this unique:table,column,except,idColumn, check documentation
So the validation will be look like this, when you will create new record $userId will be null but at time of patch it will have value. So this validation will work for both create and patch.
$userId = isset($user) ? $user->id : null;
$rules = [
'email' => 'required|email|unique:users,email,'. $userId.',id',
];
$this->validate($request, $rules);

I had a similar issue and found a tidy way of addressing it in the docs.
My issue was that if a user was already created, the email address claimed it wasn't unique, even if it was already registered to the user.
https://laravel.com/docs/8.x/validation#rule-unique
A few headings down is something like this:
use Illuminate\Validation\Rule;
$request->validate([
'email' => [
'required',
Rule::unique('users')->ignore($user->id),
],
]);
This does the following:
Validate the email address, make it required and unique, unless the user ID of that row matches this user.

There is a built-in feature for this. You can add the actual user id to the unique constraint, if present. This will ensure that the unique constraint will still work, but it will not fail when the value did not change:
$exists = $user->exists;
$rules = return [
'user.email' => 'required|email|unique:users,email' . ($exists ? ','.$user->id : ''),
];
Internally, this will execute a query like:
SELECT count(id) FROM users WHERE email = 'some-mail#example.com' AND id != 42
The latter part AND id != 42 will only be part of the query when you add the third parameter to the unique validation rule.

You are missing the break; statement after every case. And for Update (PATCH request) you have to pass id also in update request.
public function rules()
{
$user = User::find($this->users);
switch($this->method())
{
case 'PATCH':
{
return [
'user.email' => 'required|email|unique:users,email,'. $user->id.',id',
];
}
break; // change here
case 'PUT': break; // change here
case 'POST':
{
return [
'user.email' => 'required|email,
];
}
break; // change here
default:break;
}
}

$request->validate([
'naam' => 'required|max:190|unique:database_name.table,column_where_you_check_unique,'.$id.',$id_column_reference'
]);
'naam' - form input field name
database_name(optional) - if you have multiple database
table(required) - table name
column_where_you_check_unique(required) - where data is unique
$id(required) - check with id autoincrement
$id_column_reference - column name of $id.

My way:
'slug' => $site->slug !== $request->slug ? 'required|min:4|max:80|unique:sites' : 'required|min:4|max:80'

In your controller methods (store and update) you can use:
$validations = [
'email' => 'required|email|unique:users,email'
];
$this->validate($request, $validations);

You can try this
$exists = Section::where('code', $request->code)->exists();
$isUpdateQuery = (($request->method() == 'PUT') || ($request->method() == 'PATCH'));
$validatedData = $request->validate([
"code" => ['required','unique:sections,code' . (($isUpdateQuery && $exists) ? ',' . $request->code . ',code': '')],
"title" => 'required | json',
"subtitle" => 'required | json',
"btn_title" => 'required | json',
"name" => 'required',
"pageID" => 'required | exists:pages,pageID',
"image" => 'nullable',
"btn_link" => 'nullable',
]);

Related

Laravel validation rules - optional, but validated if present

I'm trying to create a user update validation through form, where I pass, for example 'password'=>NULL, or 'password'=>'newone';
I'm trying to make it validate ONLY if it's passed as not null, and nothing, not even 'sometimes' works :/
I'm trying to validate as :
Validator::make(
['test' => null],
['test' => 'sometimes|required|min:6']
)->validate();
But it fails to validate.
Perhaps you were looking for 'nullable'?
'test'=> 'nullable|min:6'
Though the question is a bit old, this is how you should do it. You dont need to struggle so hard, with so much code, on something this simple.
You need to have both nullable and sometimes on the validation rule, like:
$this->validate($request, [
'username' => 'required|unique:login',
'password' => 'sometimes|nullable|between:8,20'
]);
The above will validate only if the field has some value, and ignore if there is none, or if it passes null. This works well.
Do not pass 'required' on validator
Validate like below
$this->validate($request, [
'username' => 'required|unique:login',
'password' => 'between:8,20'
]);
The above validator will accept password only if they are present but should be between 8 and 20
This is what I did in my use case
case 'update':
$rules = [
'protocol_id' => 'required',
'name' => 'required|max:30|unique:tenant.trackers'.',name,' . $id,
'ip'=>'required',
'imei' => 'max:30|unique:tenant.trackers'.',imei,' . $id,
'simcard_no' => 'between:8,15|unique:tenant.trackers'.',simcard_no,' . $id,
'data_retention_period'=>'required|integer'
];
break;
Here the tracker may or may not have sim card number , if present it will be 8 to 15 characters wrong
Update
if you still want to pass hardcoded 'NULL' value then add the
following in validator
$str='NULL';
$rules = [
password => 'required|not_in:'.$str,
];
I think you are looking for filled.
https://laravel.com/docs/5.4/validation#rule-filled
The relevant validation rules are:
required
sometimes
nullable
All have their uses and they can be checked here:
https://laravel.com/docs/5.8/validation#rule-required
if you want validation to always apply
https://laravel.com/docs/5.8/validation#conditionally-adding-rules
if you want to apply validation rules sometimes
https://laravel.com/docs/5.8/validation#a-note-on-optional-fields
if you want your attribute to allow for null as value too

Laravel 5.1 - Update name for current entry, must be unique to current User ID

I've got this code below in my GeckoRequest file. It works for the most part, however if I try to update an entry and keep the name as it is currently it fails saying that the name needs to be unique... Which it is as no other entries have this name apart from the current entry loaded in the edit form.
What is the best thing for me to do to check that the name is unique for the current user but also allow me to update the same entry without changing the name?
public function rules()
{
switch($this->method())
{
case 'GET':
case 'DELETE':
{
return [];
}
case 'POST':
{
return [
'morph' => 'required',
'sex' => 'required',
'genetics' => 'required',
'name' => "required|unique:geckos,name,NULL,id,user_id," . \Auth::user()->id
];
}
case 'PUT':
case 'PATCH':
{
return [
'morph' => 'required',
'sex' => 'required',
'genetics' => 'required',
'name' => "required|unique:geckos,name,NULL,id,user_id," . \Auth::user()->id
];
}
default:break;
}
}
You should be able specify the id of the Gecko resource in the rule.
case 'PATCH':
{
//This is assuming that you're using route model binding
//and that it's called gecko and not geckos (or something else).
$id = $this->route('gecko')->id;
return [
'morph' => 'required',
'sex' => 'required',
'genetics' => 'required',
'name' => "required|unique:geckos,name,{$id},id,user_id," . \Auth::user()->id
];
}
This will tell laravel that it has to be unique unless the ids match.
NB When using the route method in a form validation class it will be looking for the additional params that have been passed through in the url (these are the parts of the url that are wrapped by {} in your routes file). Depending on how these have been set up and/or what version of Laravel you're using it is possible that Laravel will have resolved the param to a model.
If you are unsure how laravel is storing the param in the route, in the rules() method add dd($this->route());, submit the form and then the look at the response. The value will be in the parameters array.
If it is not being stored as a model and it's literally just the id: $id = $this->route('gecko').
Hope this helps!

Laravel 5 Unique form validation ignore id / slug

These are my rules in my class:
class AppointmentsController extends Controller
{
protected $rules = [
'appointment' => ['required', 'min:5'],
'slug' => ['required', 'unique:appointments'],
'description' => ['required'],
'date' => ['required', 'date_format:"Y-m-d H:i"'],
];
This is in the laravel official docs:
Sometimes, you may wish to ignore a given ID during the unique check.
For example, consider an "update profile" screen that includes the
user's name, e-mail address, and location. Of course, you will want to
verify that the e-mail address is unique. However, if the user only
changes the name field and not the e-mail field, you do not want a
validation error to be thrown because the user is already the owner of
the e-mail address. You only want to throw a validation error if the
user provides an e-mail address that is already used by a different
user. To tell the unique rule to ignore the user's ID, you may pass
the ID as the third parameter:
'email' => 'unique:users,email_address,'.$user->id.',user_id'
I tried using this in my rules:
'slug' => ['required', 'unique:appointments,id,:id'],
This indeed ignores the current row BUT it ignores it completely. What I want to accomplish is, I want it to ignore the current row only if the slug is unchanged. When it is changed to something that is already unique in another row, I want it to throw an error.
The Unique validator works like that
unique:table,column,except,idColumn
So in your case, you can do it like that:
Get the id you want to validate against, you can get it from the route or with any other way that works for you; something like that
$id = $this->route('id');
'slug' => ['required','unique:appointments,slug,'.$id],
For example we need to update contact info into Users table.
In my model User I created this static method:
static function getContactDataValidationRules( $idUserToExcept ) {
return [
'email' => 'required|email|max:255|unique:users,email,' . $idUserToExcept,
'pec' => 'required|email|max:255',
'phone' => 'required|regex:/^([0-9\s\-\+\(\)]*)$/|min:8|max:20',
'mobile' => 'required|regex:/^([0-9\s\-\+\(\)]*)$/|min:8|max:20',
'phone2' => 'required|regex:/^([0-9\s\-\+\(\)]*)$/|min:8|max:20',
'recovery_email' => 'required|email|max:255',
];
}
and in my UsersController, into the method that update User I've:
$id = $request->input('id');
$request->validate(User::getContactDataValidationRules( $id ));
:-)

Laravel validation: validate field only if another is present

I wanted to validate an 'account_id' only if 'needs_login' is present. I did this:
$rules = [
'account_id' => ['required_with:needs_login','custom_validation']
];
But it doesn't work, because if needs_login field is not present but account_id has some value, then it tries to do the 'custom_validation'.
I also tried to put the 'sometimes' parameter
$rules = [
'account_id' => ['required_with:needs_login', 'sometimes', 'custom_validation']
];
but it didn't work.
Any ideas?
P.S.: Remember that I wanted to validate the account_id only if needs_login is present, not to check if account_id is present if needs_login does.
Have you tried required_if?
$rules = [
'account_id' => ['required_if:needs_login,1']
];
Something like this works for Laravel 5 if you are going the 'sometimes' route. Perhaps you can adapt for L4? Looks like it's the same in the Docs.
$validation = Validator::make($formData, [
'some_form_item' => 'rule_1|rule_2'
]
$validation->sometimes('account_id', 'required', function($input){
return $input->needs_login == true;
});

laravel update a uique value validation

I have a model with a mobileNumber property. the mobile number is unique and the validation rules are:
public static $rulesForEdit = array(
'firstName' => 'required|min:5',
'lastName' => 'required|min:5',
'mobileNumber' => 'required|min:5|unique:admin,mobileNumber|numeric'
);
when I update the model, I do this:
$data = array('firstName' => Input::get('firstName'),
'lastName' => Input::get('lastName'),
'mobileNumber' => Input::get('mobileNumber')
);
$validation = Validator::make($data, Admin::$rulesForEdit);
if($validation->passes()){
$admin = Admin::find($id);
$admin->firstName = Input::get('firstName');
$admin->lastName = Input::get('lastName');
$admin->mobileNumber = Input::get('mobileNumber');
$admin->update();
return Redirect::to("restaurants/admins/".$admin->id);
}else{
return Redirect::back()->withInput()->withErrors($validation);
}
The problem that I keep getting a validation error message states that : The mobile number has already been taken, which is correct, but the mobile is belongs to the same model that I am updating, there is no other model that took this mobile number, just the one that I want to update. In other words, I am updating the firstname and the last name but not the mobile number,
To force the Validator to ignore unique rule for a given id you may pass the id of that recored which is being validated, for example:
'mobileNumber' => 'required|min:5|numeric|unique:admin,mobileNumber,50'
This, will not check uniqueness of the model if the id is 10, so when you are updating the model you need to pass the id of the current model to ignore the unique rule on this model:
'mobileNumber' => 'required|min:5|numeric|unique:admin,mobileNumber,' . $id
// Replace the Model with the name of your model within the controller
// update method before the validation takes place
Model::$rules['mobileNumber'] = 'required|min:5|numeric|unique:admin,mobileNumber,' . $id;

Categories