Right way to handle this situation using Laravel validation - laravel

i am new in Laravel and I would like to have some directive on how to handle this situation.
I have two entities: Ad and Nomination. Ad can have many Nominations.
In a controller i receive two external inputs: [ad_id] and [nomination_id] both required.
What i have to do with these two inputs is:
Check if [ad_id] is an existing Ad entity and his attribute "active" is true.
Check [nomination_id] is an existing Nomination entity.
Only if [ad_id] was an existing Ad and [nomination_id] was an existing Nomination check if this Nomination belongs to this Ad.
Can you show me an example about how to manage this using only validation class?

You can write your validation rules like this
public function rules()
{
return [
'ad_id' => [
'bail',
'required',
Rule::exists('ads')->where(function ($query) use ($request) {
$query->where([
['active' => 1],
['id' => $request->ad_id]
]);
}),
],
'nomination_id' => [
'bail',
'required',
Rule::exists('nominations')->where(function ($query) use ($request) {
$query->where([
['ad_id' => $request->ad_id],
['id' => $request->nomination_id]
]);
}),
],
];
}
Assuming you have ads and nominations are tables name and primary key field is id and ad_id as foreign key in nominations table.

It's pretty straightforward - you can write validation rules just as you listed them in your question:
$validator = Validator::make($request->only('ad_id', 'nomination_id'), [
'ad_id' => 'required|exists:ads,id,active,1',
'nomination_id' => 'required|exists:nominations,id,ad_id,' . $request->ad_id,
]);
if ($validator->fails()) {
...
}

$inputAd = <some_value>;
$inputNomination = <some_value>;
$nomination = Nomination::where(['id' => $inputNomination])->with(['ads'])->first();
if(!$nomination || !($nomination->ad_id == $inputAd)) {
// not the same
}
// same

To validate the ad_id and nomination_id, you can use laravel in rule.
FormRequest Class
public function rules()
{
return [
'ad_id' => [
'required',
Rule::in(Ad::where('active', true)->pluck('id')->toArray()),
],
'nomination_id' => [
'required',
Rule::in(Nomination::where('id', $this->nomination_id)->where('ad_id', $this->ad_id)->pluck('id')->toArray()),
],
];
}
The Rule::in(Ad::where('active', true)->pluck('id')->toArray()), rule will check if the ad_id is present in the array of ids of Ad which have active field is true.
The Rule::in(Nomination::where('id', $this->nomination_id)->where('ad_id', $this->ad_id)->pluck('id')->toArray()), rule will check if the nomination_id is present in the array of ids of Nomination which is related to Ad.

Related

How to validate unique more than one field in Laravel 9?

I want to do validate when store and update data in Laravel 9. My question is how to do that validate unique more than one field?
I want to store data, that is validate formId and kecamatanId only one data stored in database.
For example:
formId: 1
kecamatanId: 1
if user save the same formId and kecamatanId value, its cant saved, and show the validation message.
But if user save:
formId: 1,
kecamatanId: 2
Its will successfully saved.
And then user save again with:
formId: 1,
kecamatanId: 2
It cant saved, because its already saved with the same condition formId and kecamatanId.
My current validate code:
$this->validate($request, [
'formId' => 'required|unique:data_masters',
'kecamatanId' => 'required',
'level' => 'required',
'fieldDatas' => 'required'
]);
Update:
I have try:
use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\Rule;
$formId = $request->formId;
$kecamatanId = $request->kecamatanId;
Validator::make($request, [
'formId' => [
'required',
Rule::unique('data_masters')->where(function ($query) use ($formId, $kecamatanId) {
return $query->where('formId', $formId)->where('kecamatanId', $kecamatanId);
}),
],
]);
But its return error:
Illuminate\Validation\Factory::make(): Argument #1 ($data) must be of type array, Illuminate\Http\Request given, called in /Volumes/project_name/vendor/laravel/framework/src/Illuminate/Support/Facades/Facade.php on line 338
You can dry using the different:field rule : which validate that the validated field must be different to the given field;
$this->validate($request,[
'formId' => ['unique:users'],
'kecamatanId' => [
'required',
Rule::unique('users')->where(fn($query) => $query->where('formId', $request->input('formId')))
]
]);

laravel unique validation on foreign key

I have database table streets in which I am storing name and block_id as foreign key from the blocks table. So I want the name of street should be unique in every block. for example if the name of street is street 1 with block_id 1, it should not be added again with block_id 1 but it should be allowed to add street 1 with block_id 2 or 3. How can I get it with Laravel validation?
My validation Code:
here you can see I am validating uniqueness only on those records which have delete = false.
$validatedValues = $request->validate([
'block' => 'required',
'street' => [
'required',
Rule::unique('streets', 'sName')->where(function ($query) {
return $query->where('delete', false);
})
],
'street_width' => 'required',
]);
Replace this with street key in your code
'name' => [
'required',
Rule::unique('streets')->where(function ($query) use($request) {
return $query->where('name', $request->name)
->where('block_id', $request->block_id);
}),
],
try this one
Create request -> PHP artisan make:request StreetsRequest
in ur controller find the store method or whatever the name u put for storing ur data, and change the (Request $request) to (StreetsRequest $request)
use App\Http\Requests\StreetsRequest;
....
public function store(StreetsRequest $request){
$request->validated();
// Do whatever u want after validate
}
Open your StreetsRequest file and inside the return [] put sth like this
use Illuminate\Validation\Rule;
...
'block_id' => [
'required',
Rule::unique('streets','block_id')->ignore(request('id')),
],
i hope it will work , let us know if u still have any problem, thx
check laravel validation for more
[https://laravel.com/docs/8.x/validation#form-request-validation]
You can try writing custom validation for that
$validator = $request->validate([
'block' => 'required',
'street' => [
function ($attribute, $value, $fail) use($request){
$street= Street::where('delete', false)->where(function ($query)use($request){
$query->where('sName',$request->sName);
$query->where('block_id',$request->block_id );
})->exists();
if($street){
$fail('The '.$attribute.' is invalid.');
}
},
],
]);
I hope for streets table you have Street model
if any issue let me know .

Laravel - Unique Request Rules allows duplicate

In Laravel-5.8 I applied Rules Request for multiple fields as shown below:
public function rules()
{
return [
'goal_type_id' => [
'required',
Rule::unique('appraisal_goals')->where(function ($query) {
return $query
->where('employee_id', 1)
->where('appraisal_identity_id', 1);
})
],
'appraisal_doc' => 'nullable|mimes:doc,docx,xls,xlsx,ppt,pptx,pdf,jpg,jpeg,bmp,png,|max:5000',
'weighted_score' => 'required|numeric|min:0|max:500',
];
}
This is mysql query:
ALTER TABLE appraisal_goals
ADD CONSTRAINT appraisal_goals_uniq1 UNIQUE KEY(goal_type_id,appraisal_identity_id,employee_id);
This is meant for create. From the code the combination of goal_type_id, employee_id and appraisal_identity_id are unique.
When I click on submit in create blade, it allows duplicate.
How do I resolve this?
Also, how do I write the one for update?
Please note that my route is appraisal_goal
Thank you
Try this code for update
public function rules()
{
$appraisal_goal_id = 'take your appraisal_goals id here from request';
return [
'goal_type_id' => [
'required',
Rule::unique('appraisal_goals')->ignore($appraisal_goal_id, 'id')->where(function ($query) {
return $query
->where('employee_id', 1)
->where('appraisal_identity_id', 1);
})
],
'appraisal_doc' => 'nullable|mimes:doc,docx,xls,xlsx,ppt,pptx,pdf,jpg,jpeg,bmp,png,|max:5000',
'weighted_score' => 'required|numeric|min:0|max:500',
];
}

Laravel avoid duplicate entry from model

I'm building a Laravel API. I have a models called Reservations. I want to avoid that a user creates two reservations for the same product and time period.
I have the following:
$reservation = Reservation::firstOrCreate([
'listing_id' => $request->listing_id,
'user_id_from' => $request->user_id_from,
'start_date' => $request->start_date,
'end_date' => $request->end_date,
]);
Edit after comments:
I'm also using validation
$validator = Validator::make($request->all(), [
'listing_id' => 'required|exists:listings,id',
'user_id_from' => 'required|exists:users,id',
'start_date' => 'required|date_format:"Y-m-d"|after:today',
'end_date' => 'required|date_format:"Y-m-d"|after:start_date'
]);
if ($validator->fails()) {
return response()->json(['error' => 'Validation failed'], 403);
}
Validation is working properly.
End of Edit
In my model I have casted the start_date and end_date as dates.
class Reservation extends Model
{
protected $fillable = ['listing_id', 'start_date', 'end_date'];
protected $dates = [
'start_date',
'end_date'
];
....
....
Documentation says:
The firstOrCreate method will attempt to locate a database record
using the given column / value pairs
However I notice that I'm still able to insert entries with the same attributes.
Any idea what I'm doing wrong or suggestions to fix it?
Probably there's a better way than this, but you can create an static method on Reservation to do this, like:
public static function createWithRules($data) {
$exists = $this->where('product_id', $data['product_id'])->whereBetween(*date logic that i don't remember right now*)->first();
if(!$exists) {
* insert logic *
} else {
* product with date exists *
}
}
So you can call Reservation::createWithRules($data)
You can achieve this using Laravel's built in ValidateRequest class. The most simple use-case for this validation, is to call it directly in your store() method like this:
public function store(){
$this->validate($request, [
'listing_id' => 'required|unique,
'start_date' => 'required|unique,
//... and so on
], $this->messages);
$reservation = Reservation::firstOrCreate([
'listing_id' => $request->listing_id,
'user_id_from' => $request->user_id_from,
'start_date' => $request->start_date,
'end_date' => $request->end_date,
]);
}
With this, you're validating users $request with by saying that specified columns are required and that they need to be unique, in order for validation to pass.
In your controller, you can also create messages function to display error messages, if the condition isn't met.
private $messages = [
'listing_id.required' => 'Listing_id is required',
'title.unique' => 'Listing_id already exists',
//... and so on
];
You can also achieve this by creating a new custom validation class:
php artisan make:request StoreReservation
The generated class will be placed in the app/Http/Requests directory. Now, you can add a few validation rules to the rules method:
public function rules()
{
return [
'listing_id' => 'required|unique,
'start_date' => 'required|unique,
//... and so on
];
}
All you need to do now is type-hint the request on your controller method. The incoming form request is validated before the controller method is called, meaning you do not need to clutter your controller with any validation logic:
public function store(StoreReservation $request)
{
// The incoming request is valid...
// Retrieve the validated input data...
$validated = $request->validated();
}
If you have any additional question about this, feel free to ask. Source: Laravel official documentation.

Two store() methods, one doesn't work (Call to undefined method save())

One of two similar store methods doesn't work. Could you clarify this for me?
Relations
A Team hasMany Users <> A User belongsTo a Team
A User hasMany Characters <> A Character belongsTo a User
Working Code (CharacterController)
public function store()
{
$fighters = Fighter::pluck('name')->toArray();
$this->validate(request(), [
'name' => 'required|min:3|max:25|alpha_num|not_in:'.Rule::notIn($fighters).'unique:characters',
'fighter' => 'required|in:'.Rule::in($fighters),
]);
auth()->user()->characters()->save(new Character([
'name' => request('name'),
'fighter' => request('fighter'),
]));
return redirect()->route('character.index');
}
Not Working (TeamController)
public function store()
{
$this->validate(request(), [
'name' => 'required|min:3|max:25|alpha_num|unique:teams',
]);
auth()->user()->team()->save(new Team([
'name' => request('name'),
'fame' => 0,
]));
return redirect()->route('team.index');
}
Questions
Why is the same method not available? Is it relation stuff?
Is the create method better? Should I try to use it?
Thought I know what I'm doing, now it turns out I don't...
Thank you for helping.
team() is a belongsTo relation, you probably have a team_id col in your user table which you want to associate with the team.
public function store()
{
$this->validate(request(), [
'name' => 'required|min:3|max:25|alpha_num|unique:teams',
]);
// create and save team
$team = new Team([
'name' => request('name'),
'fame' => 0,
]);
$team->save();
// associate current authenticated user with team (set foreign key) and save user
auth()->user()->team()->associate($team)->save();
return redirect()->route('team.index');
}

Resources