Let's say I have a product which can have a colour. Depending on the product type, the colour field may or may not be required.
If colour is always required, I would have the following in the product model
public function rules()
{
return array(
array('colour', 'required')
);
}
However, I want this to be dynamic depending on the product type.
Should this be done in the controller? I would imagine having something like the following in the controller:
public function actionOrder() {
// ....
if ($product->HasColour) {
// set the colour validation to be required
} else {
// set the colour validation to be not required
}
}
What is the best way to approach this?
Thanks
You can use scenario. In the model:
class Model extends CActiveRecord {
// ....
public function rules() {
return array(
array('colour', 'required', 'on' => 'hasColour')
);
}
// ....
}
And in the controller:
public function actionOrder() {
// ....
$model = new Product();
if ($product->HasColour) {
$model->setScenario('hasColour');
}
}
So, required colour will be validated when the model's scenario is hasColour
class LoginForm extends CFormModel
{
public $username;
public $password;
}
$form = new LoginForm();
$form->validatorList->add(
CValidator::createValidator('required', $form, 'username, password')
);
Now $form has two required fields.
One approach is to use a custom validation rule. For example, the rule:
array('colour', 'requiredOnHasColour'),
And then the validator method in the same model class:
public function requiredOnHasColour($attribute, $params) {
if ($this->hasColour && $this->$attribute == null)
$this->addError($attribute, 'Colour is required.');
}
More info: Create your own validation rule
If you want to do more complicated logic, then scenarios might not satisfy your needs. Then you can override method init and do all the logic that define validation rules over there, adding results to $validationRules array. And the in rules() method you just return that array. Something like that:
class Person extends CActiveRecord
{
public function init(){
if( TRUE){
$this->validationRules[] = array('first_name','required');
$this->validationRules[] = array('last_name','required');
}
}
public $validationRules = array(
array('email', 'required'),
array('email, email1, email2, email3', 'email', 'message'=>'Email format is invalid'),
array('email, address, email1, email2, email3', 'length', 'max'=>255),
);
public function rules()
{
return $this->validationRules;
}
}
In your ActiveRecord you can add in any place dynamic validator. For example i overwrite parent method setAttributes and add $this->validatorList->add() depends on selected value in $this->type attribute. Official docs: https://www.yiiframework.com/doc/api/1.1/CValidator
public function setAttributes($values, $safeOnly = true)
{
$result = parent::setAttributes($values, $safeOnly);
if (self::TYPE_PHONE == $this->type) {
$this->validatorList->add(CValidator::createValidator('required', $this, ['phone', 'phonePrefix']));
} elseif (self::TYPE_EMAIL == $this->type) {
$this->validatorList->add(CValidator::createValidator('required', $this, ['email']));
}
return $result;
}
Related
I have request class like below.
class CategoryRequest extends Request
{
public function response(array $errors){
return \Redirect::back()->withErrors($errors)->withInput();
}
public function authorize()
{
return false;
}
public function rules()
{
return [
'Category' => 'required|unique:tblcategory|max:25|min:5'
];
}
}
There is rules function.
In the controller, there are multiple methods that have Request as a Parameter. Most of them vary in the validation point of view. I mean, if I am admin,. I can update 4 fields. If I am manager, I can update 3 and if I am normal user, I can update 2. So validation will change according to roles.
Is that possible to have multiple rules function in Request class ?
You can use here any conditions you want, so you you could do something like this:
public function rules()
{
$rules = [];
if (Auth::user()->isAdmin()) {
$rules['Category'] = '...';
}
elseif (Auth::user()->isManager()) {
$rules['Category'] = '...';
}
return $rules;
}
Of course you need to create isAdmin and isManager in your User model
need help updating a unique rule in my validation rules. I have a abstract validator that will validate a rules before storing into my database and in the rules array I set the email to be unique when creating or registering a user but when updating the user the enique email should not validate if the email is owned by the user.
abstract class Validator
abstract class Validator {
protected $errors;
protected $attributes;
public function __construct($attributes = null)
{
$this->attributes = $attributes ?: \Input::all();
}
public function passes()
{
$validation = \Validator::make($this->attributes, $this->rules());
if ($validation->passes()) return true;
$this->errors = $validation->messages();
return false;
}
public function getErrors()
{
return $this->errors;
}
}
Validation Rules(UserRule.php)
use MyCustomValidatorNamespaceHere....
class UserRules extends Validator
public function rules()
{
return [
'email' => 'required|email|unique:users,email,id',
...
];
}
and in my UserController I injected the UserRule in the constractor. (UserRule $userRule). Here is the code in the update method.
public function update($id)
{
$if ($this->userRule->passes())
{
$this->user->find($id)->update(Input::all());
return .........
}
}
But the validation always fail and displaying the error that the email is already taken. Please help guys.
The problem is your rule. When you update, you need to use unique that doesn't check record you update. So you should have:
unique:users,email,id
but for example:
unique:users,email,10
if you edit record with id 10.
What you could do is to define this rule:
'email' => 'required|email|unique:users,email,{id}',
and your passes method:
public function passes($id = null)
{
$rules = $this->rules();
$rules['email'] = str_replace('{id}', $id, $rules['email']);
$validation = \Validator::make($this->attributes, $rules);
if ($validation->passes()) return true;
$this->errors = $validation->messages();
return false;
}
and now in update rule use:
if ($this->userRule->passes($id))
By the way you have error in $if ($this->userRule->passes()) - it should be if and not $if
You can use the route method inside your request class to except an id from the validation
public function rules()
{
return [
'email' => 'required|email|unique:users,email,'.$this->route('user'),
...
];
}
I had a problem like that before and it was difficult to find an answer. Here is what I did.
class UserRules extends Validator {
public function __construct($input = NULL) {
$this->input = $input ?: \Input::all();
if(isset($this->input['id'])):
self::$rules['username'] = 'required|unique:users,username,'.$this->input['id'];
self::$rules['email'] = 'required|email|unique:users,email,'.$this->input['id'];
else:
self::$rules['username'] = 'required|unique:users';
self::$rules['email'] = 'required|email|unique:users';
endif;
}
public static $rules = array(
'company_id' => 'required',
'role' => 'required',
'password' => 'sometimes|required|confirmed'
);
}
You need to use self:: because $rules is static.
I have moved this function:
public function passes($id)
{
$rules = static::$rules;
$rules['username'] = str_replace('{id}', $id, $rules['username']);
$rules['email'] = str_replace('{id}', $id, $rules['email']);
$validation = \Validator::make($this->attributes, $rules);
if($validation->passes()) return true;
$this->errors = $validation->messages();
return false;
}
into UserRule.php and commented the same function in abstract class Validator
Now updating is working.
I solved this problem here on stackoverflow in a generic way. It will automatically adapt your rules if you do an update and add exceptions to your :unique if necessary.
Hey can anyone help me with validating a model in yii?
class MyFormModel extends FormModel
{
public myAttribute1;
public myAttribute2;
public function __construct()
{
$this->myAttribute1 = 'blablabla'
$this->user = new User();
}
public function rules()
{
$rules = parent::rules()
$rules[] = array('myAttribute1', 'required', 'message' => 'this is required');
$rules[] = array(#i need to add validation for $user->firstname here#);
return $rules;
}
}
how do i validate an attribute from another model?
You can write the rules for the firstname attribute in user model itself. And on this page you can validate this particular field by using
$userMDl = new User();
if($userMDl->validate(array('firstname '))
// valid
}
You can add to your MyFormModel attribute
private $firstname;
override init method
public function init(){
...
$this->firstname = $user->firstname;
}
and in your rules
$rules[] = array('firstname', 'required', 'message' => 'firstname is required');
You can write your own validation function in form class. Please see next article: http://www.yiiframework.com/wiki/168/create-your-own-validation-rule/
I would like to make a Yii Model rule dynamic, according to an attribute.
It's not giving error but also not working.
Am I doing something wrong? There any easier way to do it?
Model.php (Attributes: NAME, TYPE)
public function rules()
{
return array(
// Name is only required when Type is equal 1.
$this->type==1 ? array('name', 'required') : null,
);
}
Change to ( use scenario):
public function rules()
{
return array(
array('name', 'required','on'=>'typeTrue')
);
}
And in controller
public function actionSome() {
$model = new Model();
if ( $model->type == 1 ) {
$model->setScenario('typeTrue');
}
}
I found the answer and I would like to share.
For it need to use Yii rules scenarios.
Model.php:
public function rules()
{
return array(
array('name', 'required', 'on'=>'type1'),
);
}
Controller.php:
...
if ($model->type==1) {
$model->scenario = 'type1';
}
....
i got such form
class CC extends CFormModel
{
public $static_field;
public $fields;
public function rules()
{
return array(
array('static_field, testF', 'required')
);
}
public function getForm()
{
return new CForm(array(
'showErrorSummary'=>true,
'elements'=>array(
'static_field'=>array(),
'testF'=>array(),
),
'buttons'=>array(
'submit'=>array(
'type'=>'submit',
'label'=>'Next'
)
)
), $this);
}
public function attributeLabels()
{
return array(
'static_field' => 'static_field'
);
}
public function __get($name)
{
if (isset($this->fields[$name]))
return $this->fields[$name];
else
return '';
}
public function __set($name, $value)
{
$this->fields[$name] = $value;
}
}
i want to add dynamical field testF
i try to use __get\__set and array for values, but nothing work. any ideas?
If by dynamic you mean not required, you can add it as a property just as you have done with static_field. All attributes, or fields, are encapsulated member data of your FormModel class. So, if you wanted to add your dynamic_field attribute, you could add it in this manner:
class CC extends CFormModel
{
public $static_field;
public $dynamic_field;
public function rules()
{
return array(
array('static_field','required'),
array('dynamic_field','safe'),
);
}
}
Also, you're not exactly following the dominant usage pattern for this type of class. If I were you, I would suggest creating some CRUD through gii and examining the usage patterns for models and forms.