CakePHP custom Validation rule checks unique field combination only on create - validation

I have a Database with a User model. These Users should be unique by their name and birthday.
So I wrote a custom validation function called checkUnique
public function checkUnique($check){
$condition = array(
"User.name" => $this->data["User"]["name"],
"User.lastname" => $this->data["User"]["lastname"],
"User.birthday" => $this->data["User"]["birthday"]
);
$result = $this->find("count", array("conditions" => $condition));
return ($result == 0);
}
The validation rule in the model:
"name" => array(
"checkUnique" => array(
"rule" => array("checkUnique"),
"message" => "This User already exists.",
"on" => "create"
),
)
I have two problems.
The first: This validation rule also triggers on update action, implemented as
public function edit($id = null) {
if (!$this->User->exists($id)) {
throw new NotFoundException(__('Invalid User'));
}
if ($this->request->is(array('post', 'put'))) {
if ($this->User->save($this->request->data)) {
$this->Session->setFlash(__('Update done.'));
return $this->redirect(array('action' => 'index'));
} else {
$this->Session->setFlash(__('The user can't be saved.'));
}
} else {
$options = array('conditions' => array('User.' . $this->User->primaryKey => $id));
$this->request->data = $this->User->find('first', $options);
}
}
But I wrote "on" => "create", so why it triggers also on update?
The second problem:
If the validation rule only triggers on create, how can I manage, to trigger an validation error, if someone change the name, lastname and birthday like an other user in the Database? Then the unique validation rule should be triggered.

Remove the 'on' => 'create'. (You want to validate in both events).
Modify your custom validation rule to this
public function checkUnique() {
$condition = array(
"User.name" => $this->data["User"]["name"],
"User.lastname" => $this->data["User"]["lastname"],
"User.birthday" => $this->data["User"]["birthday"]
);
if (isset($this->data["User"]["id"])) {
$condition["User.id <>"] = $this->data["User"]["id"];
//your query will be against id different than this one when
//updating
}
$result = $this->find("count", array("conditions" => $condition));
return ($result == 0);
}

Related

How can i skip unique field in Yii2?

I have a unique field that I check when editing or adding a new training course. But, for some reason, when I enter a value in a field, it does not show me a hint that the field is already taken.
In addition, I need to do this: when I change the values and did not change this unique field, but left it as it is, then the validor should not swear that the field is already taken.
Thank.
InfCourses Model:
public function rules()
{
return [
[['name', 'short_description', 'price', 'favorite', 'active', 'course_order', 'link'], 'required'],
[['price', 'active'], 'integer'],
[['favorite'], 'string'],
[['name', 'short_description', 'link'], 'string', 'max' => 255],
[['active'], 'exist', 'skipOnError' => true, 'targetClass' => InfStatuses::className(), 'targetAttribute' => ['active' => 'id']],
[['course_order'], 'integer', 'min' => 1],
[
['course_order'], 'unique',
'targetAttribute' => ['course_order'],
'filter' => ['!=', 'id', Yii::$app->request->get('id')],
],
];
}
Validator in InfCoursesController:
public function actionValidate()
{
$model = new InfCourses();
if (Yii::$app->request->isAjax && $model->load(Yii::$app->request->post())) {
Yii::$app->response->format = Response::FORMAT_JSON;
return ActiveForm::validate($model);
}
}
Part of form code:
<?php $form = ActiveForm::begin([
'enableAjaxValidation' => true,
'validationUrl' => 'validate',
'options' => [
'data-pjax' => true,
]
]); ?>
Your validation is simply incorrect. You're using Yii::$app->request->get('id') in your rules, which is probably main source of your problems. Model should not access request or web user component directly - it breaks MVC pattern. Also putting values directly in rules in this way may give you unexpected results. You should check what query is generated by this validator, because it is hard to guess what is happening with such twisted rule.
But it may be easier to fix actionValidate() and distinguish between validating of new record and validating existing record:
public function actionValidate($id = null) {
if (empty($id)) {
$model = new InfCourses();
} else {
$model = $this->findModel($id);
}
if (Yii::$app->request->isAjax && $model->load(Yii::$app->request->post())) {
Yii::$app->response->format = Response::FORMAT_JSON;
return ActiveForm::validate($model);
}
}
Then you can limit your unique rule to:
[['course_order'], 'unique'],
Validator will be smart enough to detect that it is validating existing record and will not report unchanged fields values as duplicates. You just need to provide record ID in this action URL.
Well... I cut code below to each action: create/update.
if (Yii::$app->request->isAjax && $model->load(Yii::$app->request->post())) {
Yii::$app->response->format = Response::FORMAT_JSON;
return ActiveForm::validate($model);
}
Then remove validationUrl from form component. Inside model i make this rule [['course_order'],'unique']... Working fine...

How to exclude a perticular field from unique validation in edit mode in cakephp3.0 validation

I want to validate a field called survey_id which is an input from user for uniqueness. It is working properly and giving the correct response when adding the new record, but when I tried to edit this record it is giving an error [unique] => Provided value already exist. So what I want is to exclude the survey_id of the current record from uniqueness check and if user input some other value for survey_id it should check for uniqueness search.
Currently I am using the CakePHP 3.0 validation with on create validation. Here is the validation rule that I am using:
validator
->requirePresence('survey_id', __('msg_required'))
->notEmpty('survey_id', __('msg_required'))
->maxlength('survey_id', 32, __('msg_maxlength'))
->add('survey_id', 'unique', ['rule' => ['validateUnique',['id']], 'provider' => 'table', 'message' => 'Provided value already exist', 'on'=>'create']);
return $validator;
Is there anything wrong with this code?
Thanks in advance.
`
It will work with this validation rule
$validator
->requirePresence('survey_id', __('msg_required'))
->notEmpty('survey_id', __('msg_required'))
->maxlength('survey_id', 32, __('msg_maxlength'))
->alphaNumeric('survey_id', __('msg_surveyid_format'))
->add('survey_id', 'custom', [
'rule' => function ($value, $context) {
if (!empty($context['data']['projectId'])) { $values = array($context['data']['projectId']); } else { $values = array(); }
$data = $this->getSurveyId($value, $values);
return (!empty($data)) ? false : true;
},
'message' => __('msg_surveyid_exsist')]);
return $validator;
}
public function getSurveyId($surveyId = null, $exclude = null) {
$where = array('p.survey_id' => $surveyId);
if (!empty($exclude) && is_array($exclude)) {
$where[] = array('p.id NOT IN' => $exclude);
}
return $this->db->newQuery()
->select('*')
->from(['p' => 'projects'])
->where($where)
->execute()
->fetch('assoc');
}

In CakePHP3 how do I create a custom model rule which validates that a time is after another time in the same table?

Columns (both datatype time):
Start
End
End cannot be before start.
$validator
->requirePresence('end', 'create')
->notEmpty('end')
->add('end', [
'time' => [
'rule' => 'time',
'message' => 'end can only accept times.'
],
'dependency' => [
'rule' => [$this, 'endBeforeStart'],
'message' => 'end can not be before start.'
],
]);
If it is a PUT request which only contains end, the model will need to query the existing record to compare against start. If it is a PUT which contains both then it need to validate against the intended new parameter.
How does cakePHP3 do this?
private function endBeforeStart($fieldValueToBeValidated, $dataRelatedToTheValidationProcess)
{
//What goes here?
}
I can't seem to find any examples of doing this online.
I'm not quite sure and haven't tested it, but maybe this gives you some hints:
$validator
->add('end', [
'endBeforeStart' => [
'rule' => function ($value, $context) {
// If it's a POST (new entry):
if ( $context['newRecord'] == '1' ) {
// Do your comparison here
// Input values are e.g. in $context['data']['starttime']
// If end is before start:
return false;
}
// If it's a PUT (update):
else {
// If starttime is not in $context['data']['starttime']
// check for the old value in $getOldEntry
$getOldEntry = $this->getOldEntry( $context['data']['id'] );
// And do your comparison here...
// If end is before start:
return false;
}
return true;
},
'message' => 'end can not be before start.' ],
])
public function getOldEntry($id = null) {
return $this->get($id);
}
I'm also not sure if the last function has to be private or public...

Cakephp validation issue

Hey guys got an issue with Cakephp validation..
I want to know why is partytwo validation going straight to false?
Here is my Relationship model:
<?php
class Relationship extends AppModel{
var $name='Relationship';
public $useTable = 'relationships_users';
public $primaryKey = 'id';
var $validate = array(
'date' => array(
'rule' => array('datevalidation', 'systemDate'),
'message' => 'Current Date and System Date is mismatched'
),
'partytwo'=>array(
'partytwoExists'=>array(
'rule'=> 'userExists',
'message'=>'That username doesnt exist.'
)
)
);
function datevalidation( $field=array(), $compare_field=null ) {
if ($field['date'] > $compare_field)
return TRUE;
else
return FALSE;
}
function userExists($check) {
$userExists= $this->find('count', array('conditions'=>$check));
if($userExists == 1) {
return TRUE;
}else{
return FALSE;
}
}
...
According to the CakePHP book Adding Your Own Validation Methods section, a custom rule that wrote like this
'rule' => array('datevalidation', 'systemDate')
means Cake will runs your datevalidation method like this:
$valid = $Relationships->datevalidation(array(
'date' => 'some user input value'
), 'systemDate');
With the same fashion,
'rule' => array('userExists')
causes Cake to run
$valid = $Relationships->userExists(array(
'partytwo' => 'some user input value'
));
(Simulated calls. Actual calling is using dispatchMethod at line 3155 of Model.php)
So you are most probably need to rewrite your datevalidation method. Furthermore, your code
$userExists= $this->find('count', array('conditions'=>$check));
$userExists can return you a number larger or equal to 0. Your logic is wrong if it returns 2 or more. Consider Model::hasAny instead. It could be the reason why it is always validated as false for your case.

CodeIgniter conditional validation

Can someone please help me with this conditional field validation in CodeIgniter?
Trying to collect some customer data and if the user selects 'Yes' in the mail radio buttons, some of the fields such as (address, city, postcode etc) becomes mandatory.
I have the CodeIgniter form validation code in config/form_Validation.php as below:
$config = array ( 'customer/new_customer' => array
(
array ( 'field' => 'firstName', 'label' => 'First Name', 'rules' => 'required' ),
array ( 'field' => 'lastName', 'label' => 'Last Name', 'rules' => 'required'),
array ('field' => 'mail', 'label' => 'Mail', 'rules' => 'required' ),
array ('field' => 'address', 'label' => 'Address','rules' => ''),
//other fields here
)
);
I have the code below in the controller to add/edit customer:
function new_customer()
{
$customer_id = $this->input->post('customer_id');
if ($this->form_validation->run() == FALSE)
{
if(($customer_id != "X") && ($customer_id != "")){
$data['add_or_edit'] = "add";
return $this->edit_customer($customer_id, 'add');
}else {
$data['title'] = "New Customer";
$data['add_or_edit'] = 'add';
$this->load->view('customer_form_view',$data);
}
}else{
$data['firstName'] = $this->input->post('firstName');
$data['lastName'] = $this->input->post('lastName');
if($this->input->post('mail') == "Yes")
{
$data['address'] = $this->input->post('address');
$data['city'] = $this->input->post('city');
//other fields
}
if(($customer_id == 'X') || ($customer_id == ''))
{
//add new customer
$customer_id = $this->customers_model->insertCustomer($data);
redirect("/customer/customerList");
}else{
//edit the customer matching the customerID
$this->customers_model->editCustomer($customer_id, $data);
redirect("/customer/customerlist");
}
}//end validation if
}//end function
I am not sure how to make the address, postcode and other fields as 'required' if the user selects 'Yes' in the mail option.
It will be great if someone can help me with this.
Thanks a lot
Regards,
PS
You could use callback function, as your mail option validation rule... Something like
$this->form_validation->set_rules('mail', 'Mail', 'callback_mail_check');
Then in callback function, you could have something like
function mail_check($str)
{
if ($str == 'YES')
{
$this->form_validation->set_message('mail_check', 'You need to fill other fields.');
return FALSE;
}
else
{
return TRUE;
}
}

Resources