Laravel custom rule's custom validation error message - laravel

i have a custom rule which i made using rule objects and its working fine except for one thing, that it doesn't pick up the custom validation message i created for it in the component and instead picks whatever it is assigned to it in the validation.php file or the equivalent translation of it from the translated validation.php file. other non-custom rules are working as expected with the custom messages for the same field.
the component:
public function rules()
{
return[
'topic' => ['required', 'string', 'max:250', 'min:5', new Profane],
'name' => ['required', 'string', 'max:250'],
'email' => ['required', 'email:rfc,dns', 'max:250']
];
}
protected $messages = [
'topic.required' => 'some message',
'topic.max' => 'some message',
'topic.min' => 'some message',
--> 'topic.profane' => 'some message',
'name.required' => 'some message',
'name.max' => 'some message.',
'email.email' => 'some message',
'email.required' => 'some message',
];
the rule object:
public function passes($attribute, $value)
{
$profane = ProfaneWord::all();
$words = $profane->pluck('word');
foreach ($words as $word)
{
if (stripos($value, $word) !== false) return false;
}
return true;
}
/**
* Get the validation error message.
*
* #return string
*/
public function message()
{
return trans('validation.profane');
}

This does not currently seem possible when using custom rules. The problem in the source the message is only ever retrieved from the message() method. However since the validation rule is your own class you can always change it:
private $message;
public __construct($message = null) {
$this->message = $message;
}
public function passes($attribute, $value)
{
$profane = ProfaneWord::all();
$words = $profane->pluck('word');
foreach ($words as $word)
{
if (stripos($value, $word) !== false) return false;
}
return true;
}
/**
* Get the validation error message.
*
* #return string
*/
public function message()
{
return $this->message??trans('validation.profane');
}

As suggested by the comment from #apokryfos and tested on Laravel 9 (v9.30.1) custom validation rule messages can be overridden with the full class name. Example below is from FormRequest usage.
public function messages(): array
{
return [
'topic.required' => 'some message',
// ... other rules
'topic.'.Profane::class => 'The :attribute can not contain ...',
];
}

Related

Validate if input is url or email in Laravel 9

I am trying to validate a field but I am not able to do it.
I have this Request Validator
public function rules()
{
return [
...
'input' => 'required|url|email',
];
}
What I want to do is to validate if the input is url or email, can be one of them, what can I do? Here I am validating both cases, but the input can be one of them. Thanks.
public function rules(): array
{
return [
'input' => ['required', function ($field, $value) {
$data = [$field => $value];
$emailValidator = Validator::make($data, [$field => 'email']);
$urlValidator = Validator::make($data, [$field => 'url']);
if ($urlValidator->passes() || $emailValidator->passes()) {
return true;
}
$this->validator->getMessageBag()->add($field, $field . ' must be a valid email address or a valid url');
return false;
}],
];
}

Validation in Laravel. How to transfer the rule and message to the controller?

I am having problems displaying custom error messages.
I received a training project that had the following code:
class StoreProject extends FormRequest
{
public function authorize()
{
return true;
}
public function rules()
{
return [
'name' => 'required|unique:projects,name|max:255',
'website' => 'url',
];
}
public function messages()
{
return [
'name' => 'Це імʼя вже використовується',
'website' => 'Будь-ласка введіть адресу вашого сайту вірно http://...'
];
}
}
I added the function message( ) myself.
This is the controller code:
public function store(StoreProject $request)
{
$project = new Project($request->except('project_image'));
$project->owner_id = Auth::user()->id;
$project->status_id = StatusProject::UNCONFIRMED;
//send email to moderator and accountant for the moderation
if( $project->save() ) {
$this->dispatch(new ConfirmNewProject($project));
}
// load image from cropie serves
if ($request->has('project_image')) {
$file = self::croppie($request->input("project_image"));
$project->uploadImage($file, 1);
}
return redirect()->route('projects.show', [$project->id]);
}
I tried various methods: withErrors([]) and this method:
'custom' => [
'attribute-name' => [
'rule-name' => 'custom-message',
'name' => 'Це імʼя вже використовується',
'website' => 'Будь-ласка введіть адресу вашого сайту вірно http://...'
],
]
but when checking, I get the key value, not the text of the error message
Errors:
validation.unique
validation.url
How to transfer the rule and message to the controller?
Try to modify the messages() function like that one:
public function messages()
{
return [
'name.required' => 'Name required message',
'name.unique' => 'Name unique message',
'name.max' => 'Name max message',
'website.url' => 'Будь-ласка введіть адресу вашого сайту вірно http://...'
];
}

Laravel custom error message from inside custom validator function using $validator->errors->add()

I have custom validation rule appointment_status. I am performing various test cases on it and decide what error message is best and throwback. it will be different for every case. I want the $validator->errors()->add('status', __('Invalid status for an appointment in past') to set the error message and it's adding. but it's not returning back to the controller. I can't access this message anywhere. it shows only the status.appointment_status one which is set in messages() function.
Custom Request class:
namespace Modules\ShopManager\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class AppointmentsRequest extends FormRequest
{
public function __construct()
{
\Validator::extend('appointment_status', 'Modules\ShopManager\Validators\CustomValidator#appointmentStatus');
}
/**
* Get the validation rules that apply to the request.
*
* #return array
*/
public function rules()
{
$rules = [
'services' => 'required',
'sdate' => 'required|date_format:m-d-Y|time_range:sTime,edate,eTime,timezone',
'edate' => 'required|date_format:m-d-Y|workinghours_range:sdate,sTime,edate,eTime,timezone',
'sTime' => 'required|date_format:h:i a',
'eTime' => 'required|date_format:h:i a',
'cname' => 'required',
'cphone' => 'required_without:cemail',
'cemail' => 'nullable|required_without:cphone|email',
'timezone' => 'required',
'status' => 'required|appointment_status:sdate,sTime,edate,eTime,timezone',
];
return $rules;
}
public function messages()
{
return [
'status.appointment_status' => 'Invalid status'
];
}
public function attributes()
{
return [
'services' => 'Services',
'date' => 'Date',
'sTime' => 'Start Time',
'eTime' => 'End Time',
'cname' => 'Customer name',
'cphone' => 'Customer phone',
'cemail' => 'Customer email',
'internal_note' => 'Internal note',
'status' => 'Status',
];
}
/**
* Determine if the user is authorized to make this request.
*
* #return bool
*/
public function authorize()
{
return true;
}
}
The custom validator function:
public function appointmentStatus($attribute, $value, $parameters, $validator)
{
$dateTimeOperations = new DateTimeOperations;
$sdate = array_get($validator->getData(), $parameters[0]);
$startTime = array_get($validator->getData(), $parameters[1]);
$edate = array_get($validator->getData(), $parameters[2]);
$endTime = array_get($validator->getData(), $parameters[3]);
$timezone = array_get($validator->getData(), $parameters[4]);
$now = $dateTimeOperations->getNow($timezone);
$start = $dateTimeOperations->getTimestamp($sdate, $startTime, $timezone);
$end = $dateTimeOperations->getTimestamp($edate, $endTime, $timezone);
switch ($value) {
case constants('appointments.status.pendig'):
$start->lessThan($now)
? $validator->errors()->add('status', __('Invalid status for an appointment in past'))
: '';
}
return $validator->errors()->any();
}
Adding an error just to the field without specifying the rule I don't think will work, that's why the message from the validation request takes precedence.
So change it to this:
$validator->errors()->add('status.appointment_status', __('Invalid status for an appointment in past'))
And also in your case do you maybe have a typo: pendig to be pending?
You have to create custom validator rules and add below code inside your rule wherever required, See example below:
$validator->after(function ($validator) {
if ($this->somethingElseIsInvalid()) {
$validator->errors()->add('field', 'Something is wrong with this field!');
}
});

Error implementing Custom Validation in Laravel

I am trying to add an extra validation rule that checks to ensure that a username is a word. I created a new rule (SingleWord) like so:
public function passes($attribute, $value)
{
$dd = strpos(trim($value), ' ');
if($dd !== false){
return false;
}
}
/**
* Get the validation error message.
*
* #return string
*/
public function message()
{
return 'Username must be one word';
}
I now added this rule to the validator like so:
$validator = Validator::make($data, [
'name' => 'required|string|max:255|unique:merchants',
'email' => 'required|string|email|max:255|unique:merchants',
'password' => 'required|string|min:6',
'username' => 'required|string|unique:merchants',
'username' => [new SingleWord]
],[
'name.required' => "Company name is required",
]);
return $validator;
But the validator returns the error message even when I enter one word. Please what might be wrong here?
You left out the affirmative case. Try this:
public function passes($attribute, $value)
{
$dd = strpos(trim($value), ' ');
if($dd !== false){
return false;
}
return true;
}
/**
* Get the validation error message.
*
* #return string
*/
public function message()
{
return 'Username must be one word';
}
Without knowing your error message, I will suggest couple of changes, first in your validation you can change your passes method to this:
public function passes($attribute, $value)
{
return !strpos(trim($value), ' ');
}
and then your validation, you can use only one key like this:
'username' => ['required', 'string', 'unique:merchants' , new SingleWord]

How to write custom validation rule in controller Laravel?

I have default validation rule in controller Laravel:
$validator = Validator::make($request->all(), [
'email' => 'required|email',
'phone' => 'required|numeric',
'code' => 'required|string|min:3|max:4',
'timezone' => 'required|numeric',
'country' => 'required|integer',
'agreement' => 'accepted'
]);
I tried this, but dont know how to transfer some parameters inside function:
public function boot()
{
Validator::extend('phone_unique', function($attribute, $value, $parameters) {
return substr($value, 0, 3) == '+44';
});
}
How can I extent this validation by my own rule? For example I need to validate concatination of inputs:
$phone = $request->code.' '.$request->phone
After check if $phone are exists in database
I want to use this method:
> $validator->sometimes('phone', 'required|alpha_dash|max:25', function
> ($input) {
> if ((Auth::user()->phone == $input->phone)) {
> return false;
>
> } else {
>
> $t = User::where("phone", $input->phone)->get();
> return ($t->count() > 0) ? false : false;
>
> }
> });
It does not work under all conditions (True, False) inside.
I added new validation nickname_unique:
$validator = Validator::make($request->all(), [
'email' => 'required|email',
'code' => 'required|string|min:3|max:4',
'phone' => 'required|phone_unique',
'timezone' => 'required|numeric',
'country' => 'required|integer',
'nickname' => 'required|alpha_dash|max:25',
'agreement' => 'accepted'
], [
'phone_unique' => 'Phone already exists!',
'nickname_unique' => 'Nickname is busy!',
]);
It does not work, even not call validation rule below previos:
Validator::extend('nickname_unique', function ($attribute, $value, $parameters, $validator) {
dd("Here");
});
You can define your custom validator inside AppServiceProvider like this:
<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Facades\Validator;
class AppServiceProvider extends ServiceProvider
{
/**
* Bootstrap any application services.
*
* #return void
*/
public function boot()
{
Validator::extend('phone_unique', function ($attribute, $value, $parameters, $validator) {
$inputs = $validator->getData();
$code = $inputs['code'];
$phone = $inputs['phone'];
$concatenated_number = $code . ' ' . $phone;
$except_id = (!empty($parameters)) ? head($parameters) : null;
$query = User::where('phone', $concatenated_number);
if(!empty($except_id)) {
$query->where('id', '<>', $except);
}
return $query->exists();
});
/**
* Register the service provider.
*
* #return void
*/
public function register()
{
//
}
}
You can get all the inputs passed to the validator, by accessing $validator property - getData()
You can just add an extra parameter to your rules array after your custom validation rule (just after the colon) like this:
'phone' => 'required|phone_unique:1',
Pass the id to be ignored while checking entries into the db
The custom validator Closure receives four arguments: the name of the $attribute being validated, the $value of the attribute, an array of $parameters passed to the rule, and the Validator instance.
Now you can call the validator like this:
$validator = Validator::make($request->all(), [
'email' => 'required|email',
'code' => 'required|string|min:3|max:4',
'phone' => 'required|phone_unique:1',
'timezone' => 'required|numeric',
'country' => 'required|integer',
'agreement' => 'accepted'
], [
'phone_unique' => 'Phone already exists!', // <---- pass a message for your custom validator
]);
See more about Custom Validation Rules.
I am writing this answer because I believe bunch of people are looking for some good answer for this topic. So I decided to share my code that I am using for booking site, where I want to check that IS NOT arrival_date > departure_date.
My version of Laravel is 5.3.30
public function postSolitudeStepTwo(Request $request)
{
$rules = [
'arrival_date' => 'required|date',
'departure_date' => 'required|departure_date_check',
'occasional_accompaniment_requested' => 'required|integer',
'accommodation' => 'required|integer',
'are_you_visiting_director' => 'required|integer',
];
if ($request->input('are_you_visiting_director') == 1) {
$rules['time_in_lieu'] = 'required|integer';
}
$messages = [
'departure_date_check' => 'Departure date can\'t be smaller then Arrival date.Please check your dates.'
];
$validation = validator(
$request->toArray(),
$rules,
$messages
);
//If validation fail send back the Input with errors
if($validation->fails()) {
//withInput keep the users info
return redirect()->back()->withInput()->withErrors($validation->messages());
} else {
MySession::setSessionData('arrival_date', $request);
MySession::setSessionData('departure_date', $request);
MySession::setSessionData('occasional_accompaniment_requested', $request);
MySession::setSessionData('accommodation', $request);
MySession::setSessionData('are_you_visiting_director', $request);
MySession::setSessionData('time_in_lieu', $request);
MySession::setSessionData('comment_solitude_step2_1', $request);
//return $request->session()->all();
return redirect("/getSolitudeStepThree");
}
}
My controller is StepController and there I have declared a method as you can see called postSolitudeStepTwo. I declare the rules and on departure date notice that for the rule we have required|departure_date_check. That will be the name of the method in
app/Providers/AppServiceProvider.php
The code there looks like this:
public function boot()
{
Validator::extend('departure_date_check', function ($attribute, $value, $parameters, $validator) {
$inputs = $validator->getData();
$arrivalDate = $inputs['arrival_date'];
$departureDate = $inputs['departure_date'];
$result = true;
if ($arrivalDate > $departureDate) {
$result = false;
}
return $result;
});
}
As the Laravel documentation 5.3 Custom validation rules we need to extend the Validator facade, the signature of that method has to be:
Validator::extend(name_of_the_function, function ($attribute, $value, $parameters, $validator) {
And I believe the rest is clear.
Hope it will help somebody.
$messsages = array(
'email.required'=>'Email is Required',
'phone.required'=>'Phone number is Required',
);
$rules = array(
'email' => 'required',
'phone' => 'required',
);
$validator = Validator::make(Input::all(), $rules,$messsages);
if ($validator->fails()):
$this->throwValidationException($request, $validator);
endif;

Resources