Laravel 5.7: Custom attributes for form validation not working - laravel

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',
];

Related

How to add the trim function to validation rules in Laravel Request?

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');
}
});
}

How to override the message of the custom validation rule in Laravel?

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.

Lumen - validation rules in model?

I am creating a REST API and would like to attach some validation rules to the CREATE and UPDATE endpoints. The validation rules for these two endpoints would be exactly the same, so I would like to be able to specify them in one place only. Coming from a Laravel background I normally create Form Requests, or put the validation rules in the model within a rules() function.
However I think Lumen does validation a bit differently and their documentation suggests to put the validation logic in the router: https://lumen.laravel.com/docs/master/validation
However personally I don't think that is the best place for it and would prefer to put in the model instead. I tried using the rules() function within the model but that doesn't seem to do anything.
My create and update methods look like this:
public function create(Request $request)
{
$product = Product::create($request->all());
return response()->json($product, 201);
}
public function update($id, Request $request)
{
$product = Product::findOrFail($id);
$product->update($request->all());
return response()->json($product, 200);
}
Is it possible for me to put my validation rules within my Product model and have them run automatically?
Here is how I have attempted to do it in my model:
namespace App;
use Illuminate\Database\Eloquent\Model;
class Product extends Model
{
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $fillable = [
'name', 'price', 'description',
];
/**
* The attributes excluded from the model's JSON form.
*
* #var array
*/
protected $hidden = [];
/**
* Set model validation rules.
*
* #return array
*/
public function validate()
{
return [
'name' => 'required',
'price' => 'required',
'description' => 'required',
];
}
}
As you mentioned and in accordance with the Lumen validation documentation (as of 5.8):
Form requests are not supported by Lumen. If you would like to use form requests, you should use the full Laravel framework.
Lumen doesn't have anything similar to the automatic validation provided by Form Requests in Laravel, therefore you will need to perform validation manually.
Product
class Product extends Model
{
/**
* Get model validation rules.
*
* #return array
*/
public static function getValidationRules()
{
return [
'name' => 'required',
'price' => 'required',
'description' => 'required',
];
}
}
The above defines your Product model validation rules as a static methods, the rules should be the same for all your Product objects. You may want to consider scenarios where a field should be unique in your database, providing an argument to the method would be an option.
ProductController
class ProductController extends Controller
{
public function create(Request $request)
{
// Perform your validation
$validatedData = $request->validate(Product::getValidationRules());
// The Product is valid
$product = Product::create($request->all());
return response()->json($product, 201);
}
public function update($id, Request $request)
{
$product = Product::findOrFail($id);
// Perform your validation
$validatedData = $request->validate(Product::getValidationRules());
// The Product is valid
$product->update($request->all());
return response()->json($product, 200);
}
}
Where your validation fails with the above, Lumen will automatically redirect the user back to the previous location and errors flashed to the session (as with Laravel). Obviously you can alter this workflow if you desire.
The same validate method should be available at the controller level as well. So if you want to reuse the rules you could do something like this:
private $product_rules = [
'name' => 'required',
];
public function create(Request $request)
{
$this->validate($request, $this->product_rules);
$product = Product::create($request->all());
return response()->json($product, 201);
}
public function update($id, Request $request)
{
$product = Product::findOrFail($id);
$this->validate($request, $this->product_rules);
$product->update($request->all());
return response()->json($product, 200);
}

How to validate request with hashed value for exist on table column?

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

Laravel 5.5 cannot make validator in a form Request class

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

Resources