targetAttribute (Or something similar) for required validator (Yii2) - yii2

So, here is my scenario. I have got a model called URL. URL has the following attributes: link (required), scheme (required, but not safe.scheme is parsed from the link) and a few other attributes as well, which are not in context to this question.
Now, I made a custom validator for the scheme, which is following:
public function validateScheme($attribute, $param) {
if(empty($this->scheme)){
$this->addError('link', Yii::t('app', 'This is an invalid URL.'));
}
if (!in_array($this->scheme, $this->allowedSchemes)) {
$this->addError('link', Yii::t('app', 'This is an invalid URL.'));
}
}
Rules for URL:
public function rules() {
return [
['link', 'required', 'message' => Yii::t('app', 'URL can\'t be blank.')],
[['link'], 'safe'],
[['link'], 'string'],
['scheme', 'validateScheme']
];
}
This works fine when an invalid scheme is encountered, for example like let's say ftp.
However, when a completely invalid URL is entered, the scheme remains empty and the validateScheme is never triggered as, attribute scheme is not required. To verify, I called $model->validate() and it returns true even if it should not (or should may be, because the attribute is not required anyway).
So, my question number 2 : Is there a way to force the validateScheme be triggered no matter if the attribute is empty, or non empty? I do not seem to find a way to do this in documentation.
I then tried the other way around, and made scheme a required field. The problem with that is the fact that scheme field is not safe and not there in the form. So, the URL does not save, but no error is shown.
My question number 1, in that case would be: Is there a way to assign targetAttribute for scheme so that the error message is shown below link?
P.S. I know I can do this in the controller. I do not want to do that. I want to use the model only.

Another solution is, instead of having a default value, you could enable verify on empty (by default, it does not validate empty and not required fields). Something like this:
public function rules() {
return [
['link', 'required', 'message' => Yii::t('app', 'URL can\'t be blank.')],
[['link'], 'string'],
['scheme', 'validateScheme', 'skipOnEmpty' => false]
];
}
See more here.

Okay, setting a default value in the rules() solved my problem. The modified rules():
public function rules() {
return [
['link', 'required', 'message' => Yii::t('app', 'URL can\'t be blank.')],
[['link'], 'safe'],
[['link'], 'string'],
['scheme', 'default', 'value' => 0],
['scheme', 'validateScheme']
];
}

Related

Yii2 : Require at least one of the two fields

One of the two phone numbers phone_parent or phone_students must be provided AND they must be an integer. When combined, only atLeastValidator works, when I leave it out, the integer works. Out of ideas. Any hint?
[['phone_parent', 'phone_student'], 'integer'],
['phone_student', AtLeastValidator::class, 'in' => ['phone_student', 'phone_parent']],
['phone_parent', AtLeastValidator::class, 'in' => ['phone_student', 'phone_parent']],
update: I've just discovered that integer works when I try to submit (no request is sent yet, I remain on form page); However it should work on focus out - just like all the other validators; It's an instance of ActiveForm.
I don't think you need to have that custom AtLeastValidator, you can use when and whenClient in the following way to work the way you want.
Your rules should look like below
[
['phone_parent'],
'required',
'when' => function ($model) {
return ($model->phone_student == '');
},
'whenClient' => 'function(attribute,value){
return ($("#testform-phone_student").val()=="");
}',
'message' => 'Either Parent or Student Phone must be filled',
],
[
['phone_student'],
'required',
'when' => function ($model) {
return ($model->phone_parent == '');
},
'whenClient' => 'function(attribute,value){
return ($("#testform-phone_parent").val()=="");
}',
'message' => 'Either Parent or Student Phone must be filled',
],
[['phone_parent', 'phone_student'], 'integer'],
Above all i would use a regular expression in order to validate the phone number to be valid rather than just using integer that will allow 0 as a pone number or mobile number which isnt valid. using match validator with a regex in the pattern will make it solid.

Why rules in public function of model don't do its job correctly?

I'm using following rules in model:
public function rules() {
return [
[['beurteilung_fachlich', 'beurteilung_persoenlich', 'sonstiges', 'anschreiben'], 'string'],
[['anschreiben'], 'string', 'max' => 255]
];
}
Although there is nowhere any restriction for attribute beurteilung_persoenlich, I will get error of form validation:
Beurteilung(fachlich) must not be longer as 255 chars!
How can this be?
What do I wrong?
Is there another place for declaring validation rules besides in model?
Database has datatype TEXT without any boundary.
Here is code of form:
<div class="col-md-6">
<?= $form->field($model, 'beurteilung_fachlich')>widget(\dosamigos\ckeditor\CKEditor::className(), ['preset' => 'full', 'clientOptions' => ['height' => 200]]) ?>
</div>
I noticed, that following rule will influence all other string attributes, too
[['anschreiben'], 'string', 'max' => 255]];
I have no idea, why, but after having removed this rule, everything did well.
In order to valid form clientside, I coded like this(in form):
$form->field($model, 'anschreiben', ['addon' => ['prepend' => ['content' => 'Anschreiben']]])->widget(TwbsMaxlength::className())->textInput(['maxlength' => 255])->label(false);
In most of my cases, the javascript / client side validation still work because of caching. Even after the model is modified. Try to Cmd / Ctrl + Shift + R (bypassing the cache), or delete the content of your backend/web/assets or frontend/web/assets.

yii2 SluggableBehavior is applied to only emty slug field

I am using SluggableBehavior in such way
public function behaviors()
{
return [
[
'class' => SluggableBehavior::className(),
'attribute' => 'name',
'slugAttribute' => 'alias',
],
];
}
It works nice If field 'alias' in form is empty.
How to ignore this behaviors If field alias is not empty on form submitting ?
thanks in advance !
Add 'immutable'=>true in the behavour config.
The behaviour works in such way, that if the slugAttribute is not empty when immutable is on, that attribute will not be changed.
Try something like:
Configure this behavior with specific name:
return [
'slug' => [
'class' => SluggableBehavior::className(),
'attribute' => 'name',
'slugAttribute' => 'alias',
],
];
In controller load attributes from the form (before validation).
Check if alias attribute is empty.
If it is not - detach this behavior ($this->detachBehavior('slug')).
Validate model.

Yii2: Kartik Select2: Initial Value from Model Attribute

I have a Model who has a column (attribute) that stored a comma separated value of IDs.
For Example,
Movie has a column "Genre" that includes more than one genre, e.g.: 40,20,1,3
How can I use Select2 widget to show these values separated when 'multiple' => true
And how can I save them back into comma-separated value as a string. I want a solution that will allow for quick flexibility. I know you can implode and explode the string but seems too much.
Any help appreciated
If I remember correctly pass the default option as part of the $options configuration for the widget:
echo $form->field($model, 'model_attribute_name')->widget(Select2::className(), [
'data' => $data
'options' => [
'class' => 'form-control',
'placeholder' => 'Choose Option...',
'selected' => 40
],
'pluginOptions' => [
'allowClear' => true,
],
])->label('Select2 Form Field');
This is from memory for grain fo salt here. The documentation at http://demos.krajee.com/widget-details/select2 is not very specific about how to do this.
I don't believe you can do that. Select2 sends the data in post as an array, so you would still need to use implode before saving. What i would do instead is in your model class:
class MyModel extends \yii\db\ActiveRecord {
$public myArrayAttribute;
...
public function beforeSave($insert) {
if (parent::beforeSave($insert)) {
$this->myAttribute = implode(',', $this->myArrayAttribute);
return true;
}
return false;
}
public function afterFind() {
parent::afterFind();
$this->myArrayAttribute = explode(',', $this->myAttribute);
}
}
This way myArrayAttribute will hold the values from the comma separated field as an array. Of course you will need to add validation rules for it and use it instead of your other attribute in create and update forms.
if you're displaying a form with already populated fields, maybe you want to update an already existing object, and you want to display the already saved value for the Select2 field, use 'data' => [ 1 => 'Some value' ], where 1 is the value, associated to the value displayed in the form. You can retrieve stuff to put in data from DB beforehand.
Source: https://github.com/kartik-v/yii2-widget-select2/issues/37

Yii2 HTML purifier

I've got a question about Yii2's validation. So, my model validation rule's something like this:
return [
['status', 'required', 'on' => 'update'],
[['status'], function ($attribute) {
$this->$attribute = \yii\helpers\HtmlPurifier::process($this->$attribute);
}],
];
The problem is that if the content is <script>alert('something')</script>, it will be blank due to purifier and the content will pass the required validation.
So how can I revalidate the content for require? Or what is the good way to do it?
Validation rules are processed one after another so just put the second one as first.
return [
['status', 'filter', 'filter' => function ($value) {
return \yii\helpers\HtmlPurifier::process($value);
}],
['status', 'required', 'on' => 'update'],
];