How to validate date time with many format in laravel - php

I want to check my request datetime with many format look like below format:
Y-m-d
Y-m-
Y-m
In Laravel, I use Validator to validate datetime, but Validator can not make checking with many format. This is my code:
Validator::make(['date' => $departureDate], [
'date' => 'required|date_format:Y-m-d, Y-m, Y-m-'
]);
How can I do it in laravel
Please help me! Many thanks!

You must write a custom validation format for that. Laravel's date_format expects only one parameter and not capable of handling multi-formats. There are Two ways to add custom validation. first, one is making a rule repository and add your validation logic there. Here Taylor Otwell explained this method.
The other way to doing that is extend validation in app service provider and add new rule there. add this code in app service provider:
use Illuminate\Support\Facades\Validator;
Validator::extend('multi_date_format', function ($attribute, $value, $parameters,$validator) {
$ok = true;
$result = [];
// iterate through all formats
foreach ($parameters as $parameter){
//validate with laravels standard date format validation
$result[] = $validator->validateDateFormat($attribute,$value,[$parameter]);
}
//if none of result array is true. it sets ok to false
if(!in_array(true,$result)){
$ok = false;
$validator->setCustomMessages(['multi_date_format' => 'The format must be one of Y-m-d ,Y-m or Y-m-']);
}
return $ok;
});
And here you can use it this way:
$validator = Validator::make(['date' => '2000-02-01'], [
'date' => 'required|multi_date_format:Y-m-d,Y-m,Y-m-'
]);
if($validator->fails()) {
$errors = $validator->errors()->all();
}

You can register custom validator in file app/Providers/AppServiceProvider.php in boot method.
Validator::extend('several_date_format', function ($attribute, $value, $parameters,$validator) {
foreach ($parameters as $parameter){
if (!$validator->validateDateFormat($attribute,$value,[$parameter]))
return false;
}
return true;
});
Now you can use it
'your_date' => 'required|several_date_format:Y-m-d,...'
Optional after it you can add custom message in resources/lang/en/validation.php
return [
...
'several_date_format' => 'Error text'
...
]

Related

Create rule to make request only contain certain keys

I am using the Lumen Framework, which utilizes the Laravel Validation
I wanted to create a Validator Rule to make the Request->input() json only contain specific keys at the root like "domain" and "nameservers". Not more and not less.
Example passing the rule:
{
"domain":"domain.tld",
"nameservers":
{...}
}
Example not passing the rule:
{
"domain":"domain.tld",
"nameservers":
{...},
"Hack":"executeSomething()"
}
I tried to use to use several default validation rules to achieve this but wasnt successful.
My approach was now to put the request in another array like this
$checkInput['input'] = $request->all();
to make the validator validate the "root" keys.
Now this is my Approach:
create the validator
$checkInput['input'] = $request->all();
$validator = Validator::make($checkInput, [
'input' => [
'onlyContains:domain,nameservers'
],
]);
creating the rule
Validator::extend('onlyContains', function($attribute, $value, $parameters, $validator){
$input = $validator->getData();
$ok = 0;
foreach ($parameters as $key => $value) {
if (Arr::has($input, $attribute . '.' . $value)) {
$ok++;
}
}
if (sizeof(Arr::get($input, $attribute)) - $ok > 0) {
return false;
}
return true;
});
It seems i got the desired result, but i am asking if there is maybe smarter solution to this with the default rules provided by Laravel/Lumen.
You are trying to do a blacklisting approach blocking out fields that are not intended. A simple approach, that is utilized a lot, is to only fetch out the validated. Also you are trying to do logic, that goes against normal validation logic, to do it a field at a time.
This is also a good time, to learn about FormRequest and how you can get that logic, into a place where it makes more sense.
public function route(MyRequest $request) {
$input = $request->validated();
}
With this approach, you will only ever have the validated fields in the $input variable. As an extra bonus, this approach will make your code way easier to pick up by other Laravel developers. Example form request below.
public class MyRequest extends FormRequest
{
public function rules()
{
return [
'domain' => ['required', 'string'],
'nameservers' => ['required', 'array'],
];
}
}
You should use prohibited rule.
For eg:
$allowedKeys = ['domain', 'nameservers'];
$inputData = $request->all();
$inputKeys = array_keys($inputData);
$diffKeys = array_diff($inputKeys, $allowedKeys);
$rules = [];
foreach($diffKeys as $value) {
$rules[$value] = ['prohibited'];
}

Laravel - Is it possible to use a validation rule on a group of attributes?

I'm using Laravel rules and I want to make a validation which requires multiple attributes.
For example, I want a rule to check that the quantity requested doesn't exceed the available stock for the given product. So, something like
public function rule() {
return [
'quantity produyctId' => "checkQty"
}
I would prefer to solve it using rules but other methods are also acceptable.
You can create a custom validation from extending the validation.
In AppServiceProvider class
Validator::extend('quantity_validity', function ($attribute, $value, $parameters, $validator) {
$productId = $parameters[0];
$quantity = $value;
// you can do whatever with these,
// and finally return true or false according to your desire.
});
In Validation
public function rule() {
return [
'quantity' => "quantity_validity:{$productId}"
]
}

How to pass variable to Laravel Validator Rule

The Issue
Consider this:
protected $rules = array(
'mobile_number' => 'phone:AUTO,mobile,:country_code'
);
In the above example, the value of country_code needs to change dependant on a variable defined prior to the validation taking place.
With this in mind, is it possible to pass a variable into a Laravel Validation rule?
Please bear in mind, this is how I call my validation:
if(!$this->some_validator->with($data)->passes()){
// Get the validation errors and throw the exception
$error_info = $this->some_validator->formatErrorMessages();
throw new ExampleException(ExampleExceptionType::$VALIDATION_ERROR,$error_info);
}
You are allowed to create custom validation rules.
It looks like this:
Validator::extend('country_code', function($attribute, $value, $parameters, $validator) {
if ($value === 'US') {
return false;
}
return true;
});
And then just use it like this:
protected $rules = array(
'mobile_number' => 'phone:AUTO,mobile|country_code'
);
After re-reading your message I realized that you meant a bit different thing. But take a look at the $parameters argument.
And read documentation. It's quite well covered there https://laravel.com/docs/5.3/validation#custom-validation-rules
With the new way of doing this, if you extend a new rule and you have a variable that you want to pass to the closure, you can do it like this:
Validator::extend(nameOfTheRule, function ($attribute, $value, $parameters, $validator) use ($priorVariable) {
//code
}

Laravel Validation: How to access rules of an attribute in customized validation

In below rules, I have my custom validation customRule: *date*
$rules = [
'my_date' => 'required|date_format: Y-m-d|customRule: someDate',
];
Inside my custom validation rules extension, I need to access the date_format attribute of the rule:
Validator::extend('customRule', function($attribute, $value, $parameters) {
$format = $attribute->getRules()['date_format']; // I need something like this
return $format == 'Y-m-d';
});
How can I get the rule value of certain attribute on an extended validator?
You can't access other rules. Validators are to be independent units - the only data they should use is:
value of field being validated
values passed to this validation rule as parameters
values of other attributes of object being validated
It seems that what you need is a custom validator that would wrap what is date_format and customRule doing:
Validator::extend('custom_date_format', function($attribute, $value, $parameters) {
$format = $parameters[0];
$someDate = $parameters[1];
$validator = Validator::make(['value' => $value], ['value' => 'date_format:' . $format]);
//validate dateformat
if ($validator->fails()) {
return false;
}
//validate custom rule using $format and $someDate and return true if passes
});
Once you have it, you can use it like that:
$rules = [
'my_date' => 'required|custom_date_format:Y-m-d,someDate',
];

Laravel: custom multidimensional array validation

I've a form which user will insert many records at a time. Each record will have an id, a start date and an end date. To process the input data, I'm looking for the best way to validate all these things.
I'll have to require at least one record
For each inputed record, id should exists at another table, date start and end date should be valid dates and end date should be older than start date
So I need some sort of multidimensional array validation here... Is there any custom validation plugin/code already coded for that?
I've tried to extend Laravel validation but I couldn't get even close to what I'd like...
What I've tried:
app/services/validators/LearningPathValidator.php (I'm using laravel-extended-validator)
<?php
use Crhayes\Validation\ContextualValidator;
class LearningPathValidator extends ContextualValidator
{
protected $rules = [
'default' => [
'name' => 'required|max:96',
'courses' => 'required|multi_array:course_id=required;exists:courses,date_start=required;date_format:d/m/Y,date_end=required;date_format:d/m/Y'
],
];
}
app/validations.php (Here I'm extending Illuminate\Validation\Validator class)
<?php
class AppValidator extends Illuminate\Validation\Validator
{
protected function validateMultiArray($attribute, $value, $parameters)
{
if (!is_array($value)) {
return false;
}
foreach ($parameters as $parameter) {
list($_attribute, $rules) = $this->parseRule(
str_replace(['=', ';'], [':', ','], $parameter));
foreach ($rules as $rule) {
foreach (array_keys(Input::get($attribute)) as $idx){
$this->validate(sprintf('%s.%d.%s', $attribute, $idx,
snake_case($_attribute)), $rule);
}
}
}
return count($this->messages->all()) === 0;
}
}
My start/global.php: (Here I extend Illuminate\Validation\Validator with AppValidator)
// ...
Validator::resolver(function($translator, $data, $rules, $messages) {
return new AppValidator($translator, $data, $rules, $messages);
});
// ...
My models are using courses[$index][course_id], courses[$index][date_start] and courses[$index][date_end] as field names.
Actually I can't require at least one record as I said before and I can't assure end date will be older than start date. Any suggestions to rewrite what I've coded? Thank you in advance!
I created a package to do just this as I encountered the same problem with data from AngularJS.
https://github.com/lakedawson/vocal

Categories