Laravel - How to validate dates with relative formats? - php

PHP defines the relative formats and Laravel doesn't seen to have an available validation rule for that. For example:
/**
* Get the validation rules that apply to the request.
*
* #return array
*/
public function rules()
{
return [
'created-at-from' => 'relative_format',
'created-at-until' => 'nullable|relative_format|gte:created-at-from'
];
}
How can we validate those formats?
UPDATE
What I'm using now:
Create a rule class.
php artisan make:rule RelativeFormat
Put the logic.
/**
* Determine if the validation rule passes.
*
* #param string $attribute
* #param mixed $value
* #return bool
*/
public function passes($attribute, $value)
{
return (bool) strtotime($value);
}
And validates:
/**
* Get the validation rules that apply to the request.
*
* #return array
*/
public function rules()
{
return [
'created-at-from' => [new RelativeFormat],
'created-at-until' => ['nullable', new RelativeFormat]
];
}

You can just create your own validation rule:
Validator::extend('relative_format', function($attribute, $value, $parameters)
{
return (bool) strtotime($value);
});
And add it to your AppServiceProvider.

Related

Make parameter unique in laravel request

hello I have a table called gallery_category_contents
language
title
gallery_category_id
en
FISH
1
en
DOGS
2
and my request rules are like this
public function rules()
{
return [
"language"=>[
"required",
Rule::unique('gallery_category_contents','language')->ignore($this->gallery_category_id,'gallery_category_id')
],
"title"=>[
"string",
"required"
]
];
}
what i want is that it doesn't add already existing language with same gallery_category_id
For example, English language has been added to 1 gallery_category_id, so it should not be added again. my current code works, but it affects all the data in that table, that is, it also affects category 2
how to do it?
Please try this way:
public function rules(): array
{
return [
'gallery_category_id' => [
'required',
Rule::exists('gallery_category'),
],
'language' => [
'required',
Rule::unique('gallery_category_contents', 'language')
->where('gallery_category_id', $this->input('gallery_category_id')),
],
'title' => [
'required',
'string',
]
];
}
You could make your own validation rule:
php artisan make:rule CompositeUnique
<?php
namespace App\Rules;
use Illuminate\Contracts\Validation\Rule;
class CompositeUnique implements Rule, DataAwareRule
{
private array $data;
private array $columns;
private $model;
private $ignoreId;
/**
* Create a new rule instance.
*
* #param $model
* #param string|array $colums
* #return void
*/
public function __construct($model, $columns, $ignoreId)
{
$this->model = $model;
$this->columns = (array)$columns;
}
/**
* Determine if the validation rule passes.
*
* #param string $attribute
* #param mixed $value
* #return bool
*/
public function passes($attribute, $value)
{
return $this->model::query()
->where($this->columns, array_reduce(function($where, $column) {
$where[$column] = $this->data[$column];
return $where;
}, [])
->where($this->model->getKeyName(), $this->ignoreId)
->doesntExist();
}
/**
* Get the validation error message.
*
* #return string
*/
public function message()
{
return 'The :attribute must be unique.';
}
/**
* Set the data under validation.
*
* #param array $data
* #return $this
*/
public function setData($data)
{
$this->data = $data;
return $this;
}
}
And use it like this:
use App\Rules\CompositeUnique;
use App\Models\GalleryCategoryContents;
//...
new CompositeUnique(
GalleryCategoryContents::class,
[ 'language' 'gallery_category_id' ],
$this->gallery_category_id
);`

Laravel Complex Validation Rule inside Array

I wrote an API (Rest, JSON) and would like to validate incoming requests.
The API expects an field "Plan".
Within this field, the client have two choices: "o2_Plan" or "telekom_Plan".
One of them are required. So my validation looks like:
'Plan.o2_Plan' => 'required_without:Plan.telekom_Plan|array',
'Plan.telekom_Plan' => 'required_without:Plan.o2_Plan',
This is working fine. But there are another conditional rules within the plans:
'Plan.o2_Plan.tariff_variation_code' => 'required_without:Plan.o2_Plan.article_id|string',
'Plan.o2_Plan.article_id' => 'sometimes|required_without:Plan.o2_Plan.tariff_variation_code|string',
That means, you have to enter a tariff_variation_code OR an article_id or both of them.
But if the client only transfer the telekom_Plan (which must possible), the validation failed, with this errors:
{
"errors": {
"Plan.o2_Plan.tariff_variation_code": [
"The tariff_variation_code field is required when article_id is not present."
],
"Plan.o2_Plan.article_id": [
"The article_id field is required when tariff_variation_code is not present."
]
}
}
How can I achive, that the validation inside the o2_Plan only works, if the o2_Plan is present.
Thanks in advance.
best regards
Martin
I have the following solution:
Changed
'Plan.o2_Plan' => 'required_without:Plan.telekom_Plan|array',
to
'Plan.o2_Plan' => ['required_without:Plan.telekom_Plan', 'array', new o2Plan(request('Plan.o2_Plan'))],
My o2Plan rule class looks like:
namespace App\Rules;
use Illuminate\Contracts\Validation\Rule;
class o2Plan implements Rule
{
protected $request;
protected $message_text = '';
/**
* Create a new rule instance.
*
* #param array $request
*/
public function __construct($request)
{
$this->request = $request;
}
/**
* Determine if the validation rule passes.
*
* #param string $attribute
* #param mixed $value
* #return bool
*/
public function passes($attribute, $value)
{
if ((!isset($this->request['tariff_variation_code']) || empty($this->request['tariff_variation_code']))
&&
(!isset($this->request['article_id']) || empty($this->request['article_id']))
) {
$this->message_text = 'tariff_variation_code or article should not be empty';
return false;
}
return true;
}
/**
* Get the validation error message.
*
* #return string
*/
public function message()
{
return $this->message_text;
}
}
And it is working fine :-)

Laravel 5.5 use custom validation rule in validation request file?

Is it possible to use my custom validation rule in a validation request file?
i want to use my custom rule called EmployeeMail
here is the code of the request file
class CoachRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* #return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* #return array
*/
public function rules()
{
$rules = [];
if ($this->isMethod('post') ) {
$rules = [
'name' => 'required|string',
'email' => 'required|email|employeemail', <<<--- this
'till' => 'required|date_format:H:i|after:from',
];
}
//TODO fix this
//TODO add custom messages for every field
return $rules;
}
}
it gives me an error when i try to use it like this
Method [validateEmployeemail] does not exist.
code of custom rule
namespace App\Rules;
use Illuminate\Contracts\Validation\Rule;
class EmployeeMail implements Rule
{
/**
* Create a new rule instance.
*
* #return void
*/
public function __construct()
{
//
}
/**
* Determine if the validation rule passes.
*
* #param string $attribute
* #param mixed $value
* #return bool
*/
public function passes($attribute, $value)
{
// If mail is that of an employee and not a student pass it
return preg_match("/#test.nl$/", $value) === 1;
}
/**
* Get the validation error message.
*
* #return string
*/
public function message()
{
return 'Email is geen werknemers mail';
}
}
can i only use this custom rule like this?
$items = $request->validate([
'name' => [new FiveCharacters],
]);
Rutvij Kothari answered the question in the comments.
It seems you are validating string with a regular expression, the same logic can be achieved by regex buit-in validation method. Check it out. laravel.com/docs/5.5/validation#rule-regex No need to create your own validation rule. – Rutvij Kothari
If you want to use your validation pass it into an array. like this. 'email' => ['required', 'email', new employeemail]

Laravel 5.5 conditional form request validation rule on update

I created a validation rule for the image form.
It works fine on store method but I do not want the image field to be required on update because I may only update the title for example.
class ImageRequest extends Request
{
/**
* Rules array
*/
protected $rules = [
'title' => 'required|string|between:3,60',
'alt' => 'sometimes|string|between:3,60',
'image' => 'required|image|max:4000|dimensions:min_width=200,min_height=200',
];
/**
* Determine if the user is authorized to make this request.
*
* #return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* #return array
*/
public function rules()
{
return $this->rules;
}
}
For unique validation we can add custom query conditions:
'email' => Rule::unique('users')->ignore($user->id, 'user_id')
or
'email' => Rule::unique('users')->where(function ($query) {
return $query->where('account_id', 1);
})
Is it a clean way to achieve something similar for required?
Apply required only for new images.
you Can use switch statement inside rule
public function rules()
{
switch ($this->method()) {
case 'GET':
case 'DELETE': {
return [];
}
case 'POST': {
return [
'first_name'=>'required',
'last_name'=>'required',
'email'=>'required|email|unique:users,email,'.$this->id,
'password'=>'',
'dob'=>'required',
'phone_one'=>'required',
'phone_two'=>'required',
//'user_role'=>'required',
// 'profile_image'=>'required'
];
}
case 'PUT':
case 'PATCH': {
return [
];
}
default:break;
}
Also you can use condtion like on update yuo have id so based on that you can check whether its update or insert since on insert you dont have id so
Create another class that extends the Request class, DI that into your update controller action
class UpdateImageRequest extends Request
{
/**
* Rules array
*/
protected $rules = [
'title' => 'required|string|between:3,60',
'alt' => 'sometimes|string|between:3,60'
];
/**
* Determine if the user is authorized to make this request.
*
* #return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* #return array
*/
public function rules()
{
return $this->rules;
}
}
much better way is to use nullable in Laravel 5.5 validations
Ref Docs
The field under validation may be null. This is particularly useful
when validating primitive such as strings and integers that can
contain null values.
class ImageRequest extends Request
{
/**
* Rules array
*/
protected $rules = [
'title' => 'required|string|between:3,60',
'alt' => 'nullable|string|between:3,60',
'image' => 'nullable|image|max:4000|dimensions:min_width=200,min_height=200',
];
/**
* Determine if the user is authorized to make this request.
*
* #return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* #return array
*/
public function rules()
{
return $this->rules;
}
}
However I have used recently with image and it worked like charm for me. Give it a try!
The simplest way in this case in the other way. By default have rules for update and if it's store add required like so:
class ImageRequest extends Request
{
/**
* Rules array
*/
protected $rules = [
'title' => 'required|string|between:3,60',
'alt' => 'sometimes|string|between:3,60',
'image' => 'image|max:4000|dimensions:min_width=200,min_height=200',
];
/**
* Determine if the user is authorized to make this request.
*
* #return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* #return array
*/
public function rules()
{
$rules = $this->rules;
if ($this->isMethod('POST')) {
$rules['image'] = 'required|' . $rules['image']
}
return $rules;
}
}
I found a solution.
I renamed image into file.
The route is homestead.app/images/1 on update and homestead.app/images on store so the $image property will be $this->image = 1 on update and $this->image = null on store.
class ImageRequest extends Request
{
/**
* Rules array
*/
protected $rules = [
'title'=> 'required|string|between:3,60',
'alt' => 'sometimes|string|between:3,60',
'file' => [
'image',
'max:4000',
'dimensions:min_width=200,min_height=200',
],
];
/**
* Determine if the user is authorized to make this request.
*
* #return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* #return array
*/
public function rules()
{
$this->rules['file'][] = is_null($this->image) ? 'required' : 'sometimes';
return $this->rules;
}
}

Laravel form validation unique using 2 fields

How can I have a unique validation rule on 2 fields?
a. The application should not allow two people to have the same identical first name and last name.
It is allowed that the users fills in only a first name or only a last name. Because the user may have only one of them.
b. But if the user enters only a first name (Glen), no other person in the table should have the same (first name = 'Glen' and last name = null). another 'Glen Smith' ok.
I tried the following rule. It works great when both fields (first and last name) are not null:
'firstName' => 'unique:people,firstName,NULL,id,lastName,' . $request->lastName
This rule fails on b. when only one field is present.
Any hint?
The built in unique validator wouldn't really support what you're trying to do. It's purpose is to ensure that a single valid is unique in the database, rather than a composite of two values. However, you can create a custom validator:
Validator::extend('uniqueFirstAndLastName', function ($attribute, $value, $parameters, $validator) {
$count = DB::table('people')->where('firstName', $value)
->where('lastName', $parameters[0])
->count();
return $count === 0;
});
You could then access this new rule with:
'firstName' => "uniqueFirstAndLastName:{$request->lastName}"
You'll probably find you might need to tweak your database query a little bit as it's untested.
I think you are looking for something like that:
'unique:table_name,column1,null,null,column2,'.$request->column2.',column3,check3'
This is an extensive answer to this question and how to create Laravel custom validator generally, you can simply copy and paste, and try to understand later:
Step 1: Create a provider app/Providers/CustomValidatorProvider.php
<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Facades\Validator as ValidatorFacade;
/**
* Provider for custom validators. Handles registration for custom validators.
*
*/
class CustomValidatorProvider extends ServiceProvider {
/**
* An array of fully qualified class names of the custom validators to be
* registered.
*
* #var array
*/
protected $validators = [
\App\Validators\MultipleUniqueValidator::class,
];
/**
* Bootstrap the application services.
*
* #return void
* #throws \Exception
*/
public function boot() {
//register custom validators
foreach ($this->validators as $class) {
$customValidator = new $class();
ValidatorFacade::extend($customValidator->getName(), function() use ($customValidator) {
//set custom error messages on the validator
func_get_args()[3]->setCustomMessages($customValidator->getCustomErrorMessages());
return call_user_func_array([$customValidator, "validate"], func_get_args());
});
ValidatorFacade::replacer($customValidator->getName(), function() use ($customValidator) {
return call_user_func_array([$customValidator, "replacer"], func_get_args());
});
}
}
/**
* Register the application services.
*
* #return void
*/
public function register() {
//
}
}
Step 2: Update your app.php in your config folder config/app.php to include your created provider in the provider array
App\Providers\CustomValidatorProvider::class,
Step 3: Create your custom validator, in my case, I am creating multiple unique fields validator app/Validators/MultipleUniqueValidator.php
<?php
namespace App\Validators;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Str;
use Illuminate\Validation\Validator;
/**
* Multiple field uniqueness in laravel
*/
class MultipleUniqueValidator{
/**
* Name of the validator.
*
* #var string
*/
protected $name = "multiple_unique";
/**
* Return the name of the validator. This is the name that is used to specify
* that this validator be used.
*
* #return string name of the validator
*/
public function getName(): string {
return $this->name;
}
/**
*
* #param string $message
* #param string $attribute
* #param string $rule
* #param array $parameters
* #return string
*/
public function replacer(string $message, string $attribute, string $rule, array $parameters): string {
unset($parameters[0]);
$replacement = implode(", ", $parameters);
$replacement = str_replace("_", " ", $replacement);
$replacement = Str::replaceLast(", ", " & ", $replacement);
$replacement = Str::title($replacement);
return str_replace(":fields", "$replacement", $message);
}
/**
*
* #param string $attribute
* #param mixed $value
* #param array $parameters
* #param Validator $validator
* #return bool
* #throws \Exception
*/
public function validate(string $attribute, $value, array $parameters, Validator $validator): bool {
$model = new $parameters[0];
if (!$model instanceof Model) {
throw new \Exception($parameters[0] . " is not an Eloquent model");
}
unset($parameters[0]);
$this->fields = $parameters;
$query = $model->query();
$request = app("request");
foreach($parameters as $parameter){
$query->where($parameter, $request->get($parameter));
}
return $query->count() == 0;
}
/**
* Custom error messages
*
* #return array
*/
public function getCustomErrorMessages(): array {
return [
$this->getName() => ":fields fields should be unique"
];
}
}
Now you can do this in your request
'ANY_FIELD_CAN_CARRY_IT' => 'required|numeric|multiple_unique:'.YOUR_MODEL_HERE::class.',FIELD_1,FIELD_2,FIELD_3...',
Laravel now allows you to add where clauses into the unique rule.
In your case you could do something like this:
'firstName' => [
Rule::unique('people', 'firstName')->where(function ($query) use ($lastName) {
return $query->where('lastName', $lastName);
})
],
in my case this works just fine (in controller):
$request->validate([
'firstName' => 'required|min:3|max:255|unique:people,firstName,NULL,id,lastname,' . $request->input('lastname'),
], [
'unique' => 'Some message for "unique" error',
]);
You can do it if the Validator class isn't required for you:
if(Model::query()->where([
'column_1' => 'data_1',
'column_2' => 'data_2'
])->exists())
{
// some code..
}

Categories