Laravel(5.3.24+) Validation: Validate 2 columns - validation

Using the new style of laravel validation seen here: https://laravel-news.com/unique-and-exists-validation
I would like the name column to be required & only unique for a competition.
Using the example table below,
id 3: The validation should stop this from happening, because "a name already exist for that competition".

Custom rule
The best solution would be to create a custom rule for this, that accepts the field with the corresponding competition_id as a parameter.
Something like
//Calling the custom rule like this
['name' => 'required|validateName:yourCompetitionIdFormFieldHere'];
Declaring the custom validation function in your service provider like this
Validator::extend('validateName', function ($attribute, $value, $parameters, $validator) {
$competitionId = ($validator->data, $parameters[0]);
//Now check if the $value is already set for the specific competition_id by using normal database queries and comparison
return count(Team::where("comeptition_id", "=", $competitionId)->whereName($value)->get()) == 0
});
What does it
The custom validation rule receives the data of the field you give with the function (in the code above it's yourCompetitionIdFormFieldHere), so it knows which league/competition_id the user chose. With this information, you now can check if there is already a similar ame for the entered id.

Related

Accessor overrides custom validation rule

I have created a custom rule to make sure the number is of two decimal places. The database has numbers stored as decimal(19,4). To display the number with only two decimal places, I also have put an accessor in place. Now the problem is that the accessor overrides the custom validation rule.
the relevant part from custom validation rule
public function passes($attribute, $value)
{
$precision = Config::get('accounting.decimal');
return preg_match('/^\d+(\.\d{1,'.$precision.'})?$/',$value);
}
the accessor in model
public function getRateAttribute($value)
{
$precision = Config::get('accounting.decimal');
return round($value, $precision);
}
the validation rule in the livewire component
public function rules()
{
return [
'editing.rate' => ['required', new NumberFormat()],
];
}
Desired behavior:
Whenever a user enters a number with more than two decimal places, it should throw the customer validation error.
Every time the field is used anywhere else to display, it should show the number with two decimal places. For eg: 78.9800 should be displayed as 78.98.
any ideas why this is not happening?
Edit:
Did a little more testing and this seems to be a laravel livewire issue. I am binding data directly to the model property. Because of that, when I edit the field, the accessor is immediately called before validation occurs.
Workaround:
I canceled the data binding to the model property. This solved the issue.
Still is there a better way to use laravel-livewire data binding to model property and use an accssor that would not override the validation rule.

Laravel unique validation must include appended text when validating

I am using the Laravel tenancy package in which there is a domains table in my database and each value in the domain column within this table is appended with .test.co.uk.
When the user enters the URL in the form, they are presented with an input element (shown above) in which they enter a URL/Domain but the .test.co.uk is already appended so the only thing they need to enter is the text that goes before that, e.g. they would enter johnsmith and in the domain column it would store johnsmith.test.co.uk. The problem I have is that I need the validation on this column to be unique but also include the .test.co.uk when performing the validation so that it looks at the value stored in the table because if a user enters johnsmith and there is currently a record in the domains table where the value is johnsmith.test.co.uk then the validation would pass but I need the validation to fail in this scenario. I am currently using a Request class which is extending the FormRequest class and have this:
public function rules()
{
return [
'url' => 'required|string|unique:domains,domain',
];
}
I have also tried a rule object but I don't think a rule object is the correct solution to this problem. Is there a convenient "Laravel" way of doing this?
In your Request class use prepareForValidation()
Docs: https://laravel.com/docs/7.x/validation#prepare-input-for-validation
protected function prepareForValidation()
{
$this->merge([
'url' => $this->url . '.test.co.uk',
]);
}

How to specify a default value for a field Laravel Nova

I want to set the default value of a resource field to the authenticated user's id. I have a model called Note which has a one to many relationship with Game and User.
User hasMany Note
Game hasMany Note
Note belongsTo User
Note belongsTo Game
In Laravel Nova my fields looks like this for the note
ID::make()->sortable(),
Text::make('Note', 'note')->onlyOnIndex(),
Textarea::make('Note', 'note')->alwaysShow(),
BelongsTo::make('Game', 'game')->hideWhenCreating()->hideWhenUpdating(),
BelongsTo::make('Created By', 'user', 'App\Nova\User')->hideWhenCreating()->hideWhenUpdating(),
DateTime::make('Created At', 'created_at')->hideWhenCreating(),
DateTime::make('Updated At', 'updated_at')->hideWhenCreating(),
Because I am referencing the Note on the Game Nova resource, when I create a Note, the game_id column is populated correctly. But, I want the user_id column to be the value of the authenticated user. It does not seem to work like this, how would I accomplish it?
If I understand correctly from the line BelongsTo::make('Created By', 'user', 'App\Nova\User')->hideWhenCreating()->hideWhenUpdating() you're trying to set a default value for the column without showing the field on the form?
I don't think this is possible in this way. As soon as you use the hide functions the fields aren't rendered and will never be passed along with the request. I tried this, and the user_id field was never sent with the request.
I think there are two ways to do this:
Show the field in the form and set the default value using the metadata (and perhaps making the field read-only for good measure).
BelongsTo::make('Created By', 'user', 'App\Nova\User')->withMeta([
"belongsToId" => auth()->user()->id,
])
See this part of the Nova docs
Or use the Eloquent creating event. The following will go in your Note model.
public static function boot()
{
parent::boot();
static::creating(function($note)
{
$note->user_id = auth()->user()->id;
});
}
Granted, the above method is a bit simple. You'd be better off using proper event listeners.
Sidenote: from an architectural point of view, I'd go with option 2. Setting a default value without getting the end-user involved sounds like a job for the Eloquent model, not for a Nova form.
You can use a method resolveUsing(). An example
<?php
//...
Select::make('My Select', 'my_custom_name')
->options(['a' => 'a', 'b' => 'b', 'c' => 'c'])
->resolveUsing(function ($value, $resource, $attribute) {
// $value = model attribute value
// $attribute = 'my_custom_name'
return 'b';
});

How to combine Laravel validation required_if and required_without_all rules?

I have a situation with a subscription form, which must have different validation rules depending on user selection.
I almost complete this, but I'm stuck in a point which need a combination of rules that I think I can't get with predefined laravel rules.
As shown in the following chart, the point is when a user select invoicing preferences, with options Digital and Printed, if user option is Printed I need at least one physical address, so street address field group OR district address fields group must be mandatory.
Mandatory field unless other field is filled can be achieved by required_without_allrule, so I've trying with no success, a combination of required_if and required_without_allrules, like the following example:
public function rules()
{
return [
...
'invoicing_preferences' => 'required',
'invoicing_email' => 'email|required_if:invoicing_preferences,digital',
'invoicing_street_name' => 'string|required_if:invoicing_preferences,printed|required_without_all:invoicing_district,invoicing_parcel',
'invoicing_street_number' => 'number|required_if:invoicing_preferences,printed|required_without_all:invoicing_district,invoicing_parcel',
'invoicing_street_flat' => 'number|required_if:invoicing_preferences,printed|required_without_all:invoicing_district,invoicing_parcel',
'invoicing_street_dep' => 'alpha_num|required_if:invoicing_preferences,printed|required_without_all:invoicing_district,invoicing_parcel',
'invoicing_district' => 'alpha_num|required_if:invoicing_preferences,printed|required_without_all:invoicing_street_name, invoicing_street_number; invoicing_street_flat,invoicing_street_dep',
'invoicing_parcel' => 'alpha_num|required_if:invoicing_preferences,printed|required_without_all:invoicing_street_name, invoicing_street_number; invoicing_street_flat,invoicing_street_dep',
...
];
}
This combination doesn't work because always results in the required_with_allrule no matter if I've checked digital at the first point.
The rules() method is a method that is expected to return array of rules. Why would I write about such an obvious thing? Well, insert any kind of validation logic inside it, which means that it can also do some evaluation of posted data and gradually build up the returning array.
public function rules()
{
$this; // holds information about request itself with all the data POST-ed
if (something) {
return []; // something is true...
}
return []; // default behaviour (ehm, something is not true)
}
Another similar approach is to use multiple arrays and in the end merge them together (build them up). Which may result in nicer code. Also do not be afraid of using one or two private methods to clean up the code.

Laravel Validator unique value

I am trying to validate an email address in my Customer table. There is a column in the Customer table called Brand where effectively the same email address could be registered to multiple brands for example:
Email Brand
me#mywebsite.com firstsite.com
me#mywebsite.com secondsite.com
When validating the email address I need to check the email is unique in the Customer table for the current Brand. Currently my validation rule looks like this
$rules = array('email' => 'required|email|unique:Customer,Email');
however Brand must appear in there someone to say the email must be unique to the customer for the current brand
Any help much appreciated
You can use the column and value parameter of the unique validation rule to add a condition to the rule.
'email' => 'required|email|unique:Customer,Email,null,null,column,value'
The problem is you need your Brand input in order to create your rule like this :
$rules['email'] = 'required|email|unique:Customer,Email,null,null,Brand,' . Input::get('Brand');
This rule will check if the email is unique where the brand has the given value.
More information here :
http://laravel.com/docs/validation#rule-unique
There is also a package that could help you :
https://github.com/felixkiss/uniquewith-validator
$rules = array('email' => 'required|email|unique:Customer,Email'); this will query your model for finding the entered email if the email is already present then an error message is raised. so this will not work with your app design flow. you have to run sql query to find out if the email is present with your current brand if yes return error message if not save the data. something like that
I would go for writing my own custom validation rule. Custom validation rule will be more common solution and you can name it as it suites you best. It can be created using the Validator::extend() and a closure, like this :
Validator::extend('awesome', function($field, $value, $params)
{
return $value == 'awesome';
});
or via validator class. Have a research at this topic, maybe here (scroll to custom rules section) or any other source you find.

Resources