I have a question about Yii validation. I have a dropdown whose options are Y and N. If the user select Y the user must explain why he chose Y hence a textArea box will become required.
My code for the rule looks at follows.
array('explain', 'check', 'trigger'=>'med_effects'),
Check is my function used for validation
public function check($attribute, $params)
{
if($this->$params['trigger'] == 0 && $this->$attribute == '') {
$this->addError($attribute, 'Explain the effects of the medicine');
}
}
The value for $this->$params['trigger'] does not change. I'm assuming because the saved value was 0(Y) and does not change even if the user choose N. How am I supposed to determine which option the user chose when he sumbits the form?
Thanks.
Create a property inside your model:
public $isDropDownChecked;
In your view, create a dropdown wired to new property created.
And return a array of rules inside the method rules() like this:
public function rules()
{
$rules[] = array();
if ($this->isDropDownChecked == 'Y')
$rules[] = array('explain', 'check', 'trigger'=>'med_effects');
return $rules;
}
This may also help someone on Yii1,
say you have three fields with a lookup of [Yes|NO], and you want atleast one field to be selected as Yes
Here is the solution
on your model add
public $arv_refill;
public $prep_refilled;
public $prep_initiate;
on your rules add
public function rules()
{
return array(
array('arv_refill,prep_refilled,prep_initiate','arvPrepInitiateValidation'),
);
}
arvPrepInitiateValidation being a function
public function arvPrepInitiateValidation($attribute_name,$params)
{
if($this->arv_refill != 1 && $this->prep_refilled != 1 && $this->prep_initiate != 1){
$msg = "arv_refill, prep_refilled or prep_initiate field must have one field as Yes";
$this->addError('arv_refill',$msg);
$this->addError('prep_refilled',$msg);
$this->addError('prep_initiate',$msg);
}
}
Related
I am having a hard time understanding this validation rule. Basically, I have two fields, and they are both nullable. But, once both fields are filled, they have to be different from each other. I cannot enter test in both of them, for example. This validation rule works, if I fill both fields.
But, when I only fill one of the fields, the validation fails and says the fields should be different from each other with the following message:
The name and replace must be different.
I checked what is being submitted to my Form Request, and this is the following:
"name" => null
"replace" => "test"
Stripped version of my validation rules:
public function rules()
{
return [
'name' => 'different:replace|nullable',
'replace' => 'different:name|nullable',
];
}
Can somebody explain to me what I am misunderstanding with this validation rule? Do null values not count with this rule?
If you take a look at the validateDifferent function from Illuminate\Validation\Concerns\ValidatesAttributes (vendor/laravel/framework/src/Illuminate/Validation/Concerns/ValidatesAttributes.php:432) rule:
public function validateDifferent($attribute, $value, $parameters)
{
$this->requireParameterCount(1, $parameters, 'different');
foreach ($parameters as $parameter) {
$other = Arr::get($this->data, $parameter);
if (is_null($other) || $value === $other) {
return false;
}
}
return true;
}
As you can see in the if case, the rule will fail if the other value is null.
if (is_null($other) || $value === $other)
I'm using a REST API to receive the data.
The data model is polymorphic related, similar to the one on the documentation:
https://laravel.com/docs/5.4/eloquent-relationships#polymorphic-relations
posts
id - integer
title - string
body - text
videos
id - integer
title - string
url - string
comments
id - integer
body - text
commentable_id - integer
commentable_type - string
Let's say, for example, the API is receiving this new comment:
{
"body": "This a test comment",
"commentable_type": "posts",
"commentable_id": "1"
}
How can I validate if the received commentable_type exists and is valid?
If I correctly understand your question, you are trying to validate that the object of the polymorphic relation exists, for the given commentable_type and commentable_id.
If that is the case, there is no existing validation rule to do so, but you can create one.
Based on the documentation, here is what you could do:
First, add the new rule in the boot method of a service provider (e.g. AppServiceProvider):
Validator::extend('poly_exists', function ($attribute, $value, $parameters, $validator) {
if (!$objectType = array_get($validator->getData(), $parameters[0], false)) {
return false;
}
return !empty(resolve($objectType)->find($value));
});
And this is how you would use it:
'commentable_id' => 'required|poly_exists:commentable_type
What the rule does is it tries and fetches the commentable type from the input values (based on the parameter passed on to the rule, i.e. commentable_type in our case), and then resolves the object and tries to find a record for the given ID ($value).
Please note that for this to work however, the value of commentable_type must be the fully qualified class name (e.g. App\Models\Post).
Hope this helps!
Better approach that includes morphs map:
Validator::extend('poly_exists', function ($attribute, $value, $parameters, $validator) {
if (! $type = array_get($validator->getData(), $parameters[0], false)) {
return false;
}
if (Relation::getMorphedModel($type)) {
$type = Relation::getMorphedModel($type);
}
if (! class_exists($type)) {
return false;
}
return ! empty(resolve($type)->find($value));
});
You can dynamically define a model_exists rule in your Request class. Something like this:
public function rules()
{
$polymorphExistsRule = '';
if ($this->has('commentable_type')) {
$polymorphExistsRule .= '|exists:' . $this->commentable_type . ',id';
}
return [
'commentable_type' => 'required_with:commentable_id',
'commentable_id' => 'required_with:commentable_type' . $polymorphExistsRule,
];
}
Edit
I might've misunderstood the first time. If you want to check that the model saved in commentable_type exists you could do something like this:
$type = $comment->commentable_type;
if(class_exists($type)) echo "it exists";
Depending on your needs you could do additional checking for it's inheritance (for example that it extends class Model). Or anything else that fits your needs really.
Edit2
This is what I would do if I were you. I would add property protected $allRelations to your Comment model and manually put all the relationships in. Then make some helper models to check if it's in the array.
Simple example:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Comment extends Model
{
// ..
protected $allRelations= [
'posts' => '\App\Post',
'videos' => '\App\Video',
];
public static function validateRelationNs($ns) {
return in_array($ns, $this->allRelations);
}
public static function validateRelationName($name) {
return array_key_exists($name, $this->allRelations);
}
// ...
}
Old answer:
Laravel expects full namespace name of the model for polymorphic type columns (in your case commentable_type should be \Full\Ns\Post, not posts).
The easiest way to ensure correctness is to always save it through the relationship. For example:
$post = Post::first();
$comment = new Comment($attributes);
$post->comments()->save($comment).
This will automatically set both commentable_id and commentable_type correctly (assuming your relationsare correctly defined).
Additional checking
Other then that you could check through model events. You could validate it before saving to the database.
My final version work for validate type and id:
Validator::extend('poly_exists', function ($attribute, $value, $parameters, $validator) {
if (!$objectType = array_get($validator->getData(), $parameters[0], false)) {
return false;
}
if (!class_exists($objectType)) {
return false;
}
return !empty(resolve($objectType)->find($value));
});
I have form, created by ActiveForm widget. User enters polish postal code there. In appropriate controller I put entered data in DB, for example:
$company_profile_data->postal_code = $_POST['CompanyProfiles']['postal_code'];
$company_profile_data->update();
I decided to use standalone validator for postal code validation. Rules for this attribute in model:
public function rules() {
return [
//...some other rules...
['postal_code', 'string', 'length' => [6,6]],
['postal_code', PostalValidator::className()], //standalone validator
];
}
app/components/validators/PostalValidator class code:
namespace app\components\validators;
use yii\validators\Validator;
use app\models\CompanyProfiles;
use app\models\Users;
class PostalValidator extends Validator {
public function init() {
parent::init();
}
public function validateAttribute($model, $attribute) {
if (!preg_match('/^[0-9]{2}-[0-9]{3}$/', $model->$attribute))
$model->addError($attribute, 'Wrong postal code format.');
}
public function clientValidateAttribute($model, $attribute, $view) { //want js-validation too
$message = 'Invalid status input.';
return <<<JS
if (!/^[0-9]{2}-[0-9]{3}$/.test("{$model->$attribute}")) {
messages.push("$message");
}
JS;
}
}
So, an example of correct code is 00-202.
When I (in user role) enter incorrect value, page reloads and I see Wrong postal code format. message, although I redefined clientValidateAttribute method and wrote JS-validation, which, as I suggested, will not allow page to reload. Then I press submit button again: this time page doesn't reload and I see Invalid status input. message (so, the second press time JS triggers). But I when enter correct code after that, I still see Invalid status input. message and nothing happens.
So, what's wrong with my clientValidateAttribute() method? validateAttribute() works great.
UPDATE
Snippet from controller
public function actionProfile(){ //can't use massive assignment here, cause info from 2 (not 1) user models is needed
if (\Yii::$app->user->isGuest) {
return $this->redirect('/site/index/');
}
$is_user_admin = Users::findOne(['is_admin' => 1]);
if ($is_user_admin->id == \Yii::$app->user->id)
return $this->redirect('/admin/login/');
$is_user_blocked = Users::find()->where(['is_blocked' => 1, 'id' => \Yii::$app->user->id])->one();
if($is_user_blocked)
return $this->actionLogout();
//3 model instances to retrieve data from users && company_profiles && logo
$user_data = Users::find()->where(['id'=>\Yii::$app->user->id])->one();
$user_data->scenario = 'update';
$company_profile_data = CompanyProfiles::find()->where(['user_id'=>Yii::$app->user->id])->one();
$logo = LogoData::findOne(['user_id' => \Yii::$app->user->id]);
$logo_name = $logo->logo_name; //will be NULL, if user have never uploaded logo. In this case placeholder will be used
$upload_logo = new UploadLogo();
if (Yii::$app->request->isPost) {
$upload_logo->imageFile = UploadedFile::getInstance($upload_logo, 'imageFile');
if ($upload_logo->imageFile) { //1st part ($logo_data->imageFile) - whether user have uploaded logo
$logo_file_name = md5($user_data->id);
$is_uploaded = $upload_logo->upload($logo_file_name);
if ($is_uploaded) { //this cond is needed, cause validation for image fails (?)
//create record in 'logo_data' tbl, deleting previous
if ($logo_name) {
$logo->delete();
} else { //if upload logo first time, set val to $logo_name. Otherwise NULL val will pass to 'profile' view, and user wont see his new logo at once
$logo_name = $logo_file_name.'.'.$upload_logo->imageFile->extension;
}
$logo_data = new LogoData;
$logo_data->user_id = \Yii::$app->user->id;
$logo_data->logo_name = $logo_name;
$logo_data->save();
}
}
}
if (isset($_POST['CompanyProfiles'])){
$company_profile_data->firm_data = $_POST['CompanyProfiles']['firm_data'];
$company_profile_data->company_name = $_POST['CompanyProfiles']['company_name'];
$company_profile_data->regon = $_POST['CompanyProfiles']['regon'];
$company_profile_data->pesel = $_POST['CompanyProfiles']['pesel'];
$company_profile_data->postal_code = $_POST['CompanyProfiles']['postal_code'];
$company_profile_data->nip = $_POST['CompanyProfiles']['nip'];
$company_profile_data->country = $_POST['CompanyProfiles']['country'];
$company_profile_data->city = $_POST['CompanyProfiles']['city'];
$company_profile_data->address = $_POST['CompanyProfiles']['address'];
$company_profile_data->telephone_num = $_POST['CompanyProfiles']['telephone_num'];
$company_profile_data->email = $_POST['CompanyProfiles']['email'];
$company_profile_data->update();
}
if (isset($_POST['personal-data-button'])) {
$user_data->username = $_POST['Users']['username'];
$user_data->password_repeat = $user_data->password = md5($_POST['Users']['password']);
$user_data->update();
}
return $this->render('profile', ['user_data' => $user_data, 'company_profile_data' => $company_profile_data, 'upload_logo' => $upload_logo, 'logo_name' => $logo_name]);
}
My inaccuracy was in clientValidateAttribute() method. Instead of $model->$attribute in code snippet:
if (!/^[0-9]{2}-[0-9]{3}$/.test("{$model->$attribute}")) {
...I had to use predefined JS-var value, cause this var changes with entered value change. So, my new code is:
public function clientValidateAttribute($model, $attribute, $view) {
return <<<JS
if (!/^[0-9]{2}-[0-9]{3}$/.test(value)) {
messages.push("Wrong postal code format.");
}
JS;
}
Model does not load rules and behaviors until not called any function from model. When you call $company_profile_data->update(); model call update and validate functions.
Try add after $company_profile_data = CompanyProfiles::find() this code:
$company_profile_data->validate();
Or just use load function. I think it will help.
I'm trying to figure out how I can use the is_unique rule from the Codeigniter form validation library in the following situation.
I'm trying to submit a edit user form and have the rule:
$this->form_validation->set_rules('user_name', 'User Name', 'required|trim|xss_clean|is_unique[users.user_name]');
What if other values in the form are being changed but this value stays the same. The form is going to see that this value already exists so how would I protect it from editing if this value isn't changed.
Using your code as an example, the is_unique validation rule works by looking for a field called user_name in your users database table. If the field with the same value exists it validates as false.
To make sure it runs only when the user submits a new value, you could check the posted value $this->input->post('user_name') against the value you pulled from the database to populate your form with. If they are the same, don't validate is_unique;
if($this->input->post('user_name') != $original_value) {
$is_unique = '|is_unique[users.user_name]'
} else {
$is_unique = ''
}
$this->form_validation->set_rules('user_name', 'User Name', 'required|trim|xss_clean'.$is_unique);
There's a better way to go around it, I think, still using CodeIgniters' validation library...
Use edit_unique where you pass an extra parameter which is the id of the row you're editing.. See below.. I use it and works pretty fine for me.. hope it helps
$this->form_validation->set_rules('user_name', 'User Name', 'required|trim|xss_clean|edit_unique[users.user_name.'.$id.']');
$something = $this->input->post('something');
$this->form->validation->set_rules('something','Something','xss_clean|is_unique['tbl'.users]');
if($this->form_validation->run()== FALSE){
}
Simple Way
Just Change isset to is_object in system/libraries/form_validation.php
public function is_unique($str, $field)
{
sscanf($field, '%[^.].%[^.]', $table, $field);
return is_object($this->CI->db) //default isset
? ($this->CI->db->limit(1)->get_where($table, array($field => $str))->num_rows() === 0)
: FALSE;
}
Here's an easy method that worked for me and uses well documented code (thanks to https://github.com/ivantcholakov for sharing it!). I found it referenced at https://github.com/bcit-ci/CodeIgniter/issues/3109#issuecomment-46346280
Download https://github.com/ivantcholakov/starter-public-edition-3/blob/master/platform/application/libraries/MY_Form_validation.php (MIT licensed) and save it to your application at application\libraries\MY_Form_validation.php
Delete these two lines from __construct():
$this->CI->load->helper('checkbox');
$this->CI->load->helper('email');
Delete all the functions except __construct() and unique().
At the end of the __construct() method of your controller add this line:
$this->load->library('form_validation');
As per the documentation of the unique() method update your validation rule to add a "unique" rule like this (e.g. if you already have required and trim rules):
…|required|unique[tablename.fieldname,tablename.(primaryKey-used-for-updates)]|trim...
Extend Form_validation.php library create class inside of application/libraries file name MY_Form_validation.php
<?php
class MY_Form_validation extends CI_Form_validation{
protected $ci;
public function __construct($config = array()){
parent::__construct($config);
$this->ci =& get_instance();
}
public function is_unique_update($str, $field){
$explode=explode('#', $field);
$field_name=$explode['0'];
$field_id_key=$explode['1'];
$field_id_value=$explode['2'];
sscanf($field_name, '%[^.].%[^.]', $table, $field_name);
if(isset($this->ci->db)){
if($this->ci->db->limit(1)->get_where($table, array($field_name => $str,$field_id_key=>$field_id_value))->num_rows() === 0){
$this->ci->form_validation->set_message('is_unique_update', 'The {field} field must contain a unique value.');
return false;
}
return true;
}
}
}
Now in your controller
$this->form_validation->set_rules('user_name', 'User Name', 'required|trim|xss_clean|is_unique_update[users.user_name#id#'.$id.']');
"#" I used for explode the string
where id is primary key of users table
and $id is the value of id.
Now you can use this is_unique_update validation in any controller.
This question is very old but maybe some new people experience this problem and this is the solution for it.
I bet your are using Modular Extensions (HMVC) and you have created a new library, MY_Form_validation. You did id for callbacks, so you have this line of code on your class in order to use callbacks:
$this->form_validation->CI =& $this;
Well, the solution to this is whenever you want to use "is_unique" you must delete this line of code "$this->form_validation->CI =& $this;" from the class. I have experienced this problem and i fix it this way, it works fine now.
If you realy want to use callbacks "$this->form_validation->CI =& $this;", then do it only on required "methods" / "functions" where you don't want to use is_unique.
This code helpful for unique validation to create and update function...
In controller
Add this form validation code in both create and update function
$this->form_validation->set_rules('order_no', 'Order no', 'required|callback_check_order_no');
Add this call back function in controller
function check_order_no($order_no) {
if($this->input->post('id'))
$id = $this->input->post('id');
else
$id = '';
$result = $this->Data_model->check_unique_order_no($id, $order_no);
if($result == 0)
$response = true;
else {
$this->form_validation->set_message('check_order_no', 'Order no already exist');
$response = false;
}
return $response;
}
In model
function check_unique_order_no($id = '', $order_no) {
$this->db->where('order_no', $order_no);
$this->db->where('status', "A");
if($id) {
$this->db->where_not_in('id', $id);
}
return $this->db->get('delivery_order')->num_rows();
}
I'm using codeigniter3 and it shows me error when I check username on updating the value, is_unique is not designed to work with update scenario
so using #Anthony Mutisya's answer, here is the complete solution
in your controller, add this line while validation username of the current user with the database
$this->form_validation->set_rules('user_name', 'User Name', 'required|trim|xss_clean|edit_unique[users.user_name.'.$id.']');
You can get that $id from your submited form.
Now, add the following function to /system/libraries/Form_Validation.php this file. System folder is present in your root of CodeIgniter3 folder.
/**
* edit_unique // for check on update value
*
* Check if the input value doesn't already exist
* in the specified database field.
*
* #param string $str
* #param string $field
* #return bool
*/
function edit_unique($value, $params) {
$CI =& get_instance();
$CI->load->database();
$CI->form_validation->set_message('edit_unique', "Sorry, that %s is already being used.");
list($table, $field, $current_id) = explode(".", $params);
$query = $CI->db->select()->from($table)->where($field, $value)->limit(1)->get();
if ($query->row() && $query->row()->id != $current_id)
{
return FALSE;
} else {
return TRUE;
}
}
It works perfectly fine in my case
CodeIgniter 4 has already solution for that,
$validation->setRules([
'email' => 'required|valid_email|is_unique[users.email,id,{id}]',
]);
$_POST = [
'id' => 4,
'email' => 'foo#example.com',
];
then the {id} placeholder would be replaced with the number 4, giving this revised rule:
$validation->setRules([
'email' => 'required|valid_email|is_unique[users.email,id,4]',
]);
Official Documentation
we must have to add table name for is_unique
for Exp.
is_unique[users.email]
There is a 'main.php' view that contains a form with email and name fields and a submit button. Eveyrthing works fine with action_index (the code is below), but I'm curious how to modify the code below so it validates if the email was entered correctly. It should not put values in the database if the email field is not valid. I hope it is possible to made using ->rule. Is it? If yes, then how where to add the validation? (I had no luck trying it in different ways).
public function action_index()
{
if ( !empty($_POST) ) {
$model = ORM::factory('tbl1'); // create
$model->values($_POST); // load values to model
if ($model->check()) {
$model->save(); // save the model
} else {
//show errors
}
}
$this->response->body(View::factory('main'));
}
Thank you.
Use rules function in your ORM model:
public function rules()
{
return array(
'email' => array(
array('email', array(':value')),
),
);
}