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.
Related
I'm making a create form for one of my resources using Nova. Some of the fields have conditional relationships to one another.
For example: if "is trial" is selected we must specify a value for "trial end date", but there's no point showing the "end date" field on the page if "is trial" isn't picked. Another example, fields A and B are mutually exclusive.
All of these can easily be enforced with conditional validators in the backend, and I know how to do that. I'm just trying to make an interface that's not confusing.
How can I customize the frontend JS forms for this resource to reflect such conditional relationships?
Its possible using this one
// put this inside **public function fields(Request $request)**
BelongsTo::make('Subcategoria', 'subcategory', 'App\Nova\SubCategory'),
// conditional display
$this->sellerFields(),
//used for conditional seller input
protected function sellerFields()
{
if(\Auth::user()->role == "admin"){
return $this->merge([
BelongsTo::make('Vendedor', 'user', 'App\Nova\User'),
]);
}else{
return $this->merge([]);
}
}
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.
In my model (In yii2 project) I have two columns called product and code. And the issue is how to validate only code not product. We know that $model->validate() validates entire model. But I need only one input field: code. Is it possible??
More clearly, In my input form I'm using 3 models. How to validate these 3 models in my controller. That's why I'm trying to validate fields of each model separalety? I meant to validate like:
$model->validate(someField)
$anotherModel->(anotherField)
Is this possible??
You can replace this function with model. also show what you have tried so far?
public function rules()
{
return [
[['code', ], 'required'],
];
}
You can use scenario approach in validations rules, and validate only needed fields, by passing appropriate scenario.
More info about scenarios:
http://www.yiiframework.com/doc-2.0/guide-structure-models.html
Is there any way to prompt for validation with the attribute's label assigned via ActiveForm?
For instance I have a model attribute amount and the label defined in its attributeLabels function is "Amount"
But while generating form I neeed the label "Fees" so:
$form->field($model, 'amount')->textInput(['maxlength' => true])->label('Fees')
After validation it prompts me "Amount cannot be blank" - it is known to me that we can write a rule to change message but according to my requirements, same attribute (from same model) is having different labels on different forms.
I know that back-end implementation of default message uses:
'message' => '{attribute} cannot be blank.' does anyone know if there is any {x} by which the assigned label in ActiveForm can be retrieved?
PS: I know that this problem can be resolved by scenarios. But it would be hectic to write rule for every field which has dual label.
There is no such way, sorry! What you are doing is overriding the label within the view. Actually within the form to be more precise. If you check out what the label()-method does, you will see that it calls activeLabel() from the Html-helper. This method in turn renders your label and returns the code.
As you can see, none of this information gets written back to the model. Therefore, during the validation process, you won't have your new label at hand, because it never makes its way into the model.
Your cleanest option is the one you mentioned already. Use scenarios to decide which validation rule (and therefore message) to use. OR you could create your own public property in which you write your temporary label like so:
class MyModel extends \yii\db\ActiveRecord
{
public $myTempLabel;
public function attributeLabels()
{
$myLabel = $this->myTempLabel === null ? Yii::t('app', 'defaultLabel') : $this->myTempLabel;
return [
//other labels...
'myField'=>$myLabel,
//other labels...
];
}
}
In your view you can then set the value back into the attribute within your model.
Sorry for not being able to help better, but that's the way it's implemented!
What you need to do is handle the label changing logic in your attributeLabels() function.
class MyModel extends \yii\base\Model {
public $amount_label = 'amount';
public function attributeLabels() {
return [
'amount' => $this->amount_label
];
}
}
Then in your controller.
$model = new MyModel;
$model->amount_label = 'fees';
...
Of course you may want to set the label in a different way. For example, if your model as a type attribute, and this is the attribute which determines the label, you could do a conditional based on that type attribute.
I'm wondering what's the "best" approach to validate fields generically. In my application several tables have date values that are always entered using a date picker widget. I don't want to repeat the validation code, so I would like to do something like filling the $validate array in the AppModel. But it gets overwritten in the concrete model class. The best I found so far is the paragraph "Dynamically change validation rules" in the cake book, and apply that logic to the AppModel somehow, but it looks a bit hacky and un-caky. Does anyone have a hint?
(If you have questions, please ask.)
Thanks
Just name them differently - unique so to speak:
public function validateDateTime() {}
etc. This way your custom rules don't verwrite the core rules and vica versa.
I had some validation rules that I wanted to put in 3 models, to not repeat the same code, here what I did
in AppModel.php, define some var with those rules that should be in multiple models.
public $validationRules = arra(
// rules here
);
and add them for necessary models in AppModel's constructor
public function __construct($id = false, $table = null, $ds = null) {
parent::__construct($id, $table, $ds);
/**
* add validation
*/
if (in_array($this->alias, array('MyModel1', 'MyModel2', 'MyModel3')) ) {
$this->validate = array_merge($this->validate, $this->validationRules);
}
}
if there are some custom validation functions, those can be moved to AppModel.php as well.