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/
Related
I'm using codeigniter 3 to build a simple login form but my callback validation message didn't work. Here is my code:
LoginForm_model.php
class LoginForm_model extends CI_Model
{
private $username;
private $password;
protected $validationRules = [
'username' => 'required|callback_username_exist',
'password' => 'required',
];
public function username_exist($str)
{
$this->load->model('Reseller_model', 'reseller');
$reseller = $this->reseller->findOne(['username' => $this->input->post('LoginForm')['username']]);
if (!empty($reseller)) {
return true;
}
$this->form_validation->set_message('username_exist', 'Username tidak ditemukan');
return false;
}
public function validate() {
$modelName = explode("_", get_class($this));
foreach($this->validationRules as $field => $validationRule) {
$this->form_validation->set_rules($modelName[0].'['.$field.']', $this->getLabels($field), $validationRule);
}
return $this->form_validation->run();
}
}
I use that model in my controller for validating login form input
Welcome.php
class Welcome extends CI_Controller {
public function index()
{
$this->load->model("Loginform_model", "loginform");
if (NULL !== $this->input->post('LoginForm')) {
if ($this->loginform->validate()) {
echo "All Set!";
}
}
$this->load->view('login');
}
}
The callback validation seems to work, but the message is like this:
Unable to access an error message corresponding to your field name Username.(username_exist)
The callback validation works if I put username_exist function in my controller. Can't we do it in a model? I want to make the controller as clean as possible. Please help.
The idea here is to insert in two different tables parameters using SCENARIO, first register the customer with their parameters and then register the order that belongs to a customer, all in just one form.
I'm sending a form with several parameters, some will be used in the insertion of a customer using scenario, and the other parameters I will use in order (I did this so I do not have to create two forms) the parameters are being correctly sent through POST together with the csrf.
public function createOrder()
{
//$customer = Customer::find()->where(['email' => $params->email])->limit(1)->asArray()->all();
$customer = new Customer;
$customer->load(Yii::$app->request->post());
$customer->scenario = 'create';
if($customer->validate()){
$customer->save();
vdp($customer);
} else{
vdpd($customer->getErrors());
}
die;
}
This returns me an array saying that the Name, email, address, cell, phone, city, etc parameters can not be left blank.
In my customer model:
const SCENARIO_CREATE = 'create';
public function scenarios()
{
$scenarios = parent::scenarios();
$scenarios[self::SCENARIO_CREATE] = ['name', 'email', 'public_place', 'cell_phone', 'phone', 'city', 'cep', 'state', 'neighborhood', 'number', 'complement'];
return $scenarios;
}
Controller
public function actionCreateOrder()
{
$customer = new Customer;
$customer->setScenario(Customer::SCENARIO_CREATE);
if($customer->load(Yii::$app->request->post())
if($customer->save()){
vdp($customer);
} else {
vdpd($customer->getErrors());
}
}
die;
}
MODEL
const SCENARIO_CREATE = 'create';
public function rules()
{
return [
[['name', 'email', 'address'], 'required', 'on' => self::SCENARIO_CREATE], // Add more required fields on 'create' scenario.
... // some more rules
];
}
public function scenarios()
{
$scenarios = parent::scenarios();
$scenarios[self::SCENARIO_CREATE] = ['name', 'email', 'public_place', 'cell_phone', 'phone', 'city', 'cep', 'state', 'neighborhood', 'number', 'complement'];
return $scenarios;
}
1) createOrder() should be actionCreateOrder() in controller, not in model.
2)
$customer->load(Yii::$app->request->post());
$customer->scenario = 'create';
if ($customer->validate()) {
...
should be
$customer->scenario = Customer::SCENARIO_CREATE;
if ($customer->load(Yii::$app->request->post()) && $customer->validate()) { ... }
Because load method loads attributes for the current scenario and there is no need to run validation before the model is loaded. See the example.
3) Declare rules for your attributes in Customer model.
You need to set $scenario before load() call. Scenario defines attributes, that can be set by load(), so you're doing it too late and it has no effect. Try this:
$customer = new Customer;
$customer->scenario = 'create';
$customer->load(Yii::$app->request->post());
I'm creating a website using Yii2. There is a model called Contact Form like this:
class ContactForm extends Model
{
public $name;
public $email;
public $subject;
public $body;
public $response;
public $response2;
public function rules()
{
return [
[['name', 'email', 'subject', 'body'], 'required'],
['email', 'email'],
['response', function ($attribute, $params)
{
$this->addError($attribute, 'Wrong response 1');
}, 'skipOnError' => false],
['response2', 'validresponse2'],
];
}
public function validresponse2($attribute, $params) {
$this->addError($attribute, 'Wrong response 2');
}
}
There are 2 attributes (response and response2) which have custom validation.
Both custom validation are not working. They didn't pop up an error message at all. When I put echo "asd; die(); inside those functions, They didn't die too.
Is my rule setting wrong? I've searched through other questions but no luck.
UPDATE
Here is how I validate the form in my controller
$model = new ContactForm();
$model->subject = "New Message";
if (isset($_POST['ContactForm'])) {
$model->attributes = $_POST['ContactForm'];
$model->response = "";
if ($model->validate()) {
echo "validated successfully";
die();
}
}
And it is always validated successfully
Instead of skipOnError you need to set skipOnEmpty to false because otherwise empty attribute does not trigger validation.
skipOnError is true by default and means that in case of validation error in one rule the rest of rules for this attribute are skipped.
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.
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;
}