Laravel Validator - Check custom validation rule after other rules get checked - laravel

How are you? Hope you are doing great
I need one help for Laravel Validator, i have created one custom validation rule like below
$validation = Validator::make($request->all(), [
'user_id' => 'required',
'role' => ['required', new RoleExist($request->user_id)],
]);
See i have passed one argument to rule's constructor new RoleExist($request->user_id) but laravel giving me 500 error if i do not pass user_id in the request
The error is
Argument 1 passed to App\Rules\RoleExist::__construct() must be of the type integer, null given
I know user_id is not passed in the request so laravel giving above error, but here my custom rule should be execute after 'user_id' => 'required',
Custom Rule Code
private $userId;
public function __construct(Int $userId)
{
$this->userId= $userId;
}
public function passes($attribute, $value)
{
return empty(\App\User::where('user_id', $this->userId)->where('status', '1')->first());
}
Is there any way to do the same
Thank you in advance

Related

The unique Rule validation is not applied

I am using laravel 9.
And I have this very simple validation rule (form request validation):
public function rules()
{
return [
'first_name' => 'required|max:50',
'last_name' => 'required|max:50',
'email' => [
'email',
Rule::unique('unknown_table')->where(function ($query) {
return $query->where('library_id', $this->library->id);
})
],
.......
What I do not understand is why my post is working with this table name "unknown_table", because this table really does not exist. I expected an exception but nothing happens !
Is this rule correctly written ?
I have understood my error. The "email" validation was twice in the rule array ! And it was the last one which was used of course.

How to validate inputs from GET request in Laravel

I wanted to validate inputs from a GET request without using the
this->validate($request... or \Validator::make($request...
and prefer to do it like
$input = $request->validate([... rules ...]);
however since get requests doesn't have $request parameters how can I achieve it?
public function sampleGet($param1, $param2) {
// How can I pass the $param1 and $param to to validate?
$input = $request->validate([
'param1' => 'required',
'param2' => 'required
]);
}
You can do so and it will have same behavior as validate
validator($request->route()->parameters(), [
'param1' => 'required',
'param2' => 'required'
....
])->validate();
If you want all the route parameters you can get them as an array:
$request->route()->parameters()
Since you already have those parameters being passed to your method you can just build an array with them:
compact('param1', 'param2');
// or
['param1' => $param1, 'param2' => $param2];
You are not going to be using the validate method on the Request though, you will have to manually create a validator. Unless you want to merge this array into the request or create a new request with these as inputs.
There is nothing special about the validate method on a Controller or on a Request. They are all making a validator and validating the data the same way you would yourself.
When manually creating a validator you still have a validate method that will throw an exception, which would be the equivalent to what is happening on Request and the Controller with their validate methods.
Laravel 7.x Docs - Validation - Manualy Creating Validators - Automatic Redirection
You can do like that.
public function getData(Request $request)
{
try {
$input['route1'] = $request->route('route1');
$input['route2'] = $request->route('route2');
$valid = Validator::make($input, [
'route1' => 'required',
'route2' => 'required'
]);
} catch (\Throwable $th) {
echo "<pre>";print_r($th->__toString());die;
}
}
Or you can follow the below link for more info.
https://laravel.com/docs/7.x/validation#manually-creating-validators

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.

Laravel 5.6 Validation on empty fields

Somehow I feel like this should be a common question, but I can't seem to find a definite answer on that one.
The problem is quite simple:
On validating a form, I would like to exclude the empty non-required fields from the resulting array - and this in order to use the default value set at database level.
Since Laravel is using the ConvertEmptyStringsToNull middleware by default (and I am not so keen on changing that), it means that my empty fields will be converted to 'null' and sent to my database (hence not getting their default value, and actually breaking the query since those fields are not nullable at database level).
$userData = $request->validate([
'email' => 'required|string|email|max:255|unique:users',
'number_of_whatever' => //if this field is empty, I want it stripped out the $userData array - or automatically default to the database default
]);
Any help on how to solve this in the cleanest way possible would be much appreciated! I was thinking about making a custom rule that would exclude the field itself (so I could reuse this validation rule across the project without having to manually do it every time we come across the situation).
Another option would be to set it at Model level - but not so keen on doing that, it seems weird to have to do it there when it's already done at DB level.
Thanks!
i think you can use nullable rule
$userData = $request->validate([
'email' => 'required|string|email|max:255|unique:users',
'number_of_whatever' => 'nullable'
]);
Hey so i've found your issue and also a sort of work around for this. Based on the below example i've replicated and now understood your issue properly, even if the validator allows null values the create method throws an error and does not set default values.
Controller
$validator = Validator::make($data, [
'name' => 'max:255',
'email' => 'max:255',
'password' => 'max:255',
]);
if ($validator->fails()) {
dd('Validator has failed');
}
// This throws an error saying that the fields cannot be null!
User::create($data);
Users Table
Schema::create('users', function (Blueprint $table) {
$table->increments('id');
$table->string('name')->default('Ken');
$table->string('email')->default('ken#stackoverflow.com');
$table->string('password')->default(bcrypt('password'));
$table->rememberToken();
$table->timestamps();
});
The work around i've devised is this, remove all null values from the post request before it hits the validator like so.
Example
$data = ['name' => null, 'email' => null, 'password' => null];
foreach($data as $key => $value)
{
if($value == null)
{
unset($data[$key]);
}
}
The logic here is by removing the fields from the post request that are null, the USER object does not see them as having a value, therefore allowing the tables default values to table place, but if the value is null this is still deemed as a value so the default value will be ignored.
I hope this makes sense.
Result of my full code
Create a FormRequest and filter out the null values using the prepareForValidation method:
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class TestRequest extends FormRequest
{
public function authorize()
{
return true;
}
public function rules()
{
return
[
'username' => 'required|string|email|max:255|unique:users',
'number_of_whatever' => 'sometimes|integer',
];
}
protected function prepareForValidation()
{
if($this->number_of_whatever == null) {
$this->request->remove('number_of_whatever');
}
}
}
You can apply any validation other than 'required' after the 'sometimes' rule and will be applied only if the value isn't null.

Using a php variable for validation in Laravel 4.2

I have this code that gets an object in a session and i put it in a certain variable. Then i wanted to use it in the laravel 4.2 'same' validation. So far here is the code i have
session_start();
$getsCapt = $_SESSION["captcha"];
$rules = array(
'st' => 'required',
'capt' => 'required|numeric|same:$getsCapt'
);
It is not doing anything. What i wanted to have is i'll compare the value i get from the session with the value i get from the textbox in my view named 'capt' but so far its not doing anything. any ideas to do this properly?
First of all, You are using same validator incorrectly.
same expects a form field name
Example:
same:field_name
Where, the given field must match the field under validation.
You could register and use a Custom Validation Rule
Validator::extend('captcha', function($attribute, $value, $parameters)
{
$captcha = \Session::get('captcha');
return $value == $captcha;
});
So later you can do:
//session_start(); - Please no need for this in Laravel
//$getsCapt = $_SESSION["captcha"]; - Also remove this not necessary
$rules = array(
'st' => 'required',
'capt' => 'required|numeric|captcha'
);
NB:
Use Session::put to save something to session e.g \Session::put('something');
There is also Session::get for retrieving value from session e.g \Session::get('something');
Please avoid using $_SESSION not Laravel way of doing things
[Edited] Where to Register Custom Validation Rule?
There are basically two ways you can register a custom validation rule in Laravel.
1. Resolving from a closure:
If you are resolving through closure you can add it to : app/start/global.php
Validator::extend('captcha', function($attribute, $value, $parameters)
{
$captcha = \Session::get('captcha');
return $value == $captcha;
});
2. Resolving from a class
This is the best and preferred way of extending custom validation rule as its more organised and easier to maintain.
i. Create your own validation class, CustomValidator.php, maybe in app/validation folder
<?php namespace App\Validation;
use Illuminate\Validation\Validator;
use Session;
class CustomValidator extends Validator{
public function validateCaptcha($attribute, $value, $parameters)
{
$captcha = Session::get('captcha');
return $value == $captcha;
}
}
NB: Notice the prefix validate used in the method name, validateCaptcha
ii. Create a Service Provider that will resolve custom validator extension in app/validation folder
<?php namespace App\Validation;
use Illuminate\Support\ServiceProvider;
class CustomValidationServiceProvider extends ServiceProvider {
public function register(){}
public function boot()
{
$this->app->validator->resolver(function($translator, $data, $rules, $messages){
return new CustomValidator($translator, $data, $rules, $messages);
});
}
}
iii. Then add CustomValidationServiceProvider under app/config/app.php providers array:
'providers' => array(
<!-- ... -->
'App\Validation\CustomValidationServiceProvider'
),
iv. And add the custom error message in app/lang/en/validation.php
return array(
...
"captcha" => "Invalid :attribute entered.",
...
)
Change single quotes to double quotes
$rules = array(
'st' => 'required',
'capt' => "required|numeric|same:$getsCapt"
);
Or simply concatenate the value
$rules = array(
'st' => 'required',
'capt' => 'required|numeric|same:' . $getsCapt
);

Resources