I have a required ISBN field that I would like to change the generic error messages for. I have set up the custom error messages and they work. The filters are part of the getInputFilterSpecification() function of the models fieldSet which validates and shows the custom error messages correctly:
use Zend\Form\Element;
use Zend\Form\Fieldset;
use Zend\Validator;
use Zend\InputFilter\InputFilterProviderInterface;
use Zend\Stdlib\Hydrator\ClassMethods as ClassMethodsHydrator;
class BookItemFieldset extends Fieldset implements InputFilterProviderInterface
{
public $Types;
public function __construct($itemName, $Types)
{
parent::__construct($itemName, $Types);
$this->Types = $Types;
// .. Other fields
$bookISBN = new Element\Number('bookISBN');
$bookISBN->setLabel('Book ISBN ')
->setAttribute('id', 'bookISBN');
// .. more fields
$this->add($bookISBN);
}
public function getInputFilterSpecification()
{
return array(
'bookISBN' => array(
'required' => true,
'validators' => array(
new Validator\NotEmpty(array(
'setMessage'=> 'ISBN is Required'
)
),
new Validator\Isbn(array(
'setMessage'=> 'ISBN is Invalid'
)),
)
),
//... more input filters
);
}
}
But when the field is left blank. Both "ISBN is Invalid" and "ISBN is required" messages appear.
Is there a way to only show the required error message if the field is left blank?
something like the following:
'bookISBN' => array(
'required' => array(
'required' => true,
'setMessage' => 'ISBN is required' // Only this shows if field is empty
),
'validators' => array(
new Validator\Isbn(array(
'setMessage'=> 'ISBN is Invalid' // only this shows if the input is invalid
)),
)
),
Thanks.
You need to break the chain by setting it to true, which by default is set to false by Zend. Can you please try and see if below code works.
public function getInputFilterSpecification()
{
return array(
'bookISBN' => array(
'required' => true,
'validators' => array(
array(
'name' => 'not_empty',
'break_chain_on_failure' => true,
'options' => array(
'messages' => array(
\Zend\Validator\NotEmpty::IS_EMPTY => 'ISBN is required',
),
),
),
new Validator\Isbn(array(
'setMessage'=> 'ISBN is Invalid'
)),
)
),
//... more input filters
);
}
or may be it is 'NotEmpty'. I can't test it out.
public function getInputFilterSpecification()
{
return array(
'bookISBN' => array(
'required' => true,
'validators' => array(
array(
'name' => 'NotEmpty',
'break_chain_on_failure' => true,
),
new Validator\Isbn(array(
'setMessage'=> 'ISBN is Invalid'
)),
)
),
//... more input filters
);
}
Related
Having a registration zend form in view looks like this :
<?php
$form = $this->form;
if(isset($form)) $form->prepare();
$form->setAttribute('action', $this->url(NULL,
array('controller' => 'register', 'action' => 'process')));
echo $this->form()->openTag($form);
?>
<dl class="form-signin">
<dd><?php
echo $this->formElement($form->get('name_reg'));
echo $this->formElementErrors($form->get('name_reg'));
?></dd>
<dd><?php
echo $this->formElement($form->get('email_reg'));
echo $this->formElementErrors($form->get('email_reg'));
?></dd>
<dd><?php
echo $this->formElement($form->get('password_reg'));
echo $this->formElementErrors($form->get('password_reg'));
?></dd>
<dd><?php
echo $this->formElement($form->get('confirm_password_reg'));
echo $this->formElementErrors($form->get('confirm_password_reg'));
?></dd>
<br>
<dd><?php
echo $this->formElement($form->get('send_reg'));
echo $this->formElementErrors($form->get('send_reg'));
?></dd>
<?php echo $this->form()->closeTag() ?>
And RegisterController as following.
<?php
namespace Test\Controller;
use Zend\Mvc\Controller\AbstractActionController;
use Zend\View\Model\ViewModel;
use Zend\Session\Container;
use Test\Form\RegisterForm;
use Test\Form\RegisterFilter;
use Test\Form\LoginFormSm;
use Test\Form\LoginFilter;
use Test\Model\User;
use Test\Model\UserTable;
class RegisterController extends AbstractActionController
{
public function indexAction()
{
$this->layout('layout/register');
$form = new RegisterForm();
$form_sm = new LoginFormSm();
$viewModel = new ViewModel(array(
'form' => $form,
'form_sm' =>$form_sm,
));
return $viewModel;
}
public function processAction()
{
$this->layout('layout/register');
if (!$this->request->isPost()) {
return $this->redirect()->toRoute(NULL,
array( 'controller' => 'index'
)
);
}
$form = $this->getServiceLocator()->get('RegisterForm');
$form->setData($this->request->getPost());
if (!$form->isValid()) {
$model = new ViewModel(array(
'form' => $form,
));
$model->setTemplate('test/register/index');
return $model;
}
// Creating New User
$this->createUser($form->getData());
return $this->redirect()->toRoute(NULL, array (
'controller' => 'auth' ,
));
}
protected function createUser(array $data)
{
$userTable = $this->getServiceLocator()->get('UserTable');
$user = new User();
$user->exchangeArray($data);
$userTable->saveUser($user);
return true;
}
}
Also a RegisterForm where are declared all variables shown in index. Also RegisterFilter as following:
<?php
namespace Test\Form;
use Zend\InputFilter\InputFilter;
class RegisterFilter extends InputFilter
{
public function __construct()
{
$this->add(array(
'name' => 'email_reg',
'required' => true,
'filters' => array(
array(
'name' => 'StripTags',
),
array(
'name' => 'StringTrim',
),
),
'validators' => array(
array(
'name' => 'EmailAddress',
'options' => array(
'domain' => true,
'messages' => array(
\Zend\Validator\EmailAddress::INVALID_FORMAT => 'Email address format is invalid'
),
),
),
array(
'name' => 'AbstractDb',
'options' => array(
'domain' => true,
'messages' => array(
\Zend\Validator\Db\AbstractDb::ERROR_RECORD_FOUND => 'Current Email Already registered'
),
),
),
),
));
$this->add(array(
'name' => 'name_reg',
'required' => true,
'filters' => array(
array(
'name' => 'StripTags',
),
array(
'name' => 'StringTrim',
),
),
'validators' => array(
array(
'name' => 'StringLength',
'options' => array(
'encoding' => 'UTF-8',
'min' => 2,
'max' => 140,
),
),
),
));
$this->add(array(
'name' => 'password_reg',
'required' => true,
'validators' => array(
array(
'name' => 'StringLength',
'options' =>array(
'encoding' => 'UTF-8',
'min' => 6,
'messages' => array(
\Zend\Validator\StringLength::TOO_SHORT => 'Password is too short; it must be at least %min% ' . 'characters'
),
),
),
array(
'name' => 'Regex',
'options' =>array(
'pattern' => '/[A-Z]\d|\d[A-Z]/',
'messages' => array(
\Zend\Validator\Regex::NOT_MATCH => 'Password must contain at least 1 digit and 1 upper-case letter'
),
),
),
),
));
$this->add(array(
'name' => 'confirm_password_reg',
'required' => true,
'validators' => array(
array(
'name' => 'Identical',
'options' => array(
'token' => 'password_reg', // name of first password field
'messages' => array(
\Zend\Validator\Identical::NOT_SAME => "Passwords Doesn't Match"
),
),
),
),
));
}
}
Problem
All i need is to throw a message when somebody tries to register and that e-mail is already registered. Tried with \Zend\Validator\Db\AbstractDb and added following validator to email in RegisterFilter as following
array(
'name' => 'AbstractDb',
'options' => array(
'domain' => true,
'messages' => array(
\Zend\Validator\Db\AbstractDb::ERROR_RECORD_FOUND => 'Current Email Already registered'
),
),
),
But that seems not to work.
Question: Is there a way to implement this validator in RegisterController?
Additional.
I've deleted from RegisterFilert validator and put inside Module.ph
'EmailValidation' => function ($sm) {
$dbAdapter = $sm->get('Zend\Db\Adapter\Adapter');
$validator = new RecordExists(
array(
'table' => 'user',
'field' => 'email',
'adapter' => $dbAdapter
)
);
return $validator;
},
And call it from RegisterController
$validator = $this->getServiceLocator()->get('EmailValidation');
if (!$validator->isValid($email)) {
// email address is invalid; print the reasons
$model = new ViewModel(array(
'error' => $validator->getMessages(),
'form' => $form,
));
$model->setTemplate('test/register/index');
return $model;
}
And when i use print_r inside view to check for it shows.
Array ( [noRecordFound] => No record matching the input was found ).
I want just to echo this 'No record matching the input was found'.
Fixed
Modified in RegisterController as following
$validator = $this->getServiceLocator()->get('EmailValidation');
$email_ch = $this->request->getPost('email_reg');
if (!$validator->isValid($email_ch)) {
// email address is invalid; print the reasons
$model = new ViewModel(array(
'error' => 'Following email is already registered please try another one',
'form' => $form,
));
$model->setTemplate('test/register/index');
return $model;
}
I had compared
if (!$validator->isValid($email_ch))
Before with nothing and that's why i needed to add first
$email_ch = $this->request->getPost('email_reg');
You don't have to do that in your controller, just try this in your RegisterFilter class :
First : Add $sm as parameter of the _construct() method :
public function __construct($sm) {....}
Second: Replace e-mail validation by this one :
$this->add ( array (
'name' => 'email_reg',
'required' => true,
'validators' => array(
array(
'name' => 'Zend\Validator\Db\NoRecordExists',
'options' => array(
'table' => 'user',
'field' => 'email',
'adapter' => $sm->get ( 'Zend\Db\Adapter\Adapter' ),
'messages' => array(
NoRecordExists::ERROR_RECORD_FOUND => 'e-mail address already exists'
),
),
),
),
)
);
When you call the RegisterFilter don't forget to pass the serviceLocator as parameter in your controller :
$form->setInputFilter(new RegisterFilter($this->getServiceLocator()));
Supposing you have this in your global.php File (config\autoload\global.php) :
'service_manager' => array(
'factories' => array(
'Zend\Db\Adapter\Adapter' => 'Zend\Db\Adapter\AdapterServiceFactory',
),
),
To your previous request, ("I want just to echo this 'No record matching the input was found'")
$validator = $this->getServiceLocator()->get('EmailValidation');
$email_ch = $this->request->getPost('email_reg');
if (!$validator->isValid($email_ch)) {
// email address is invalid; print the reasons
foreach ($validator->getMessages() as $message) {
$msg = $message;
}
$model = new ViewModel(array(
'error' => $msg,
'form' => $form,
));
$model->setTemplate('test/register/index');
return $model;
}
I just created a form in cakephp and I would like to do a validation for one of the field.
The form will only be submitted if this particular field is left empty.
Here's a my particular field
<?php echo
$this->Form->input('MyForm.fieldA', array(
'type' => 'text',
'label' => '',
'class' => 'span3 detector-form',
))
?>
And my validation code:
public $validate = array(
'fieldA' => array(
'rule' => 'blank',
'on' => 'submit',
'message' => 'Failed authorize',
'last' => true
),
);
P/s. I tried using
public $validate = array(
'fieldA' => array(
'rule' => 'blank',
'on' => 'create',
'message' => 'Failed authorize',
'last' => true
),
);
But the 'create' sounds like only worked when the field is created.So I changed to 'submit' for a testing purpose.
I tried to use rule''=> 'Empty' as well but the validation doesn't work. Or is there any other alternative rules that I can use to reach this goal?
I'm struggeling with my cakePHP validation
Scenario:
In my DB I have one table "alliances" and one "federations". In "federations" connections between alliances are stored. "alliances" has got stupid cols like id, name, etc.. federations is like this:
id, request_alliance, accept_alliance, type, requested_at, accepted_at
where request_alliance and accept_alliance are FK to alliances, type is 1 or 2.
So my model looks like this:
class Federation extends AppModel
{
// Bundarten:
// 1 - NAP
// 2 - Bund
public $displayField;
var $belongsTo = array('Alliance_requesting' => array('className' => 'Alliance', 'foreignKey' => 'request_alliance'),
'Alliance_accepting' => array('className' => 'Alliance', 'foreignKey' => 'accept_alliance'));
public $validate = array(
'request_alliance' => array('required' => true, 'allowEmpty' => false),
'accept_alliance' => array('required' => true, 'allowEmpty' => false),
'type' => array('required' => true, 'allowEmpty' => false, 'rule' => array('between', 1, 2))
);
}
Alliance (created by an former partner, I only added the $hasMany)
class Alliance extends AppModel{
var $hasMany = array(
'Federation_requesting' => array('className' => 'Federation', 'foreignKey' => 'request_alliance', 'dependent' => true),
'Federation_accepting' => array('className' => 'Federation', 'foreignKey' => 'accept_alliance', 'dependent' => true)
);
public $validationDomain = 'alliance';
public $validate = array(
'tag' => array(
'uniqueTag' => array(
'rule' => 'isUnique',
'message' => 'Alliance tag already in use'),
'between' => array(
'rule' => array('between', 2, 15),
'message' => 'Alliance tag must betwenn %d to %d characters')),
'name' => array(
'rule' => array('between', 3, 30),
'message' => 'Alliance name must between %d to %d characters'),
'image_url' => array(
'rule' => 'url',
'message' => 'Alliance picture must be a valid URL',
'allowEmpty' => true),
'homepage' => array(
'rule' => 'url',
'message' => 'Homepage must be a valid URL',
'allowEmpty' => true));
}
So far I've written a view to add a new federation between two alliances. The controller for this
class FederationsController extends AppController
{
var $name = 'Federations';
var $components = array('Message');
var $uses = array('Alliance', 'Federation');
// Requesting new federation
function add()
{
if(empty($this->data['Federation'])) {
$message = __d('federation', "Invalid Request");
$this->notice($message);
return $this->redirect(Path::overall_highscore_alliances_path());
}
$requesting_alliance_id = $this->data['Federation']['req_alliance_id'];
$req_alliance = $this->Alliance->get($requesting_alliance_id);
if(!$req_alliance) {
return $this->redirect(Path::overall_highscore_alliances_path());
}
if(!$this->Alliance->isCurrentUserDiplomat($req_alliance)) {
$message = __d('federation', "Only the diplomat is allowed to modify federations.");
$this->notice($message);
return $this->redirect(Path::alliance_path($requesting_alliance_id));
}
$accepting_alliance_id = $this->data['Federation']['acc_alliance_id'];
$acc_alliance = $this->Alliance->get($accepting_alliance_id);
if(!$acc_alliance) {
$message = __d('federation', "The target alliance for this federation doesn't exists.");
$this->notice($message);
return $this->redirect(Path::alliance_path($requesting_alliance_id));
}
$type = $this->data['Federation']['type'];
$requested_at = time();
$this->Federation->create();
$values = array('request_alliance' => $requesting_alliance_id,
'accept_alliance' => $accepting_alliance_id,
'type' => $type,
'requested_at' => $requested_at);
$saved = $this->Federation->save($values, true, array('request_alliance', 'accept_alliance', 'type', 'requested_at'));
$name = h($acc_alliance['name']);
$message = $saved ? __d('federation', "Federation with '%s' successfully requested.", $name) : '';
$this->notice($message);
$this->errors($this->Federation->validationErrors);
$this->redirect(Path::alliance_path($requesting_alliance_id));
}
}
When I try to add a new federation it the above function is called and a new row is stored inside the DB with the correct values. But the page still shows me the following errors
Could not find validation handler 1 for request_alliance
Could not find validation handler for request_alliance
Could not find validation handler 1 for accept_alliance
Could not find validation handler for accept_alliance
I can't imagine that my validation is not done, because some hours ago I had a mistake which leads to empty fields and I got the correct validation message that this field can't left blank.
Can anyone tell me where I do the mistake which leads to these errors and how to correct it?
Thanks in advance!
There is no validation rule definition
From the question, compare:
'request_alliance' => array(
'required' => true,
'allowEmpty' => false
),
With
'type' => array(
'required' => true,
'allowEmpty' => false,
'rule' => array('between', 1, 2)
)
In the first case there is no rule, rule is a mandatory key if you define the validation rules as an array.
Use the notEmpty validation rule
From the validation rules defined, it looks like there's a misunderstanding. You probably want the notEmpty validation rule:
'request_alliance' => array(
'rule' => 'notEmpty'
)
If you want to ensure that the field is present in all saves, use the required key
'request_alliance' => array(
'rule' => 'notEmpty',
'required' => true
)
There is no need to define the allowEmpty key, as it is the same as the notEmpty validation rule if false, and illogical if defined as true.
First : sorry for my long message.
I'm trying to learn Fuel, but I have some problems with Fieldset class and Orm.
I've create a Model which extends ORM, in order to get an automatic generated form, according to my database.
My Model
class Model_Product extends \Orm\Model
{
protected static $_table_name = 'products';
protected static $_properties = array(
'id' => array(
'data_type' => 'int'
),
'name' => array(
'data_type' => 'varchar',
'label' => 'Name',
'validation' => array(
'required', 'trim', 'max_length'=>array(30), 'min_length'=>array(3)
),
),
'description' => array(
'data_type' => 'varchar',
'label' => 'Description',
'validation' => array(
'max_length' => array(290)
),
'form' => array(
'type' => 'textarea'
),
),
'price' => array(
'data_type' => 'integer',
'label' => 'Price',
'validation' => array(
'required', 'trim', 'valid_string' => array('numeric','dots')
),
),
'pic' => array(
'data_type' => 'varchar',
'label' => 'Path to the pic',
'validation' => array(
'required', 'trim'
),
),
'registered' => array(
'data_type' => 'date',
'label' => 'Registration date',
'validation' => array(
'required', 'trim'
) //'valid_string' => array('numeric','dashes')
),
);
} //end of class Model_Product
Then I create the controller which will validate the form.
My function from the controller
function action_add()
{
$fieldset = Fieldset::forge('add_product')->add_model('Model_Product')->repopulate();
$form = $fieldset->form();
$form->add('submit', '', array('type' => 'button', 'value' => 'Add item', 'class' => 'button-link' ));
$validation = $fieldset->Validation();
if($validation->run() === true)
{
$fields = $fieldset->validated();
//create a new Product, with validated fields
$product = new Model_Product;
$product->name = $fields['name'];
$product->description = $fields['description'];
$product->price = $fields['price'];
$product->pic = $fields['pic'];
$product->registered = $fields['registered'];
try
{
//if the product is successfully inserted in the database
if($product->save())
{
Session::set_flash('success', 'Product successfully added !');
\Response::redirect('products/product_details/'.$product->id);
}
}
catch(Exception $e)
{
Session::set_flash('error', 'Unable to save the product into the database !'.$e->getMessage());
}
}
//If the validation doesn't pass
else
{
Session::set_flash('error', $fieldset->show_errors());
}
$this->template->set('content', $form->build(), false);
} // end of method add()
My first question :
How and where in my function from controller can i add a 'fieldset' tag with a specific class, in order to 'beautify' my auto-generated form ?
Let's say
<fieldset class="add_product">
Second question :
What do I have to do in order to correctly validate de 'price' field, because in MySQL is set as decimal(5,2), but when I'm trying to validate with my actual validation rule, it doesn't pass (it works only with integer values Ex.: 42, but not with decimal Ex.: 42.35). I have tried to change the type from 'integer' to 'double', but it doesn't work .
If you can point to some specific documentation regarding my problems, which I possible didn't read yet, please do feel free.
Gabriel
I can answer the first question To change the automatically generated form you will need to copy fuel/core/config/form.php to the fuel/app/config directory and edit this file to suit your needs.
I wrote a custom validation method inside my Submission model that basically allows a blank input field, but once someone enters something in it, it'll validate the data entered.
The validation inside my Submission Model looks like this (All other validation rules are working except for 'description'):
var $validate = array(
'title' => array(
'title' => array(
'rule' => 'notEmpty',
'required' => true,
'allowEmpty' => false,
'message' => 'Please enter a title'
),
'minLength' => array(
'rule' => array('minLength', 5),
'message' => 'Please make your title longer'
),
'maxLength' => array(
'rule' => array('maxLength', 300),
'message' => 'Your title needs to be shorter'
),
),
'description' => array(
'checkDescription' => array(
'rule' => array('validateDescription'),
'message' => 'Description must be greater than 5 characters'
),
),
'source' => array(
'source' => array(
'rule' => 'notEmpty',
'required' => true,
'allowEmpty' => false,
'message' => 'Enter a valid source URL'
),
'website' => array(
'rule' => 'url',
'message' => 'Please enter a valid source URL'
),
)
);
My method which is also in my Submission model (below the above code) is:
public function validateDescription($data) {
if(empty($data['Submission']['description']))
return true;
if((strlen($data['Submission']['description'])) <= 5)
return false;
}
I'm not sure why this isn't working at all. In my view, I've got this to display the error:
if ($form->isFieldError('Submission.description'))
echo ($form->error('Submission.description', null, array('class' => 'error')));
The only reason I'm trying to do this, is because using the normal validation with required => false and allowEmpty => true along with a minLength and maxLength validation rule weren't behaving how I intended.
Any help would be greatly appreciated! :)
The $data variable passed into the validation method only contains array($fieldname => $value). You're also not returning true for strings over length of 5. Your method should look like this:
public function validateDescription(array $data) {
$value = current($data);
return !$value || strlen($value) > 5;
}