How can i set the identical validator on a field without making it a required field in Zendframework 1 + allow the check of empty value.
here is the code
$this->addElement('password', 'password', array(
'label' => 'password',
'required' => false
),
));
$this->addElement('password', 'confirm', array(
'label' => 'confirm',
'required' => false,
'validators' => array(
array(
'validator' => 'Identical',
'options' => array(
'token' => 'password'
)
)
)
));
If the confirm element is set to 'required'=>false, the identical validator doesn't work if the value is empty.
You can't use Identical validator without required because in the isValid() method of Zend_Form_Element there is :
if ((('' === $value) || (null === $value))
&& !$this->isRequired()
&& $this->getAllowEmpty()
) {
return true;
}
and Zend_Form_Element_Password is an instance of Zend_Form_Element.
I think you have 3 possibilities:
Add required option in your confirm element, but you have a specific message required
Add allowEmpty option to false in your confirm element like this : 'allowEmpty' => false, instead of 'required' => false,
Create your own validator like the Cully Larson's answer. An other example it's the example #3 of the documentation about Writing validators. Otherwise, you can find more on the web. :)
I ended up writing my own validator for password confirmation:
class MyLib_Validate_PasswordConfirmation extends Zend_Validate_Abstract
{
const PASSWORD_MISMATACH = 'passwordMismatch';
protected $_messageTemplates = array(
self::PASSWORD_MISMATCH => 'This value does not match the confirmation value.'
);
/**
* Requires that 'password_confirm' be set in $context.
*/
public function isValid($value, $context = null)
{
$value = (string) $value;
$this->_setValue($value);
if (is_array($context)) {
if (isset($context['password_confirm'])
&& ($value == $context['password_confirm']))
{
return true;
}
} elseif (is_string($context) && ($value == $context)) {
return true;
}
$this->_error(self::PASSWORD_MISMATCH);
return false;
}
}
Related
I used zf2 authentication for authenticate user in my project.I saved Harib in my user table as user name but if i use my user name Harib then its accept or if i use harib then its not accept,i want to remove case sensitivity of user name so both Harib or harib access how i fix this?
Here is my code:
public function loginAction()
{
$this->layout('layout/login-layout.phtml');
$login_error = false;
$loginForm = new LoginForm();
$form_elements = json_encode($loginForm->form_elements);
if ($this->request->isPost()) {
$post = $this->request->getPost();
$loginForm->setData($post);
if ($loginForm->isValid()) {
$hashed_string = '';
if(
array_key_exists('hashed_input' , $post) &&
$post['hashed_input'] != '' &&
strpos(urldecode($this->params('redirect')) , 'programdetailrequest') !== false
) {
$hashed_string = $post['hashed_input'];
}
$data = $loginForm->getData();
$authService = $this->getServiceLocator()->get('doctrine.authenticationservice.odm_default');
$adapter = $authService->getAdapter();
$adapter->setIdentityValue($data['username']);
$adapter->setCredentialValue(md5($data['password']));
$authResult = $authService->authenticate();
if($authResult->isValid()){
$identity = $authResult->getIdentity();
if( is_object($identity) && method_exists($identity, 'getData') ){
$user_data = $identity->getData();
$authService->getStorage()->write($identity);
// for remeber checkbox
if ($post['rememberme']) {
$token = new UserToken();
$dm = $this->getServiceLocator()->get('doctrine.documentmanager.odm_default');
//if same user already running from other browser then remove previous token.
$check_token = $dm->getRepository('Admin\Document\UserToken')->findOneBy(array( "user_id.id" => $user_data['id'] ));
if (is_object($check_token) && !is_null($check_token)) {
$remove_token = $dm->createQueryBuilder('Admin\Document\UserToken')
->remove()
->field('id')->equals($check_token->id)
->getQuery()->execute();
}
//create token
$user = $dm->getRepository('Admin\Document\User')->findOneBy(array( "id" => $user_data['id'] ));
$token->setProperty('user_id', $user);
$token->setProperty('dataentered', new \MongoDate());
$dm->persist($token);
$dm->flush($token);
//create cookie
if(is_object($token) && property_exists($token, 'id')){
$time = time() + (60 * 60 * 24 * 30); // 1 month
setcookie('token', $token->getProperty('id'), $time, '/');
}
}
if ($user_data['user_type'] == 'onlinemarketer') {
$this->redirect()->toRoute('admin_program_meta');
} elseif ($user_data['user_type'] == 'bucharestofficemanager') {
$this->redirect()->toRoute('admin_program_detail_request');
} else {
if ($this->params('redirect') && urldecode($this->params('redirect')) !== '/logout/') {
$server_url = $this->getRequest()->getUri()->getScheme() . '://' . $this->getRequest()->getUri()->getHost().urldecode($this->params('redirect') . $hashed_string);
return $this->redirect()->toUrl($server_url);
}
return $this->redirect()->toRoute('admin_index');
}
}
} else {
$identity = false;
$login_error = true;
}
}
}
return new ViewModel(array(
'loginForm' => $loginForm,
'form_elements' =>$form_elements,
'login_error' => $login_error,
));
}
and here is my login form code:
<?php
namespace Admin\Form;
use Zend\Form\Form;
use Zend\Form\Element;
use Zend\InputFilter\InputFilterAwareInterface;
use Zend\InputFilter\InputFilter;
use Zend\InputFilter\Factory as InputFactory;
class LoginForm extends Form implements InputFilterAwareInterface
{
protected $inputFilter;
public $form_elements = array(
array(
'name' => 'username',
'attributes' => array(
'id' => 'username',
'type' => 'text',
'error_msg' => 'Enter Valid Username',
'data-parsley-required' => 'true',
'data-parsley-pattern' => '^[a-zA-Z0-9_\.\-]{1,50}$',
'data-parsley-trigger' => 'change'
),
'options' => array(
'label' => 'User Name'
),
'validation' => array(
'required'=>true,
'filters'=> array(
array('name'=>'StripTags'),
array('name'=>'StringTrim')
),
'validators'=>array(
array('name'=>'Regex',
'options'=> array(
'pattern' => '/^[a-z0-9_.-]{1,50}+$/', // contain only a to z 0 to 9 underscore, hypen and space, min 1 max 50
'pattern_js' => '^[a-zA-Z0-9_\.\-]{1,50}$'
)
)
)
)
),
array(
'name' => 'password',
'attributes' => array(
'id' => 'password',
'type' => 'password',
'error_msg' => 'Enter Valid Password',
'data-parsley-required' => 'true',
'data-parsley-pattern' => '^[a-zA-Z0-9_\.\-]{6,25}$',
'data-parsley-trigger' => 'change'
),
'options' => array(
'label' => 'Password'
),
'validation' => array(
'required' => true,
'filters'=> array(
array('name'=>'StripTags'),
array('name'=>'StringTrim')
),
'validators'=>array(
array('name'=>'Regex',
'options'=> array(
'pattern' => '/^[a-z0-9_.-]{6,25}+$/', // contain only a to z 0 to 9 underscore, hypen and space, min 1 max 50
'pattern_js' => '^[a-zA-Z0-9_\.\-]{6,25}$'
)
)
)
)
),
array(
'name' => 'hashed_input',
'attributes' => array(
'type' => 'hidden',
'id' => 'hashed_input',
'value' => ''
)
),
array(
'name' => 'rememberme',
'attributes' => array(
'value' => 1,
'id' => 'rememberme',
'type' => 'Checkbox'
),
'options' => array(
'label' => 'Remember Me',
'use_hidden_element' => false,
)
),
array(
'name' => 'submit',
'attributes' => array(
'type' => 'submit',
'value' => 'Log in',
'id' => 'submitbutton'
)
)
);
public function __construct()
{
parent::__construct('user');
$this->setAttribute('method', 'post');
$this->setAttribute('data-parsley-validate', '');
$this->setAttribute('data-elements', json_encode($this->form_elements));
$this->setAttribute('autocomplete', 'off');
for($i=0;$i<count($this->form_elements);$i++){
$elements=$this->form_elements[$i];
$this->add($elements);
}
}
public function getInputFilter($action=false)
{
if(!$this->inputFilter){
$inputFilter = new InputFilter();
$factory = new InputFactory();
for($i=0;$i<count($this->form_elements);$i++){
if(array_key_exists('validation',$this->form_elements[$i])){
$this->form_elements[$i]['validation']['name']=$this->form_elements[$i]['name'];
$inputFilter->add($factory->createInput( $this->form_elements[$i]['validation'] ));
}
}
$this->inputFilter = $inputFilter;
}
return $this->inputFilter;
}
}
how we remove case sensitivity of user name so both Harib or harib accepted?
Add a filter StringToLower in your loginform on the element user_id.
For this, the class that defines your loginform must implement InputFilterProviderInterface and you must add in the getInputFilterSpecification method as follows :
public function getInputFilterSpecification()
{
return [
'username' => [
'name' => 'username',
'required' => true,
'filters' => [
'name' => 'StringToLower',
'name'=>'StripTags',
'name'=>'StringTrim'
],
validators => [
[
'name'=>'Regex',
'options'=> [
'pattern' => '/^[a-z0-9_.-]{1,50}+$/',
'pattern_js' => '^[a-zA-Z0-9_\.\-]{1,50}$'
]
]
]
],
'password' => [
'name' => 'password',
'required' => true,
'filters' => [
array('name'=>'StripTags'),
array('name'=>'StringTrim')
],
'validators' => [
[
'name'=>'Regex',
'options'=> [
'pattern' => '/^[a-z0-9_.-]{6,25}+$/',
'pattern_js' => '^[a-zA-Z0-9_\.\-]{6,25}$'
]
]
]
]
];
}
So you are assured that the value returned in the post is in lowercase.
Since you're using MongoDB, you could use a regex to get the user name from the database.
Suggestion 1:
In your example that would be:
db.stuff.find( { foo: /^bar$/i } );
Suggestion 2:
You can Use $options => i for case insensitive search. Giving some possible examples required for string match.
Exact case insensitive string
db.collection.find({name:{'$regex' : '^string$', '$options' : 'i'}})
Contains string
db.collection.find({name:{'$regex' : 'string', '$options' : 'i'}})
Start with string
db.collection.find({name:{'$regex' : '^string', '$options' : 'i'}})
End with string
db.collection.find({name:{'$regex' : 'string$', '$options' : 'i'}})
Doesn't Contains string
db.collection.find({name:{'$regex' : '^((?!string).)*$', '$options' : 'i'}})
More about regex in MongoDb here: https://docs.mongodb.com/manual/reference/operator/query/regex/index.html
You may do this in two ways. Either you may create a custom authentication adapter or override a method of the default authentication adapter. I recommend that override that method which is easier than creating custom adapter.
So here is the method CredentialTreatmentAdapter::authenticateCreateSelect(). If you look up around 94 line (of zf 2.5) of that method from zend-authentication component then you would find the following line.
$dbSelect->from($this->tableName)
->columns(['*', $credentialExpression])
// See the making of where clause
->where(new SqlOp($this->identityColumn, '=', $this->identity));
Here we are going to make our changes. Now lets override that method by extending Zend\Authentication\Adapter\DbTable. We would make a where clause which would search for both Harib or harib therefore. See the following extended CustomDbTable::class.
<?php
namespace Define\Your\Own\Namespace;
use Zend\Authentication\Adapter\DbTable;
class CustomDbTable extends DbTable
{
protected function authenticateCreateSelect()
{
// build credential expression
if (empty($this->credentialTreatment) || (strpos($this->credentialTreatment, '?') === false)) {
$this->credentialTreatment = '?';
}
$credentialExpression = new SqlExpr(
'(CASE WHEN ?' . ' = ' . $this->credentialTreatment . ' THEN 1 ELSE 0 END) AS ?',
array($this->credentialColumn, $this->credential, 'zend_auth_credential_match'),
array(SqlExpr::TYPE_IDENTIFIER, SqlExpr::TYPE_VALUE, SqlExpr::TYPE_IDENTIFIER)
);
// Here is the catch
$where = new \Zend\Db\Sql\Where();
$where->nest()
->equalTo($this->identityColumn, $this->identity)
->or
->equalTo($this->identityColumn, strtolower($this->identity))
->unnest();
// get select
$dbSelect = clone $this->getDbSelect();
$dbSelect->from($this->tableName)
->columns(array('*', $credentialExpression))
->where($where); // Here we are making our own where clause
return $dbSelect;
}
}
Now custom authentication adapter is ready. You need to use this one inside the factory for authentication service instead of Zend\Authentication\Adapter\DbTable as follows
'factories' => array(
// Auth service
'AuthService' => function($sm) {
$dbAdapter = $sm->get('Zend\Db\Adapter\Adapter');
// Use CustomDbTable instead of DbTable here
$customDbTable = new CustomDbTable($dbAdapter, 'tableName', 'usernameColumn', 'passwordColumn', 'MD5(?)');
$authService = new AuthenticationService();
$authService->setAdapter($customDbTable);
return $authService;
},
),
All are now set. That overridden method should be called whenever you call this one in your controller method:
$authResult = $authService->authenticate();
This is not tested. So you may need to change things where you need. Please fix those if needed.
Hope this would help you!
I am trying to develop a form validation system. But the problem is even though there is no data given by users as required the validation passes. Can't figure out where the problem is.
This is my form validation class
class Validation
{
private $_passed = false,
$_errors = array(),
$_db = null;
public function __construct() {
$this->_db = DB::getInstance();
}
public function check($source, $items= array()) {
foreach ($items as $item => $rules) {
foreach ($rules as $rule => $rule_value) {
$value = $source[$item];
if ($rule === 'required' && empty($value)) {
$this->addError("{$item} is required");
} else {
}
}
}
if (empty($this->_errors)) {
$this->_passed = true;
}
return $this;
}
private function addError($error) {
$this->errors[] = $error;
}
public function errors() {
return $this->_errors;
}
public function passed() {
return $this->_passed;
}
}
And this is the form page containing Html form.
require_once 'core/init.php';
if (Input::exists()) {
$validate = new Validation();
$validation = $validate->check($_POST, array(
'username' => array(
'required' => true,
'min' => 2,
'max' => 20,
'unique' => 'users'
),
'password' => array(
'required' => true,
'matches' => 'password'
),
'password_again' => array(
'required' => true,
'min' => 6
),
'name' => array(
'required' => true,
'min' => 2,
'max' => 60
),
));
if ($validation->passed()) {
//register new user
echo "passed"; //this passes even though users provides no data
} else {
print_r($validation->errors());
}
}
So, all i get is echo passed on the screen even though user provide no data at all. It should throw the errors instead. Please help. Thanks
addError writes in $this->errors, while the other methods use $this->_errors. (with underscore). The _errors array will remain empty, so _passed will be set to true in this statement:
if (empty($this->_errors)) {
$this->_passed = true;
}
I have this in my class
$inputFilter->add(
$factory->createInput(array(
'name' => 'precio',
'required' => true,
'validators' => array(
array(
'name' => 'Float',
'options' => array(
'min' => 0,
),
),
),
))
);
When I enter a Integer number like 5 or 78 everything seems ok, but when I try with a number like 5.2 I got the next error message
The input does not appear to be a float
The decimal character in the Float Validator class depends on the locale used in the application. Try adding the locale as an option like this:
$factory->createInput( array(
'name' => 'precio',
'required' => true,
'validators' => array(
array(
'name' => 'Float',
'options' => array(
'min' => 0,
'locale' => '<my_locale>'
),
),
),
) );
If you don't set the locale, the Float class gets the intl.default_locale defined in php.ini
You can write own validator like this:
class Currency extends \Zend\Validator\AbstractValidator {
/**
* Error constants
*/
const ERROR_WRONG_CURRENCY_FORMAT = 'wrongCurrencyFormat';
/**
* #var array Message templates
*/
protected $messageTemplates = array(
self::ERROR_WRONG_CURRENCY_FORMAT => "Vaule is not valid currency format. (xxx | xxx.xx | xxx,xx)",
);
/**
* {#inheritDoc}
*/
public function isValid($value) {
$exploded = null;
if (strpos($value, '.')) {
$exploded = explode('.', $value);
}
if (strpos($value, ',')) {
$exploded = explode(',', $value);
}
if (!$exploded && ctype_digit($value)) {
return true;
}
if (ctype_digit($exploded[0]) && ctype_digit($exploded[1]) && strlen($exploded[1]) == 2) {
return true;
}
$this->error(self::ERROR_WRONG_CURRENCY_FORMAT);
return false;
}
}
And filter value:
class Float extends \Zend\Filter\AbstractFilter {
public function filter($value) {
$float = $value;
if($value){
$float = str_replace(',', '.', $value);
}
return $float;
}
}
you could use callback validator.
use Zend\Validator\Callback;
$callback = new Callback([$this, 'validateFloat']);
$priceCallBack->setMessage('The value is not valid.');
then, somewhere in the class you need to have this function.
public function validateFloat($value){
return (is_numeric($value));
}
And finally inside the form, add this validator,
for eg.
$this->inputFilter->add([
'name'=>'pro_price',
'required' => true,
'validators'=>[$callback]
]);
I have a model Interesttype where i want two fields to be validated one should not be more than the other and none should be less than a particular set value. Here is my model.
class Interesttype extends AppModel
{
public $primaryKey = 'int_id';
public $displayField = 'int_name';
public $hasMany= array(
'Loan' => array(
'className' => 'Loan',
'foreignKey' => 'lon_int_id'
)
);
public $validate = array(
'int_name'=> array(
'rule' => 'notEmpty',
'allowEmpty' => false,
'message' => 'The interest type name is required.'
),
'int_active'=>array(
'rule'=>array('boolean'),
'allowEmpty'=>false,
'message'=>'Please select the status of this interest type'
),
'int_max'=> array(
'numeric'=>array(
'rule' => 'numeric',
'allowEmpty' => false,
'message' => 'Please specify a valid maximum interest rate.'
),
'comparison'=>array(
'rule' => array('comparison','>',1000),
'allowEmpty' => false,
'message' => 'The Maximum interest rate cannot be less than the special rate.'
),
'checklimits'=>array(
'rule' => array('checkRateLimits','int_min'),
'allowEmpty' => false,
'message' => 'The Maximum interest rate cannot be less than the minimum rate.'
)
),
'int_min'=> array(
'numeric'=>array(
'rule' => 'numeric',
'allowEmpty' => false,
'message' => 'Please specify a valid minimum interest rate.'
),
'comparison'=>array(
'rule' => array('comparison','>',1000),
'allowEmpty' => false,
'message' => 'The Minimum interest rate cannot be less than the special rate.'
))
);
function checkRateLimits($maxr,$minr){
if($maxr>=$minr){
return true;
}
else{
return false;
}
}
}
the above model validates my forms well except that one check will not be done, it will not check if the maximum interest rate is indeed more than or equal to the minimum inerest rate.
Where am i going wrong on the validation?
You'll have to add your own Validation Method to make this possible. Here's a pretty generic example that makes use of Validation::comparison() and supports all of its operators (>, <, >=, <=, ==, !=, isgreater, isless, greaterorequal, lessorequal, equalto, notequal) as the second argument in the options array and a field name to compare the validated value to as a third parameter.
class MyModel extends AppModel
{
/* Example usage of custom validation function */
public $validate = array(
'myfield' => array(
'lessThanMyOtherField' => array(
'rule' => array('comparisonWithField', '<', 'myotherfield'),
'message' => 'Myfield value has to be lower then Myotherfield value.',
),
),
);
/* Custom validation function */
public function comparisonWithField($validationFields = array(), $operator = null, $compareFieldName = '') {
if (!isset($this->data[$this->name][$compareFieldName])) {
throw new CakeException(sprintf(__('Can\'t compare to the non-existing field "%s" of model %s.'), $compareFieldName, $this->name));
}
$compareTo = $this->data[$this->name][$compareFieldName];
foreach ($validationFields as $key => $value) {
if (!Validation::comparison($value, $operator, $compareTo)) {
return false;
}
}
return true;
}
}
This is generic, so you could throw the function in your AppModel if you want to make use of it in multiple models in your App. You could also make this a Behavior to share it between different models.
I won't help you to do your code however I will suggest you to read the following backery article http://bakery.cakephp.org/articles/aranworld/2008/01/14/using-equalto-validation-to-compare-two-form-fields
<?php
class Contact extends AppModel
{
var $name = 'Contact';
var $validate = array(
'email' => array(
'identicalFieldValues' => array(
'rule' => array('identicalFieldValues', 'confirm_email' ),
'message' => 'Please re-enter your password twice so that the values match'
)
)
);
function identicalFieldValues( $field=array(), $compare_field=null )
{
foreach( $field as $key => $value ){
$v1 = $value;
$v2 = $this->data[$this->name][ $compare_field ];
if($v1 !== $v2) {
return FALSE;
} else {
continue;
}
}
return TRUE;
}
}
?>
modify the code I think it should be easy from here.
I don't think you can achieve this with directly inserting it into the validate array in a model. You can however specify your own validation function. An example is given here (look at the model code):
Example custom validation function
In your Model your validate should be field name with rule pointing to the function you want to run.
And "None should be less than a particular set value" I would use the "range" validation as seen below. The example will accept any value which is larger than 0 (e.g., 1) and less than 10
public $validate = array(
'maxr'=>array(
'Rate Limits'=>array(
'rule'=>'checkRateLimits',
'message'=>'Your Error Message'
),
'Greater Than'=>array(
'rule'=>array('range', -1, 11),
'message'=>'Please enter a number between 0 and 10'
)
),
);
In the function you pass $data which is the fields being passed. If $data doesn't work try $this->data['Model']['field'] ex($this->data['Model']['maxr'] >= $this->data['Model']['minr'])
function checkRateLimits($data){
if ( $data['maxr'] >= $data['minr'] ) {
return true;
} else {
$this->invalidate('minr', 'Your error message here');
return false;
}
}
Your Form would be something like
echo $this->Form->create('Model');
echo $this->Form->input('maxr');
echo $this->Form->input('minr');
echo $this->Form->end('Submit');
On my bootstrap I don't have a class, it's a simple php file:
I have added there:
$loader = Zend_Loader_Autoloader::getInstance ();
$loader->setFallbackAutoloader ( true );
$loader->suppressNotFoundWarnings ( false );
//resource Loader
$resourceLoader = new Zend_Loader_Autoloader_Resource(array(
'basePath' => APPLICATION_PATH,
'namespace' => '',
));
$resourceLoader->addResourceType('validate', 'validators/', 'My_Validate_');
$loader->pushAutoloader($resourceLoader);
Then, in application/validators I have:
class My_Validate_Spam extends Zend_Validate_Abstract {
const SPAM = 'spam';
protected $_messageTemplates = array(
self::SPAM => "Spammer"
);
public function isValid($value, $context=null)
{
$value = (string)$value;
$this->_setValue($value);
if(is_string($value) and $value == ''){
return true;
}
$this->_error(self::SPAM);
return false;
}
}
In my form constructor I have:
$this->addElement(
'text',
'honeypot',
array(
'label' => 'Honeypot',
'required' => false,
'class' => 'honeypot',
'decorators' => array('ViewHelper'),
'validators' => array(
array(
'validator' => 'Spam'
)
)
)
);
And finally on my view I have:
<dt><label for="honeypot">Honeypot Test:</label></dt>
<dd><?php echo $this->form->honeypot;?></dd>
Despite all this, I receive my form data, either by filling or not filling that text field.
What am I missing here ?
Thanks a lot in advance.
Thats expected behaviour. $honeypot is a form-element. Now, let's say you have a form $hp_form where $honeypot is one of the elements assigned.
Now, in your controller simply use something like:
if ($hp_form->isValid($this->getRequest()->getPost())) {
// do something meaningful with your data here
}
Probably you also want to check, if you display the form for the first time or if the user submitted the form:
if ($this->getRequest()->isPost() &&
false !== $this->getRequest()->getPost('submit_button', false)) {
if ($hp_form->isValid($this->getRequest()->getPost())) {
// do something meaningful with your data here
}
}
...assuming that your submit button has the id 'submit_button'.
Hope this helps
Bye,
Christian
replace :
if (is_string($value) and $value == ''){
return true;
}
by :
if (strlen($value) > 0)
{
return true;
}