Laravel Custom Request not catching the unique in the rules when creating custom messages - laravel-5.6

I have read the docs like 12 times and I created the following:
class LSDLocationRequest 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 [
'organization_id' => 'required',
'name' => 'required|unique:lsd_locations'
];
}
public function messages()
{
return [
'name.unique:lsd_locations' => 'The LSD name is already used by a LSD Location.',
];
}
}
In the controller I do:
public function store(LSDLocationRequest $request, Organization $organization) {
$request->validated();
// ...
}
When I submit this form the first time with a name of Sample Location, it works. When I do it the second time with the same name, it shows me:
The name has already been taken.
I am 110% sure this is the correct controller action it's coming into. Why is it not showing me my custom message? I have a unique key on the name field in the table. It's ignoring my name.unique:lsd_locations and writing the default message.
The table exists.
The column exists.
There is a unique constraint for name on this table.
Why is it not using my message? Why is it using the default message?

Simply change the message to:
name.unique
So it should be like this:
public function messages()
{
return [
'name.unique' => 'The LSD name is already used by a LSD Location.',
];
}

Related

Laravel - How to dynamically resolve an instance of a model that has different Namespace?

tldr:
How do you dynamically get an instance of a model just by its DB table name?
What you get from the request:
ID of the model
table name of the model (it varies all the time!)
What you don't know:
Namespace of the model
Longer explanation:
I have a reporting system, that users can use to report something. For each reporting, the ID and the table name is sent.
Until now, every model was under the Namespace App\*. However, since my project is too big, I needed to split some code into Modules\*
Here is an example, how the report is saved in the database:
Example:
Request contains rules:
public function rules()
{
return [
'id' => 'required|string',
'type' => 'required|in:users,comments,offer_reviews, ......',
'reason' => 'required|string',
'meta' => 'nullable|array',
'meta.*' => 'string|max:300'
];
}
In the database, we save the data into :
id reportable_type ...
1 App\User ...
4 Modules\Review\OfferReview ...
How would you create an instance of a model dynamically, when you just know the database table name for example offer_reviews?
There is one solution that jumps to my mind, however, I'm not sure if it adds more security issues. What is if the user sends the full namespace + class name? With that, I know directly where to resolve an instance.
Have a look what I'm doing right now
(before I changed to modules)
//In my controller
class ReportController extends Controller
{
/**
* Stores the report in DB.
*/
public function store(StoreReportRequest $request)
{
$model = $request->getModel();
$model->report([
'reason' => $request->reason,
'meta' => $request->meta
], auth()->user());
return response()->json(['status' => 'Submitted'], 201);
}
}
//in StoreReportRequest
/**
* Gets the Model dynamically.
* If not found we throw an error
* #return \App\Model
*/
public function getModel()
{
return get_model($this->type)
->findOrFail(\Hashids::decode($this->id)[0]);
}
//in Helpers
/**
* Gets me the model of a table name.
* #param String $table Has to be the name of a table of Database
* #return Eloquent The model itself
*/
function get_model($table)
{
if (Schema::hasTable(strtolower($table))) {
return resolve('App\\' . Str::studly(Str::singular($table)));
}
throw new TableNotFound;
}
I don't know if there is a better solution, but here you go. My code is looking for a method with namespace when it's not found we are using App\ as the namespace.
Maybe this code helps someone :)
class StoreReportRequest extends FormRequest
{
/**
* Get the validation rules that apply to the request.
*
* #return array
*/
public function rules()
{
return [
'id' => 'required|string',
'type' => 'required|in:mc_messages,profile_tweets,food,users,comments,offer_reviews,user_reviews',
'reason' => 'required|string',
'meta' => 'nullable|array',
'meta.*' => 'string|max:300'
];
}
/**
* Gets the Model dynamically.
* If not found we throw an error
* #return \App\Model
*/
public function getModel()
{
$namespace = $this->getNamespace();
return $this->resolveModel($namespace);
}
protected function getNamespace(): string
{
$method = $this->typeToMethod();
if (method_exists($this, $method)) {
return $this->$method();
}
return 'App\\';
}
protected function typeToMethod(): string
{
return 'get' . \Str::studly(\Str::singular($this->type)) . 'Namespace';
}
protected function resolveModel(string $namespace)
{
return get_model($this->type, $namespace)
->findOrFail(\Hashids::decode($this->id)[0]);
}
protected function getOfferReviewNamespace(): string
{
return 'Modules\Review\Entities\\';
}
protected function getUserReviewNamespace(): string
{
return 'Modules\Review\Entities\\';
}
}

Laravel 5.7: Custom attributes for form validation not working

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

How to validate images array type using rule object with custom message in Laravel

Actually, I tried to create rule object which is able to validate every image type in array of images and not only enough but also, I must to show custom message in override message function in rule object.
<?php
namespace App\Rules;
use Illuminate\Contracts\Validation\Rule;
class ImagesArray 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)
{
return [$attribute => 'mimes:jpeg,jpg,png' ];
here i need to validate these file types.
}
/**
* Get the validation error message.
*
* #return string
*/
public function message()
{
return 'The validation error message.';
here, I need to show my custom messgae.
}
}
You should use Request.
For example, create q request class: php artisan make:request MyRequest.
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class MyRequest 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 [
'image' => 'mimes:jpeg,jpg,png',
];
}
public function messages()
{
return [
'image.mimes' => 'This image is not supported.',
];
}
}
In your controller import class MyRequest and in the method use MyRequest
e.g:
public function store(MyRequest $request)
{ // your code
}
Let me know if that was helpful. Thanks!
When validating arrays or nested parameters, you should use . in your rules access a specific array index. but if you want to apply a rule to every index on that array, you can use .*.
$validator = Validator::make($request->all(), [
'image.*' => 'mimes:jpeg,jpg,png',
], [
'image.*' => 'Invalid file type.',
]);
Or if you're using Request Forms
public function rules(){
return [
'image.*' => 'mimes:jpeg,jpg,png',
];
}
public function mesages(){
return [
'image.*' => 'Invalid file type.',
];
}
For more info, see Laravel's Documentation on Validation Arrays

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

Laravel validation required_if with in:1

I need to use required_id with a in:1 as this field is required to be checked only if another field equal to 4
'affirm_agency' => 'required_if:role,4|in:1',
but when I write it that way I can't pass the validation when it doesn't equal to 1
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class userRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* #return bool
*/
public function authorize()
{
return false;
}
public function rules()
{
return [ 'affirm_agency' => 'required_if:role,4|in:1',];
}
}
I typically handle something like this like so:
/**
* Validation rules
*
* #return array
*/
public function rules() : array
{
$rules = [
// add whatever default rules you always want
];
if (request()->role == 4) {
$rules['affirm_agency'] = 'required|in:1';
}
return $rules;
}
Please see laravel documentation
in:foo,bar,...
The field under validation must be included in the given list of values. Since this rule often requires you to implode an array, the Rule::in method may be used to fluently construct the rule:
use Illuminate\Validation\Rule;
Validator::make($data, [
'zones' => [
'required',
Rule::in(['first-zone', 'second-zone']),
],
]);
You need to update you authorize function as it is returning false now.
public function authorize()
{
return true;
}

Resources