I am developing a Laravel application. What I am doing in my application is that I am trying to override the custom validation rule message.
I have validation rules like this in the request class:
[
'name'=> [ 'required' ],
'age' => [ 'required', new OverAge() ],
];
Normally, we override the error message of the rules like this:
return [
'title.required' => 'A title is required',
'body.required' => 'A message is required',
];
But how can I do that to the custom validation rule class?
You cannot simply overwrite it with the custom messages of the request. If you take a look at the Validator class:
/**
* Validate an attribute using a custom rule object.
*
* #param string $attribute
* #param mixed $value
* #param \Illuminate\Contracts\Validation\Rule $rule
* #return void
*/
protected function validateUsingCustomRule($attribute, $value, $rule)
{
if (! $rule->passes($attribute, $value)) {
$this->failedRules[$attribute][get_class($rule)] = [];
$this->messages->add($attribute, $this->makeReplacements(
$rule->message(), $attribute, get_class($rule), []
));
}
}
As you can see, it's simply adding the $rule->message() directly to the message bag.
However, you can add a parameter for the message in your custom rule's class:
public function __construct(string $message = null)
{
$this->message = $message;
}
Then in your message function:
public function message()
{
return $this->message ?: 'Default message';
}
And finally in your rules:
'age' => ['required', new OverAge('Overwritten message')];
Working with Laravel 9, overriding the message is supported, but you must supply the FQDN for the validation class. So your OverAge custom message may look like this:
return [
'age.required' => 'An age is required',
'age.App\\Rules\\OverAge' => 'The age must be less than 65',
];
You may also want to support place-holders for the message, so the 65 can be replaced by something like :max-age. Many ways to do that are published, none of which look particularly clean.
Related
I have the following working validation rule which checks to make sure each recipient,cc,bcc list of emails contain valid email addresses:
return [
'recipients.*' => 'email',
'cc.*' => 'email',
'bcc.*' => 'email',
];
I need to also be able to allow the string ###EMAIL### as well as email validation for each of these rules and struggling to create a custom validation rule in Laravel 5.8 (it can't be upgraded at this time).
Any idea how to go about this? If it were a higher version of Laravel I was thinking something like (not tested) to give you an idea of what I'm trying to do:
return [
'recipients.*' => 'exclude_if:recipients.*,###EMAIL###|email',
'cc.*' => 'exclude_if:recipients.*,###EMAIL###|email',
'bcc.*' => 'exclude_if:recipients.*,###EMAIL###|email',
];
In 5.8 you can create Custom Rule Object Let's see how we can actually make it work.
Create your rule with php artisan make:rule EmailRule
Make it to look like this
<?php
namespace App\Rules;
use Illuminate\Contracts\Validation\Rule;
class EmailRule implements Rule
{
/**
* Determine if the validation rule passes.
*
* #param string $attribute
* #param mixed $value
* #return bool
*/
public function passes($attribute, $value)
{
if ($value === '###EMAIL###' or filter_var($value, FILTER_VALIDATE_EMAIL)) {
return true;
}
return false;
}
/**
* Get the validation error message.
*
* #return string
*/
public function message()
{
return 'The :attribute must be valid email or ###EMAIL###.';
}
}
Include in your rules so it looks like
return [
'recipients.*' => [new EmailRule()],
'cc.*' => [new EmailRule()],
'bcc.*' => [new EmailRule()],
];
Write a test (optional)
I have a validation rule as shown below. I am using exists to ensure that the contract is unique. The issue now is that contract numbers are stored with spaces in the database so this validation is not working for those cases (for example it will say the contract number does not exist which is due to the space before the number). To solve this, I want to do trim(contract_number). Please how can I apply the trim function to the contract_number below?
public function rules()
{
return [
'tel' => 'required|max:25',
'contract' => 'required|digits:9|exists:accounts,contract_number',
'nom' => 'required|max:255',
'language' => 'required|max:2',
'g-recaptcha-response' => 'required|captcha',
];
}
From the Laravel docs, you can use withValidator method on Custom Form Request to add any further validations on the request. Remove the rule exists:accounts,trim(contract_number) from the list, and try custom rule with after hook.
/**
* Configure the validator instance.
*
* #param \Illuminate\Validation\Validator $validator
* #return void
*/
public function withValidator($validator)
{
$validator->after(function ($validator) {
if (!\DB::table('accounts')->whereRaw('TRIM(contract_number) = ?', [request('contract_number')])->exists()) {
$validator->errors()->add('contract_number', 'Contract number does not exists');
}
});
}
I'd like to validate a form using both custom messages and attributes. Instead of name: The name may not be greater than 20 characters. the user should see Name: Please use fewer characters., for example.
I'm using AJAX and both keys and values of the response.data.errors object that Laravel returns. Im using Laravel 5.7.
This is a simplified version of the validator function in my RegisterController.
protected function validator(array $data)
{
// Nice attribute names
$attributes = [
'name' => 'Name',
// ...
];
// Custom messages
$messages = [
'max' => 'Please use fewer characters.'
// ...
];
// Rules
$rules = [
'name'=> 'required|max:20',
// ...
];
// Working for messages, but not for attribute names
$validator = Validator::make($data, $rules, $messages, $attributes);
// Also not working
// $validator->setAttributeNames($attributes);
return $validator;
}
When there's a validation error, the user get's a message like name: Please use fewer characters.. That means the message from my custom array is displayed, but the default attribute name is used. What's wrong here?
Attributes do not replace key names, they are used to change the appearance of a key within a message - i.e The Name field is required - to achieve what you're trying to do in your question you'll need to create a new data array.
protected function validator(array $data)
{
$data = [
'Name' => $data['name'] ?? '',
// ...
];
// ...
Validator::make($data, $rules, $messages);
}
use Laravel Form Request, scroll down to Customizing The Error Messages section. Check out the below sample code.
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rule;
class UserRegistrationForm 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()
{
return [
'name' => 'required|max:20',
];
}
/**
* Get the error messages for the defined validation rules.
*
* #return array
*/
public function messages()
{
return [
'name.max' => 'Please use less characters'
];
}
}
In controller
public function register(UserRegistrationForm $request)
{
// do saving here
}
That is coming from validation.php located in resources/Lang/xx/
EDIT :
You'll have to use
$messages = [
'name.max' => 'Your sentence here',
];
I have hashed user name in my table. How I can use this validation method for hashed values:
'name' => 'required|unique:users'
Example request with user name: John
Exist user name example on table: RndqMUU5ZUJnQ2JhWjZvNUh5ZGp2UT09
I think first I must hash input value from request and after validate am I right? Where I can hash and validate this values?
You can use the check method of the Hash facade, from the docs:
use Illuminate\Support\Facades\Hash;
// some code
if (Hash::check('plain-text', $hashedElement)) {
// The elements match...
}
Now, you can use this in a Custom Validation Rule:
1. Creating Rule class
php artisan make:rule HashedNameCheck
2. Customize class
app\Rules\HashedNameCheck.php
<?php
namespace App\Rules;
use Illuminate\Contracts\Validation\Rule;
use Illuminate\Support\Facades\Hash; // <-- notice.
class HashedNameCheck implements Rule
{
/**
* Determine if the validation rule passes.
*
* #param string $attribute
* #param mixed $value
* #return bool
*/
public function passes($attribute, $value)
{
// here you get the hashed name stored in your database (?)
$hashedName = App\User::find(1)->name;
// next, you compare this with the received value.
return Hash::check($value, $hashedName);
}
/**
* Get the validation error message.
*
* #return string
*/
public function message()
{
return 'The :attribute does not match with the stored value.';
}
}
3. Apply the rule.
Use this in your controller:
$request->validate([
// some other validation rules..
'name' => ['required', 'unique:users', new HashedNameCheck],
]);
or in your custom Form Request class:
public function rules()
{
return [
// some other validation rules..
'name' => ['required','unique:users', new HashedNameCheck],
];
}
As far as I know there's no built-in validation rule that first hashes the value.
You can always write a custom rule:
$rules = [
'name' => [
'required',
function($attribute, $value, $fail) {
if (User::find(Hash::make($value))) {
return $fail('The username already exists');
}
},
];
You can also move this rule outside if you use it often, e.g. you can add it in your service provider:
public function boot() {
Validator::extend('uniqueHashedUser', function ($attribute, $value, $parameters, $validator) {
if (User::find(Hash::make($value))) {
return false;
}
return true;
});
}
Then you can just use it as:
$rules = [ "name" => 'required|uniqueHashedUser' ];
You can pass a closure to validation and then you can check hashed value.
$validator = Validator::make($request->all(), [
'name' => [
'required',
'max:255',
function($attribute, $value, $fail) {
if (Hash::check($attribute) === $value) {
return $fail($attribute.' is invalid.');
}
},
],
]);
Hi all here something to consider on setting up you storage driver
The Local Storage is not Public altho you can symlink to get access to it this is ot recommended for public items like avatars
The Public driver: will work best for public items
Take note and this is important:
Your APP_URL in env must be set to the url you are using, so if you have something like mysite.test you must have this as your APP_URL
Hope this help anyone
I want to check if a form input 'departement' is filled only if two 'villes' have the same name.
within controller this code wokds perfectly :
$rules=[ 'nom' => 'required', 'ville'=> 'required|exists:villes,nom'];
$messages = [
'depart.required' => 'Deux villes portent le même nom, preciser le
département'];
$validator = Validator::make($request->All(), $rules,$messages);
$validator->sometimes('depart_id', 'required|exists:departs,id', function
($input) {
return Ville::where('nom',$input->ville)->count()>1;
});
if ($validator->fails()) {
return redirect('admin/etab/create')
->withErrors($validator)
->withInput();
}
I put the same code in a Form Request class:
public function rules()
{
$rules=[ 'nom' => 'required', 'ville'=> 'required|exists:villes,nom'];
$messages = [
'depart.required' => 'Deux villes portent le même nom, preciser le
département',
];
$validator = Validator::make($this->All(), $rules,$messages);
$validator->sometimes('depart_id', 'required|exists:departs,id', function
($input) {
return Ville::where('nom',$input->ville)->count()>1;
});
return $validator;
}
I get "Type error: Argument 2 passed to Illuminate\Validation\Factory::make() must be of the type array, object given," I think error message is inadequate but I cannot find why this way does not work
Thanks ......
You can check out the FormRequest class in vendor/laravel/framework/src/Illuminate/Foundation/Http/FormRequest.php and check what it does.
It contains these 2 method at the top:
/**
* Get the validator instance for the request.
*
* #return \Illuminate\Contracts\Validation\Validator
*/
protected function getValidatorInstance()
{
$factory = $this->container->make(ValidationFactory::class);
if (method_exists($this, 'validator')) {
$validator = $this->container->call([$this, 'validator'], compact('factory'));
} else {
$validator = $this->createDefaultValidator($factory);
}
if (method_exists($this, 'withValidator')) {
$this->withValidator($validator);
}
return $validator;
}
/**
* Create the default validator instance.
*
* #param \Illuminate\Contracts\Validation\Factory $factory
* #return \Illuminate\Contracts\Validation\Validator
*/
protected function createDefaultValidator(ValidationFactory $factory)
{
return $factory->make(
$this->validationData(), $this->container->call([$this, 'rules']),
$this->messages(), $this->attributes()
);
}
So you can basically provide a validator method in your own FormRequest class to create a custom Validator object, that method will get the ValidatorFactory as param.
In your case you would not need to do this, because you just want to append the sometimes rule to a default validator. Looking at the code above, it checks for the existence of the withValidator method, if it exists, it is called:
if (method_exists($this, 'withValidator')) {
$this->withValidator($validator);
}
You could create the FormRequest, make sure the rules, messages and authorize methods are properly used, e.g. rules and messages return arrays and authorize returns a bool.
Then create a withValidator method in which you attach the sometimes rule to the Validator.
/**
* Do foo with Validator
*
* #param \Illuminate\Contracts\Validation\Validator $validator
* #return void
*/
public function withValidator(Validator $validator)
{
$validator->sometimes('depart_id', 'required|exists:departs,id', function {
return Ville::where('nom', $this->input('ville'))->count() > 1;
});
}
This way sometimes is attached to your validator before the validation is performed.
You don't put all the validation logic in the rules method like that. Only the rule definitions go there. All you need is this:
public function rules()
{
return [
'nom' => 'required',
'ville'=> 'required|exists:villes,nom',
];
}
Laravel will handle the validation from there on out. You don't need to manually create a Validator class when using FormRequests.
Customizing the message involves creating a messages method within the class like so:
public function messages()
{
return [
'depart.required' => 'Deux villes portent le même nom, preciser le départemen'
];
}
Update
As for the sometimes rule, I'd suggest creating a Rule object and customizing how you need to check for 2 vills having the same name.
Rule classes