I need to validate input field depending on options i pass to buildForm()
And i have this code i glued together from googling around.
However in controller
$form->isValid()
passes with values that are outside the range:
$builder->add('limit', 'integer', array(
'constraints' => [
new Assert\Range(array(
'min' => $options['min'],
'max' => $options['max'],
'minMessage' => 'min error message',
'maxMessage' => 'max error message',
))
],
));
How do I dynamically validate the input?
UPDATE:
Symfony2 validation using Assert annotation does not work same issue. Will have to write a custom validator.
You can do something basic like this:
if ($option['condition'] === 'whatever') {
$limtiConstraint = new Assert\Range(array(
'min' => $options['min'],
'max' => $options['max'],
'minMessage' => 'min error message',
'maxMessage' => 'max error message',
));
} else {
// $limtiConstraint = 'other constraint with other options'
}
$builder->add('limit', 'integer', array(
'constraints' => [$limtiConstraint]
));
Or if you want to modify the limits, you can use the ternary operator:
'min' => $options['condition'] ? $options['min'] : $options['other_min'],
Related
I'm trying to submit a form using PHP and the mailchimp 2.0 api.
I'm getting an error that says:
FNAME must be provided
My form has a field for the first name:
<input type="text" name="fname">
So it must have something to do with the way I am handling it the php side.
Here is the bit of php that handles FNAME:
$result = $MailChimp->call('lists/subscribe', array(
'id' => 'myid',
'email' => array( 'email' => $_POST['email']),
'FNAME' => $_POST['fname'],
'LNAME' => $_POST['lname'],
'double_optin' => false,
'update_existing' => true,
'replace_interests' => false
));
I'm not sure if I'm forming the array correctly or not.
By the way, I'm using this wrapper, but I think my error has to do with how I create $result and not the wrapper.
https://github.com/drewm/mailchimp-api
Any help would be appreciated.
Thanks!
Peep the example at the bottom of the page you linked to. I've pasted it here:
$result = $MailChimp->call('lists/subscribe', array(
'id' => 'b1234346',
'email' => array('email'=>'davy#example.com'),
'merge_vars' => array('FNAME'=>'Davy', 'LNAME'=>'Jones'),
'double_optin' => false,
'update_existing' => true,
'replace_interests' => false,
'send_welcome' => false,
));
print_r($result);
Your merge vars (ex. FNAME and LNAME) need to be in its own array. So, add a 'merge_vars' in your array and create an array that contains your field's merge variables.
I am trying to have a Checkbox "Agree TOS".
If the Checkbox is not checked, I want to put out a Flash Message.
How do I do this?
My View:
<?php
echo $form->create('Item', array('url' => array_merge(array('action' => 'find'), $this->params['pass'])));
echo $form->input('Search', array('div' => false));
echo $form->submit(__('Search', true), array('div' => false));
echo $form->checkbox('tos', array('label' => false, 'value'=>1)).' Agree TOS';
echo $form->error('tos');
echo $form->end();
?>
My Model:
var $check = array(
'tos' => array(
'rule' => array('comparison', 'equal to', 1),
'required' => true,
'allowEmpty' => false,
'on' => 'index',
'message' => 'You have to agree TOS'
));
This seems working for me. Hope it will help.
In model:
'tos' => array(
'notEmpty' => array(
'rule' => array('comparison', '!=', 0),
'required' => true,
'message' => 'Please check this box if you want to proceed.'
)
In view:
<?php echo $this->Form->input('tos', array('type'=>'checkbox', 'label'=>__('I confirm I have read the privacy statement.', true), 'hiddenField' => false, 'value' => '0')); ?>
Model
'agreed' => array(
'notempty' => array(
'rule' => array('comparison', '!=', 0),//'checkAgree',
'message' => ''You have to agree TOS'',
'allowEmpty' => false,
'required' => true,
'last' => true, // Stop validation after this rule
'on' => 'signup', // Limit validation to 'create' or 'update' operations
),
),
View
<?php echo $this->Form->input('agreed',array('value'=>'0'),array('type'=>'checkbox', 'label'=>'Agree to TOS')); ?>
basically, you add the rule "notEmpty" for this field to the public $validate array of the model.
this way an error will get triggered on Model->validates() if the checkbox has not been checked.
maybe some overhead in your case, but if you happen to use it more often try a DRY (dont repeat yourself) approach. you can also use a behavior for this to use this clean and without tempering too much with the model/controller:
// view form
echo $this->Form->input('confirmation', array('type'=>'checkbox', 'label'=>__('Yes, I actually read it', true)));
and in the controller action
// if posted
$this->Model->Behaviors->attach(array('Tools.Confirmable'=>array('field'=>'confirmation', 'message'=>'My custom message')));
$this->Model->set($this->data);
if ($this->Model->validates()) {
// OK
} else {
// Error flash message here
}
1.x:
https://github.com/dereuromark/tools/blob/1.3/models/behaviors/confirmable.php
for 2.x:
https://github.com/dereuromark/cakephp-tools/blob/2.x/Model/Behavior/ConfirmableBehavior.php
3.x: https://github.com/dereuromark/cakephp-tools/blob/master/src/Model/Behavior/ConfirmableBehavior.php
details:
http://www.dereuromark.de/2011/07/05/introducing-two-cakephp-behaviors/
I believe you need try save it into your model to catch your tos rules. I should do something like =
if(!$mymodel->save()){
// catch error tos.
}
$this->ModelName->invalidFields() returns an array of fields that failed validation.
You could try and search this for the tos field, and output a message if the key exists.
~untested (I'm not sure off the top of my head the exact structure of the invalidFields return array.
$failed_fields = $this->ModelName->invalidFields();
if(array_key_exists('tos', $failed_fields)) {
$this->Session->setFlash('Please accept the terms and conditions');
}
you don't even have to have validation rule for tos, just check that at the controller before saving the data.
if($this->data['Model']['tos']==1){
// save data
}else{
//set flash
}
I've been at this most of the day now, and I cannot get this working for the life of me (well I can get it 1/2 working but not fully correctly).
Basically, I am trying to use Validation on a search form field like so:
if(isset($search['ApplicantAge']) && !empty($search['ApplicantAge'])) {
if ($this->Plan->validates()) {
$ApplicantAge = $search['ApplicantAge'];
}
}
And here is my model code:
...
'ApplicantAge' => array(
'required' => true,
'allowEmpty' => false,
'rule' => 'numeric',
'message' => 'A valid Age is required. Please enter a valid Age.'),
...
The validation is working BUT when I enter a number (numeric), it displays my error! And when it's blank NO error displays, and when I enter letters it seems to work : ( ??
Does anyone know a trick to this odd behavior?
Try using the 'notEmpty' rule instead of the required/allowEmpty stuff.
'ApplicantAge' => array(
'applicant-age-numeric'=> array(
'rule' => 'numeric',
'message' => 'A valid Age is required. Please enter a valid Age.'
),
'applicant-age-not-empty'=> array(
'rule' => 'notEmpty',
'message' => 'This field cannot be left blank'
)
)
firstly why are you using the field 'ApplicantAge' when the conventions say its should be lower case under scored?
to answer your question the best way to do validation like that is http://book.cakephp.org/view/410/Validating-Data-from-the-Controller
the other option is to do $this->Model->save($data, array('validate' => 'only'));
The manual did not assist me at all : (
But your suggestion on the validate => only array seems to have done the trick. This is how I got it working:
plans_controller.php
if (isset($search['ApplicantAge'])) {
$this->Plan->save($search, array('validate' => 'only'));
if ($this->Plan->validates($this->data)) {
$ApplicantAge = $search['ApplicantAge'];
}
}
plan.php (model)
var $validate = array(
'ApplicantAge' => array(
'applicant-age-numeric' => array(
'rule' => 'numeric',
'message' => 'A valid Age is required. Please enter a valid Age.'),
'applicant-age-not-empty' => array(
'rule' => 'notEmpty',
'message' => 'This field cannot be left blank'),
),
Now, if no data is entered in the ApplicateAge field, the proper message is displayed. And if a non-numeric is entered, the correct message is also displayed.
This was a lot more challenging than I thought it would be!
For the record, I'll make a correction to my earlier accepted post. Little did I know the validate => only on the save() was actually still saving data to my plans table.
I was able to get it working using set(). Here is the code that completely solved the problem:
plans_controller.php
if (isset($search['ApplicantAge'])) {
$this->Plan->set($this->data);
if ($this->Plan->validates()) {
$ApplicantAge = $search['ApplicantAge'];
}
}
plan.php (model):
var $validate = array(
'ApplicantAge' => array(
'applicant-age-numeric' => array(
'rule' => 'numeric',
'message' => 'A valid Age is required. Please enter a valid Age.'),
'applicant-age-not-empty' => array(
'rule' => 'notEmpty',
'message' => 'This field cannot be left blank'),
)
How to make file uploading as optional with validation?
The code below validates even if i didn't selected any file.
I want to check the extension only if i selected the the file.
If i am not selecting any file it should not return any validation error.
class Catalog extends AppModel{
var $name = 'Catalog';
var $validate = array(
'name' => array(
'rule' => '/^[a-z0-9 ]{0,}$/i',
'allowEmpty' => false,
'message' => 'Invalid Catalog name'
),
'imageupload' => array(
'rule' => array('extension',array('jpeg','jpg','png','gif')),
'required' => false,
'allowEmpty' => true,
'message' => 'Invalid file'
),
);
}
thanks in advance
"I assign $this->data['Catalog']['image'] = $this->data['Catalog']['imageupload']['name'];"
So by the time you save your data array, it looks something like this I assume:
array(
'image' => 'foobar',
'imageupload' => array(
'name' => 'foobar',
'size' => 1234567,
'error' => 0,
...
)
)
Which means, the imageupload validation rule is trying to work on this data:
array(
'name' => 'foobar',
'size' => 1234567,
'error' => 0,
...
)
I.e. the value it's trying to validate is an array of stuff, not just a string. And that is unlikely to pass the specified validation rule. It's also probably never "empty".
Either you create a custom validation rule that can handle this array, or you need to do some more processing in the controller before you try to validate it.
Concept:
In Controller, before validating, or saving (which does validation automatically by default) check if any file is uploaded. If not uploaded, then unset validator for the file field.
Sample code:
Controller
// is any image uploaded?
$isNoFileUploaded = ($this->request->data['Model']['field_name']['error'] == UPLOAD_ERR_NO_FILE) ? true : false ;
if ($isNoFileUploaded) {
$this->Model->validator()->remove('field_name');
}
Notes:
This solution comes under preprocessing as one of the two alternative approaches (preprocessing in controller, custom validation in model) suggested by #deceze's answer
im doing a custom validation but it does not display error message when invalidated.
do you know where is the problem? I think the problem might be in the invalidate function. do you know how to set it up for the nested validation like this one?
var $validate = array(
'receiver' => array(
'maxMsg' => array(
'rule' => array('maxMsgSend'),
//'message' => ''
),
'notEmpty' => array(
'rule' => array('notEmpty'),
'message' => 'field must not be left empty'
))......
custom validation method in the model:
function maxMsgSend ( $data )
{
$id = User::$auth['User']['id'];
$count_contacts = (int)$this->Contact->find( 'count', array( 'conditions' =>array( 'and' =>array( 'Contact.contact_status_id' => '2',
'Contact.user_id' => $id))));
$current_credit = (int)$this->field( '3_credit_counter', array( 'id' => $id));
$max_allowed_messages = ($count_contacts >= $current_credit)? $current_credit: $count_contacts ;
if ($data>$max_allowed_messages)
{
$this->invalidate('maxMsg', "you can send maximum of {$max_allowed_messages} text messages.");
}
}
UPDATE: how is solved it.
i moved the the guts of the function to beforeValidate() in the model.
function beforeValidate($data) {
if (isset($this->data['User']['receiver']))
{
$id = User::$auth['User']['id'];
$count_contacts = (int)$this->Contact->find( 'count', array( 'conditions' =>array( 'and' =>array( 'Contact.contact_status_id' => '2',
'Contact.user_id' => $id))));
$current_credit = (int)$this->field( '3_credit_counter', array( 'id' => $id));
$max_allowed_messages = ($count_contacts >= $current_credit)? $current_credit: $count_contacts ;
if ($data>$max_allowed_messages)
{
$this->invalidate('receiver', "you can send maximum of {$max_allowed_messages} text messages.");
return false;
}
}
return true;
}
I think your maxMsgSend function still needs to return false if validation fails.
I think the problem is in your Model::maxMsgSend function. As written in the bakery, (http://bakery.cakephp.org/articles/view/using-equalto-validation-to-compare-two-form-fields), to build a custom validation rule (they want to compare two fields, but the concepts are the same), they write:
I return a false if the values don't match, and a true if they do.
Check out their code for the Model class, about half way down. In short, you don't need to call invalidate from within the custom validation method; you simply return true if it passes validation, and false if it doesn't pass validation.