Laravel Livewire - Constant expression contains invalid operations in $rules property - php

I tried to define some validation rules in my livewire component to validate some FormData:
protected $rules = [
'website' => 'url|nullable',
'zipcode' => 'regex:/\b\d{5}\b/|nullable',
'founding_year' => 'required|digits:4|integer|min:1700|max:2020',
];
That work's very well until I need to validate against the value of a variable or a dynamic value in general.
E.g.: Changing the max property from hardcoded 2020 to the current year:
protected $rules = [
...
'founding_year' => 'required|digits:4|integer|min:1700|max:'. date('Y'),
];
Unfortunatelly this resolves in an exception:
Symfony\Component\ErrorHandler\Error\FatalError
Constant expression contains invalid operations
Has someone an idea how to fix this?

You can't call functions or methods when declaring the value of a property directly in PHP.
With Livewire, you can specify a rules() method which returns the rule-array instead - this allows you to use functions in the rules. Internally, Livewire will now run the result of that method instead of grabbing the protected $rules array. This means you can still hook into the $this->validate() and $this->validateOnly() methods Livewire ships with.
So instead of defining your protected $rules; attribute, declare the rules() method,
public function rules()
{
return [
'website' => 'url|nullable',
'zipcode' => 'regex:/\b\d{5}\b/|nullable',
'founding_year' => 'required|digits:4|integer|min:1700|max:'.date("Y"),
];
}

Related

laravel model factory override default state

Is it possible to override the defaults of a factory without using states?
I use a tool that generates the factories from my models, but I would like to modify some attributes. I know that I can use
$factory->state(\App\User::class, 'moderator', function ...
but I would like to do it without depending on specifying the state with every model creation. So something like
$factory->state(\App\User::class, 'default', function ...
What you define in the factory is the default behaviour, for example
$factory->define(App\User::class, function(Faker $faker) {
return [
...
'name' => 'Jon Snow',
...
];
});
You can override this default behaviour with a state, for example
$factory->state(App\User::class, 'bad-guy', function (Faker $faker) {
return [
'name' => 'Night King'
]
};
And the last override you can do, is when you want to create that instance, for example
$jonSnow = factory(App\User::class)->create();
$nightKing = factory(App\User::class)->states('bad-guy')->create();
$samTarly = factory(App\User::class)->create([
'name' => 'Sam Tarly'
]);

Yii2 Custom ActionFilter not working with "only" defined as wildcard

I have:
RestModule > TargetController extends BaseController
in BaseController:
public function behaviors()
{
$behaviors['myfilter'] = [
'class' => MyFilter::className(),
'only' => ['rest/target/*'],
];
return $behaviors;
}
but my filter working until "only" is not set or if I set TargetController actions names using "except"
Yii2 versin is 2.0.11.2 on php 5.5 debian8
Since version 2.0.9 action IDs can be specified as wildcards, e.g. site/*. yii2-doc
If you want to attach filter with 'only' property and put IDs as wildcard, e.g. target/*, you should attach it as behavior to module class, not controller.
Try this in your RestModule:
RestModule:
public function behaviors()
{
$behaviors['myfilter'] = [
'class' => MyFilter::className(),
'only' => ['target/*'],
];
return $behaviors;
}

Laravel localization of validation rule 'before' and 'after' with parameter like 'today/tomorrow'

Lets assume I have this rules in my model:
public $rules = [
'a_date' => 'after:today',
'b_date' => 'before:today',
];
And I have this string in my project\resources\lang\en\validation.php:
'after' => 'The :attribute must be a date after :date.',
'before' => 'The :attribute must be a date before :date.',
I translate them into some language in project\resources\lang\some-language\validation.php
'after' => ':attribute *somelanguage* :date.',
'before' => ':attribute *somelanguage* :date.',
But when I hit validation error in my app I see string like that:
*field* *some language* today (for example in Russian:Поле a_date должно быть раньше чем today)
So the question is: How and where to replace this today(and any other predifined words like that) to desired localization?
PS: I could use a custom validation as stated in docs https://laravel.com/docs/5.2/validation#localization but it only aplied to certain fields and I wish it to replace today whenever I use it in any of fields.
Create an array in your lang file (in my case it was title.php):
'time_periods' => [
'yesterday' => 'вчера',
'now' => 'сейчас',
'today' => 'сегодня',
'tomorrow' => 'завтра',
],
And then add the following code in CustomValidator class:
All this code does is replacing parameters in certain rules with values from your lang file array using keys of this array. First it changes english replacer to localized one and then use it to replace the actual placeholder(:date) in validation message.
class CustomValidator extends Validator {
public function replaceBefore($message, $attribute, $rule, $parameters) {
$parameter_translated = str_replace(
array_keys(trans('title.time_periods')),
array_values(trans('title.time_periods')),
$parameters[0]
);
return str_replace(':date', $parameter_translated, $message);
}
// this method does the same but for 'after' rule
public function replaceAfter($message, $attribute, $rule, $parameters) {
$parameter_translated = str_replace(array_keys(trans('title.time_periods')), array_values(trans('title.time_periods')), $parameters[0]);
return str_replace(':date', $parameter_translated, $message);
}
}
If you don't like to have CustomValidator then use this approach (taken from the documentation , see: 'Defining The Error Message' part):
When creating a custom validation rule, you may sometimes need to
define custom place-holder replacements for error messages. You may do
so by creating a custom Validator as described above then making a
call to the replacer method on the Validator facade. You may do this
within the boot method of a service provider:
/**
* Bootstrap any application services.
*
* #return void
*/
public function boot()
{
Validator::extend(...);
Validator::replacer('foo', function($message, $attribute, $rule, $parameters) {
return str_replace(...);
});
}

Hooks in Laravel 5?

I'm creating a package and want hook functionality (the package should inject some extra validation rules when a user updates a field in my app).
I managed to do this using the event system. What I do is pass the $rules variable and $request into the listener, I modify the $rules variable and return it.
Would this be bad practice? What would be the recommended way of doing it?
I mean, it works. I'm just unsure if this is the best way to go about it.
Code below:
SettingsController.php (this is under App/ and where I'm validating on update)
public function update(Setting $setting, Request $request)
{
$rules = [
'package' => 'required|in:'.implode(config('app.packages'),','),
'name' => 'required|max:255|alpha_dash|not_contains:-|unique:auth_setting,name,'.$setting->id.',id,package,'.$setting->package,
'description' => '',
];
// Is this bad??
$rules = Event::fire(new SettingsWereSubmitted($request,$rules))[0];
$v = Validator::make($request->all(),$rules);
Then in my package (packages/exchange/src/Listeners) I got this listener (ValidateSettings.php):
public function handle(SettingsWereSubmitted $event)
{
if($event->request->package == 'exchange')
{
// Add rules
$rules = [
'fee' => 'required|decimal|min_amount:0|max_amount:1|max_decimal:8',
'freeze_trade' => 'required|in:1,0',
];
$event->rules['value'] = $rules[$event->request->name];
return $event->rules;
}
}
I'm looking at this piece of your code
if($event->request->package == 'exchange')
and think that you can achieve the same behaviour easier by using required_if validation rule.
$rules = [
'package' => 'required|in:'.implode(config('app.packages'),','),
'name' => 'required|max:255|alpha_dash|not_contains:-|unique:auth_setting,name,'.$setting->id.',id,package,'.$setting->package,
'description' => '',
'fee' => 'required_if:package,exchange|decimal|min_amount:0|max_amount:1|max_decimal:8',
'freeze_trade' => 'required_if:package,exchange|in:1,0',
];
ADDED:
By the way, I would suggest using Request classes to validate income requests and remove validation code from controllers because validation of request is responsibility of Request but not Controller.
It's pretty easy in Laravel. First, you create your request class in your Http\Requests folder:
class UpdateSomethingRequest extends Requst
{
public function rules()
{
return [
'package' => 'required|in:'.implode(config('app.packages'),','),
'name' => 'required|max:255|alpha_dash|not_contains:-|unique:auth_setting,name,'.$setting->id.',id,package,'.$setting->package,
'description' => '',
'fee' => 'required_if:package,exchange|decimal|min_amount:0|max_amount:1|max_decimal:8',
'freeze_trade' => 'required_if:package,exchange|in:1,0',
];
}
}
And then just remove that code from you Controller and type-hint new request class to update method like following:
public function update(Setting $setting, UpdateSomethingRequest $request)
{
// Your request is already validated here so no need to do validation again
}

Is there a way to alias input names in the rules for Laravel 5 validation?

I like the feature in Laravel 5 that let's me throw my validation logic into one of Laravel 5's Form Requests and then Laravel will automatically validate it just before my controller's method is ran. However, one thing that is missing is an easy way to "alias" an input name.
For example (for simplicity sake), say I have a login form with an input field called "username" but it actually accepts an email address. The label on my form says Email. If there's an error, the error will say something like "Username is required". This is confusing since I'm labeling the field as Email on my form.
What's a good solution for aliasing inputs when using Laravel's validator?
So far I've come up with two ideas:
Solution 1: Use Laravel's custom error messages for each rule
<?php namespace App\Http\Requests;
use App\Http\Requests\Request;
class AuthLoginRequest extends Request {
public function rules()
{
return [
'username' => 'required|email',
'password' => 'required'
];
}
// This is redundant. And may have other pitfalls?
public function messages()
{
return [
'username.required' => 'email is required.',
'username.email' => 'email is invalid.'
];
}
...
}
Solution 2: Use my own custom form class to handle changing the input names to their label/alias (username becomes email for validation purposes), running validation, then changing them back.
`
use App\Http\Requests\Request;
class AuthLoginRequest extends Request {
public function rules()
{
return [
'username' => 'required|email',
'password' => 'required'
];
}
public function messages()
{
return [
'username.required' => ':attribute is required',
'username.email' => ':attribute is invalid'
];
}
public function attributes()
{
return[
'username' => 'email', //This will replace any instance of 'username' in validation messages with 'email'
//'anyinput' => 'Nice Name',
];
}
}`
Updated
I believe anywhere there's an :attribute in your messages will be either default to the name set in your form, or overridden by the method attributes() above. (on :attribute http://laravel.com/docs/5.0/validation#custom-error-messages)
Footnote: I have not explained how I'm doing a dynamic array for my form, as I'm iterating over each form input in a way which may be clumsy. I did notice that I'm using the following line for my form request. I thought I'd mention that here in case:
use Illuminate\Foundation\Http\FormRequest;
Let me know if this is more helpful!
You can customise your validation input names in file resources/lang/en/validation.php, assuming you are using Laravel 5 and using English as locale language by default.
You can find an array called 'custom', simply customise your input names and validation messages there.
For example:
'custom' => array(
'username' => array(
'email' => 'Username is actually an email.'
),
)
where 'username' is your input name, 'email' is the name of built-in rule, and 'Username is actually an email' is whatever you want to tell your user.
Hope this helps!
Open the file resources/lang/en/validation.php, where en is the default language of the app. thars if you are using English. At the bottom of the file, update the attributes array as the following:
'attributes' => array( 'password' => 'Password', 'email' => 'Email Address', ),
Add other attribute names and the corresponding error label. It will do the trick you wanted.
Use this:
$validator = Validator::make($data, [
'username' => 'required|string', // Your field validation
])
->setAttributeNames(
['username' => '"Username"'], // Your field name and alias
);

Categories