Laravel 4: Unique Validation for Multiple Columns - validation

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 :)

Related

Laravel validation for option list: if value is not in DB then give error

I have an option list in my front end and the data is retrieved from the DB table, named countries and nicename column.
Trying to prevent the user request for value which is not in countries table and in nicename column. I have created custom validation, which works fine but it looks like it's not too professional way of doing this control. I have seen somewhere that there is shorter way of doing this validation by in:table_name,column_name. But it did not work for me Maybe I am doing something wrong?
$rules = [
'shop_country' => [
'required',
function ($attribute, $value, $fail ) {
$found_country = '';
$countries = Country::get()->toArray();
foreach ($countries as $country) {
if ($country['nicename'] === $value) {
$found_country= $country['nicename'];
}
}
if($found_country === ''){
$fail('The country is not valid!');
}
}],
]
You can define it as:
'shop_country' => ['required|exists:table_name,column']
See docs
You may use the exists validation rule:
The field under validation must exist in a given database table.
$rules = [
'shop_country' => ['required', 'exists:countries,nicename']
];
In the above example, the table name is assumed to be countries.
You should be able to achieve this by using the exists validation rule.
You can check it in the official laravel documentation.
Also this is an example they provide
'state' => 'exists:states'
You can validate nicename as string
'shop_country' => 'required|string|exists:countries,nicename',
Or validate country id itself
'country_id' => 'required|integer|exists:countries,id',

How to ignore field itself while creating rule for two different DB table

I have a form and getting input values. One of the input field is email. I need to check that email is unique in two different table but need to allow the value itself in both table. Tried almost all combinations, but couldn't sort it out.
$rules = [
'email' => "required|unique:admins|unique:vendors,email,$id",
];
The code above works but gives warning that Email has already been taken. But when I use one table only it works as expected. Maybe someone could help me with that, would be highly appreciated.
That seems a good use case for Custom Validation Rules using Closures. Basically, instead of using a bunch of unique rules, you'll simply have a function (closure) where you do your validation logic that decides whether to accept or decline the input.
$rules = [
/** added "email" rule to tell Laravel that we expect an email address */
'email' => ['required', 'email', function (string $attribute, string $value, Closure $fail) {
/** a function that checks whether an "email" exists in a "table" or not */
$existsInTableFN = fn(string $table): bool => DB::table($table)->where($attribute, $value)->exists();
/**
* the below condition means that the validation will FAIL
* if the "email" is found in one table but doesn't exist in the other
*/
if ($existsInTableFN('admins') !== $existsInTableFN('vendors'))
$fail('Email address already taken.'); /** laravel will take care of the rest if the above condition is met */
}]
];
Summary:
the above closure rule will only accept an email if:
it doesn't exist in both tables
OR does exist in both of them
In other words, the closure rule will fail:
if it finds the submitted email in one table
but it doesn't find it (the submitted email) in the other table

Laravel, unique columns

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);
}),]
];

Duplicate on 2 fields

I looking a way to avoid the duplicates in Laravel. For example, I have a table with 2 fields which are name and firstname.
How to manage the duplicates on the Controller? I am newbie...
Here is my function store().
public function store(Request $request)
{
$request->validate([
'name' => 'required|string|max:25',
'firstname' => 'required|string|max:25'
]);
$exists = Auteur::where('name', $request->get('name'))->where('firstName', $request->get('firstName'))->count();
if (!$exists){
Auteur::create($request->all());
return redirect()->route('auteurs.index')
->with('success', 'save');
}
}
I thank you for your help and your explanations.
If it were one field, you could use validation, or make that field unique and that would take care of it with a little error handling. However, with two fields to have to check against, it might be easiest to check if the element is already in the database and deduplicate from there. So in the store() method:
$exists = Auteur::where('name', $req->get('name'))->where('firstname', $req->get('firstname')->count();
if (!$exists){
// create the new model, now that we know there is no duplicate in the database
Auteur::create($request->all());
}
Just add unique in your validation rule. If you would like to check unique validation each field separately.
$request->validate([
'name' => 'required|unique:auteurs,name|string|max:25',
'firstname' => 'required|unique:auteurs,firstname|string|max:25'
]);
Here I hope the table name is `auteurs`

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.

Resources