Laravel, unique columns - laravel

I need validate if field already exist, throws error, but with this code no errors are triggered
Laravel 7
table: departamento
column: id int incremental
column: departamento varchar(150)
column: idPais int
store method
$this->validate($request, ['departamento' => Rule::unique('departamento','idPais')->where('departamento',$request->depto)->where('idPais',$request->pais)]);
Try with this too
$rules = [
'depto' => [Rule::unique('departamento')->where(function ($query) use ($request) {
return $query->where('departamento','=', $request->depto)->where('idPais','=', $request->pais);
}),]
];
$this->validate($request,$rules);
with this code, throws this error
$rules = [
'depto' => [Rule::unique('departamento')->where(function ($query) use ($request) {
return $query->where('departamento','=', $request->depto)->where('idPais','=', $request->pais);
}),]
];
$this->validate($request,$rules);
Thanks!!!
EDIT:
My bad, I need check two fields , departamento and idPais, thanks.

This is happening because of your field name. The unique rule will try to find a record with that column name set to the request value (in your case, depto, but your column name is departamento). When you are customising the query you are not overriding it, you are adding on top of this default behaviour. From the laravel docs:
By default, the unique rule will check the uniqueness of the column matching the name of the attribute being validated. However, you may pass a different column name as the second argument to the unique method:
With that in mind, you could either change the unique rule to set the column to departamento and not depto as per below, or change the request to send departamento attribute instead of depto:
$rules = [
'depto' => [Rule::unique('departamento', 'departamento')->where(function ($query) use ($request) {
return $query->where('idPais','=', $request->pais);
}),]
];

Related

Laravel 8 UNIQUE composite key validation

In the database, I have a unique index on two columns like
`external_id`, `some_id`
I believe validating user input should not be a problem for this unique key.
Somehow I still have not found the proper solution
This is what I am doing now, but it looks overkill for this, supposed to be a simple task
$validator->after(function ($validator) use ($request) {
$some_id = $request->input('some_id');
$external_id = $request->input('external_id');
if ($some_id && $external_id) {
$exists = MyModel::where([
['some_id', '=', $some_id],
['external_id', '=', $external_id]
])->exists();
if ($exists) {
$validator->errors()->add('external_id', 'Something is wrong with this field!');
}
}
});
Can this be simplified with some built-in validation like exclude_if/unless... etc ?
Ok, so after hours of searching and trying and failing I found something
'external_id' => "nullable|numeric|min:1|unique:menus,external_id,NULL,id,some_id,$some_id"
| where it searches | ????? | AND some_id={id}
In the rule above, only rows with a some_id of {id} would be included in the unique check.
source: https://stackoverflow.com/a/26717913/2641425
Also (modified original documentation text):
"... let's add a query condition that scopes the query to only search records that have a some_id column value of {id}"
'external_id' => Rule::unique('table')->where(function ($query) use ($model) {
return $query->where([
['some_id', '=', $model->some_id],
['id', '<>', $model->id]
]);
})

Laravel Rule::unique not working as expected

This is my migration:
Schema::create('coupons', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('name')->unique();
});
And this is my custom request:
use Illuminate\Validation\Rule;
public function rules()
{
return [
'name' => [
'required', 'string',
Rule::unique('coupons')
->where(function ($q) {
$q->where('name', 'OFFER-2020');
})
]
}
I am trying to block requests where coupon name field already exists in database table. Since OFFER-2020 row already exists in coupons table, it should block the request instead it passes validation.
Update
My input was OFFER 2020. And OFFER-2020 is value after transformation. I thought laravel was checking unique with OFFER-2020. Instead it was checking with both OFFER 2020 and OFFER-2020 by a and query. And that's why my unique was not working.
If you input is OFFER-2020. You will only need to set the correct column name which is the second parameter of the unique rules. This will check the coupons table, for a row with the same name as the input.
Rule::unique('coupons', 'name')
To make your input slugified, you can utilise the form request method prepareForValidation(), this can transform your data.
function prepareForValidation() {
$this->merge(['name' => Str::slug($this->get('name'))]);
}
Why did the previous version fail? As you can see here in the Unique class. If you do not provide a column it will default to id. So you previous query would end up looking something similar to this.
select * from coupons where id = 'OFFER-2020' and name = 'OFFER-2020'

Composite Unique Validation in Laravel

How I can use composite unique validation for two columns.
Like I have tow columns clinicName and datefield.
Example.
Hclinic, 07-2019
Hclinic, 08-2019
$this->validate($request, [
'clinicName'=> 'unique:hospital',
'dates'=> 'unique:hospital',
]);
My aim is to use Composite Unique Validation.
According to Laravel Docs you can do it by defining the rule this way:
Adding Additional Where Clauses:
You may also specify additional query constraints by customizing the
query using the where method. For example, let's add a constraint that
verifies the account_id is 1:
'email' => Rule::unique('users')->where(function ($query) {
return $query->where('account_id', 1);
})
So you can change your code to:
$this->validate($request, [
'clinicName' => Rule::unique('hospital')->where(function ($query) {
return $query->where('dates', Request::get('dates'));
]);
This will check uniqueness of your clinicName where dates is equal to the given dates of your request.

Laravel, Can't update a soft-deleted value

I have this values in my db:
id - name - created_at - updated_at - deleted_at
------------------------------------------------
1 - John - 2018-11-11 - 2018-11-11 - (NULL)
2 - John - 2018-11-11 - 2018-11-11 - 2018-11-11
If I search for "John" with my Datatable (Yajra) I only see the John with id=1 because I'm using softdeletes. My model is this:
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
class MyModel extends Model
{
use SoftDeletes;
protected $fillable = ['name'];
protected $dates = ['deleted_at'];
}
When I delete (destroy) a registry it puts a date at deleted_at which is correct. But when I want to edit (update) John the Validator is giving me the error that that value is already in use. My update method is this:
public function update(Request $request, $id)
{
$rules = array(
'name' => 'unique:my_table'
);
$validator = Validator::make($request->all(), $rules);
if ($validator->passes()) {
MyModel::find($id)->update($request->input());
return redirect()->route('myroute')->withFlashSuccess('Ok!');
} else {
return redirect()->back()->withInput()->withErrors($validator);
}
}
What I'm doing wrong?
This person has written a blog post that looks like it would solve your problem:
https://wisdmlabs.com/blog/laravel-soft-delete-unique-validations/
I'm not 100% sure you want to do this though as you may later wish to use the restore() method to bring the soft-deleted data back. At which point, you'll have a collision.
The problem has nothing to do with the SoftDeletes, it is a validation issue. The unique validation rule is very special, because in case of an update it needs to know which entry it may ignore when performing the validation. In the background, the rule is performing an SQL query like
IF EXISTS (
SELECT id
FROM my_table
WHERE name = 'some value'
)
SELECT 1
ELSE
SELECT 0
(it may not be the exact query, but similar).
As you can see, the query does not take into account if you perform an update or not. Because your entity already exists, it will return 1 and therefore fail the validation because it thinks that the value under validation is not unique.
But there is actually a way to make the validation work for updates. You simply have to add the id of the existing entity (which is being validated) as third parameter to the validation rule. So your rules should look something like this:
$rules = [
'name' => 'unique:my_table,name,'.$id
];
Please be aware that there is also a second parameter for the unique validation rule - the column of the database table you want to search in.
Edit:
In case the unique constraint does only relate to not deleted entries, meaning that a unique value may be reused if other occurences of the same value are flagged as deleted, then it may be necessary to add an additional where() clause to the unique validation rule. For this, the fourth parameter needs to be set to the identifier column name and then we can add the additional where clauses as pairs of two parameters.
$rules = [
'name' => 'unique:my_table,name,'.$id.',id,deleted_at,NULL'
];
This will add where('deleted_at', 'NULL') (or whereNull('deleted_at')) to the query.

Laravel 4: Unique Validation for Multiple Columns

I know this question has been asked earlier but i did not get relevant answer.
I want to know that how can i write a rule to check uniqueness of two columns. I have tried to write a rule like:
public $rules = array(
"event_id"=>"required",
"label"=>"required|unique:tblSection,label,event_id,$this->event_id",
"description"=>"required"
);
In my example i need to put validation so that one label could be unique for a single event id but may be used for other event id as well. For Example i want to achieve:
id event_id label description
1 1 demo testing
2 2 demo testing
In the rule defined above, somehow i need to pass current selected event_id so that it could check whether the label does not exist in the database table for selected event_id but i am getting syntax error like:
{"error":{"type":"Symfony\\Component\\Debug\\Exception\\FatalErrorException","message":"syntax error, unexpected '\"'","file":"\/var\/www\/tamvote\/app\/modules\/sections\/models\/Sections.php","line":39}}
Note: I don't want to use any package but simply checking if laravel 4 capable enough to allow to write such rules.
The answer from Mohamed Bouallegue is correct.
In your controller for the store method you do:
Model::$rules['label'] = 'required|unique:table_name,label,NULL,event_id,event_id,' .$data['event_id'];
where $data is your POST data.
And for the update method you do:
$model = Model::find($id);
Model::$rules['label'] = 'required|unique:table_name,label,NULL,event_id,event_id,'.$data['event_id'].',id,id'.$model->id;
where $data is your PUT/PATCH data, $model is the record you are editing and id is the table primary key.
I didn't try this before but I think if you get the event_Id before validating then you can do it like this:
'label' => 'unique:table_name,label,NULL,event_id,event_id,'.$eventId
//you should get the $eventId first
If you want to declare your validation rules statically you can do this as well. It's not the most efficient since it checks the database for each value.
protected $rules = [
'user_id' => 'unique_multiple:memberships,user_id,group_id',
'group_id' => 'unique_multiple:memberships,user_id,group_id',
]
/**
* Validates that two or more fields are unique
*/
Validator::extend('unique_multiple', function ($attribute, $value, $parameters, $validator)
{
//if this is for an update then don't validate
//todo: this might be an issue if we allow people to "update" one of the columns..but currently these are getting set on create only
if (isset($validator->getData()['id'])) return true;
// Get table name from first parameter
$table = array_shift($parameters);
// Build the query
$query = DB::table($table);
// Add the field conditions
foreach ($parameters as $i => $field){
$query->where($field, $validator->getData()[$field]);
}
// Validation result will be false if any rows match the combination
return ($query->count() == 0);
});
Like Sabrina Leggett mentioned, you need to create your own custom validator.
Validator::extend('uniqueEventLabel', function ($attribute, $value, $parameters, $validator) {
$count = DB::table('table_name')->where('event_id', $value)
->where('label', $parameters[0])
->count();
return $count === 0;
}, 'Your error message if validation fails.');
You can call your validator by adding the following line to your rules:
'event_id' => "uniqueEventLabel:".request("label")
If you need more fields, you could add a new where clause to the sql statement.
(Source: edcs from this answer)
As you I was looking for hours to do that but nothing worked, I test everything ... suddenly the randomness of the doc I came across this:
'email' => Rule::unique('users')->where(function ($query) {
return $query->where('account_id', 1);
})
https://laravel.com/docs/5.5/validation#rule-unique
and it works perfectly and moreover it is very flexible :)

Resources