Zend Form Validator Callback: How to exclude a username? - zend-framework

I have written one function in User Mapper class userExist($username) which return true if same username does not exist in my database table. Now I want to use this function in the Add_User_Form class which extends Zend_Form.
I want to use:
$username = $this->createElement('text','username');
$username->setLabel('Username:')
->addValidator('Callback', true, array('callback'=>userExist???));
Can I use mapper class userExist($username) function as callback function?
Please let me know if I am doing something wrong with my implementation. I just want to show error messages if a user is already registered and prevent form submission.
Thank you.

Instead of using a callback validator, you can use a standard validator:
$element->addValidator('Db_NoRecordExists', true, array('users', 'username'))
Db_NoRecordExists allows you to check whether a record exists in a specific table of your database.
If a user want to edit his profile, you might want to check if a record exists except itself. Then you should use the same validator as follow:
$where = array('users', 'username', array('field' => 'username', 'value' => $username));
$element->addValidator('Db_NoRecordExists', true, $where)
In these example, users is the name of your database table. username is your column name and $username the username you want to exclude.

Bit late but this may be a possible answer to your question.
Date Element example:
I created a date element with a custom Callback validator that calls a method in the same form class.
// Date picker from
$datePickerFrom = new Zend_Form_Element_Text('date_picker_from_date');
$datePickerFrom->setRequired(true)
->setDecorators($this->elementDecorators)
->setLabel('Valid From')
->addFilter('StripTags')
->addFilter('StringTrim')
->setValue(date("d/m/Y",strtotime(Zend_Date::now())))
->addValidator('Callback',true, array('callback' => array($this, 'dateCheck'));
$datePickerFrom->getValidator('Callback')->setMessage("'%value%' Cannot be in the past.");
$this->addElement($datePickerFrom);
If you have your own class with custom validators in it then you can do this:
$myValidatorClass = new \SomeValidaotrClass();
->addValidator('Callback',true, array('callback' => array($myValidatorClass, 'dateCheck'));
In the same form class I created the custom validation method:
public function dateCheck($value, $elements, $option)
{
$now = new DateTime();
$fromDate = new DateTime(str_replace('/', '-', $elements['date_picker_from_date']));
if ($fromDate < $now) {
$this->getElement('date_picker_from_date')->getValidator('Callback')->setMessage("'%value%' Cannot be in the past.");
return false;
} else (SomethingElse) {
$this->getElement('date_picker_from_date')->getValidator('Callback')->setMessage("Some other message.");
}
return true;
}
Or if you are using your own class:
class SomeValidaotrClass
{
public function dateCheck($value, $elements, $option)
{
// Validation code...
}
}
If the methods aren't public the Callback will not find them.

Related

Laravel one to many relationship save method empty attributes

I am trying to save a relationship with the Laravel save method:
public function storeContact(Request $request)
{
$user = User::firstOrNew(['email' => $request->input('email')]);
$user->save();
$message = new App\Message([
'message' => $request->input('remarks')
]);
$user->message()->save($message);
}
var_dump($request->all) confirms both fields are available in the request.
All relations work. hasOne, belongsTo are configured in the models. The relation is saved like expected (but message field is empty)
When I var_dump($message), there are no attributes in the collection.
I already tried fillable and guarded on the models without any effect. These should not be necessary for the save method though because this uses a full Eloquent model instance.
What am I missing here??
I think I found the culprit.
When I use a __construct on a model it fails. When I instantiate a model with a __construct no variables are passes as attributes. Even if the __construct is empty.
Test with __construct method
class Message extends Model
{
protected $connection = 'system';
public function __construct()
{
}
// ...
}
$message = new App\Message(['remarks' => 'Test remarks']);
var_dump(message) // does NOT contain attributes!
Test2 without __construct method
class Message extends Model
{
protected $connection = 'system';
// ...
}
$message = new App\Message(['remarks' => 'Test remarks']);
var_dump(message) // does contain attributes!
This looks like a bug in Laravel to me.

Reuse Zend 2 form with captcha for editing

I have a form for some element creation with captcha.
It works fine.
But I want use this form in Admin page too, and there should be no captcha.
When I try to submit form I have an error
["captcha"] => array(1) {
["isEmpty"] => string(36) "Value is required and can't be empty"
}
How can I reuse this form without captcha? Is there another method without extending another new form?
Solved.
Just create Filter
use Zend\InputFilter\Input;
use Zend\InputFilter\InputFilter;
class FreeLessonFilter extends InputFilter
{
public function __construct()
{
$this->addElements();
}
protected function addElements()
{
$captcha = new Input('captcha');
$captcha->setRequired(TRUE);
$this->add($captcha);
}
}
And than use it in Admin page controller, but not in Index page
$this->defaultForm = new FreeLessonForm();
$filter = new FreeLessonFilter();
$filter->get('captcha')->setRequired(false);
$this->defaultForm->setInputFilter($filter);
By default it required, but in admin - not.

Yii2: how to use custom validation function for activeform?

In my form's model, I have a custom validation function for a field defined in this way
class SignupForm extends Model
{
public function rules()
{
return [
['birth_date', 'checkDateFormat'],
// other rules
];
}
public function checkDateFormat($attribute, $params)
{
// no real check at the moment to be sure that the error is triggered
$this->addError($attribute, Yii::t('user', 'You entered an invalid date format.'));
}
}
The error message doesn't appear under the field in the form view when I push the submit button, while other rules like the required email and password appear.
I'm working on the Signup native form, so to be sure that it is not a filed problem, I've set the rule
['username', 'checkDateFormat']
and removed all the other rules related to the username field, but the message doesn't appear either for it.
I've tried passing nothing as parameters to checkDateFormat, I've tried to explicitly pass the field's name to addError()
$this->addError('username', '....');
but nothing appears.
Which is the correct way to set a custom validation function?
Did you read documentation?
According to the above validation steps, an attribute will be
validated if and only if it is an active attribute declared in
scenarios() and is associated with one or multiple active rules
declared in rules().
So your code should looks like:
class SignupForm extends Model
{
public function rules()
{
return [
['birth_date', 'checkDateFormat'],
// other rules
];
}
public function scenarios()
{
$scenarios = [
'some_scenario' => ['birth_date'],
];
return array_merge(parent::scenarios(), $scenarios);
}
public function checkDateFormat($attribute, $params)
{
// no real check at the moment to be sure that the error is triggered
$this->addError($attribute, Yii::t('user', 'You entered an invalid date format.'));
}
}
And in controller set scenario, example:
$signupForm = new SignupForm(['scenario' => 'some_scenario']);
Try forcing the validation on empty field
['birth_date', 'checkDateFormat', 'skipOnEmpty' => false, 'skipOnError' => false],
Also, make sure you don't assign id to your birth_date field in your view.
If you do have id for your birth_date, you need to specify the selectors
<?= $form->field($model, 'birth_date', ['selectors' => ['input' => '#myBirthDate']])->textInput(['id' => 'myBirthDate']) ?>
To make custom validations in yii 2 , you can write custom function in model and assign that function in rule.
for eg. I have to apply password criteria in password field then I will write like this in model.
public function rules()
{
return [
['new_password','passwordCriteria'],
];
}
public function passwordCriteria()
{
if(!empty($this->new_password)){
if(strlen($this->new_password)<8){
$this->addError('new_password','Password must contains eight letters one digit and one character.');
}
else{
if(!preg_match('/[0-9]/',$this->new_password)){
$this->addError('new_password','Password must contain one digit.');
}
if(!preg_match('/[a-zA-Z]/', $this->new_password)){
$this->addError('new_password','Password must contain one character.');
}
}
}
}
You need to trigger $model->validate() somewhere if you are extending from class Model.
I stumbled on this when using the CRUD generator. The generated actionCreate() function doesn't include a model validation call so custom validators never get called. Also, the _form doesn't include and error summary.
So add the error summary to the _form.
<?= $form->errorSummary($model); ?>
...and add the validation call - $model->validate() - to the controller action
public function actionCreate()
{
$model = new YourModel();
if ($model->load(Yii::$app->request->post()) && $model->validate()) {...
Although it's an old post i thought I should answer.
You should create a Custom Validator Class and to create a validator that supports client-side validation, you should implement the yii\validators\Validator::clientValidateAttribute() method which returns a piece of JavaScript code that performs the validation on the client-side. Within the JavaScript code.
You may use the following predefined variables:
attribute: the name of the attribute being validated.
value: the value being validated.
messages: an array used to hold the validation error messages for
the attribute.
deferred: an array which deferred objects can be pushed into
(explained in the next subsection).
SO that means you can use messages array to push your messages to the client end on runtime within the javascript code block in this method.
I will create a class that includes dummy checks that could be replaced the way you want them to. and change the namespace according to your yii2 advanced or basic.
Custom Client-side Validator
namespace common\components;
use yii\validators\Validator;
class DateFormatValidator extends Validator{
public function init() {
parent::init ();
$this->message = 'You entered an invalid date format.';
}
public function validateAttribute( $model , $attribute ) {
if ( /*SOME CONDITION TO CHECK*/) {
$model->addError ( $attribute , $this->message );
}
}
public function clientValidateAttribute( $model , $attribute , $view ) {
$message = json_encode ( $this->message , JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE );
return <<<JS
if ($("#DATE-1").val()=="" || $("#DATE-2").val() =="") {
messages.push($message);
}
JS;
}
}
and then inside your model SigupForm add the rule
['birth_date', 'common\components\DateFormatValidator'],
Deferred Validation
You can even add ajax calls inside the clientValidateAttribute function and on the base of the result of that ajax call you can push message to the client end but you can use the deferred object provided by yii that is an array of Deferred objects and you push your calls inside that array or explicitly create the Deferred Object and call its resolve() method.
Default Yii's deferred Object
public function clientValidateAttribute($model, $attribute, $view)
{
return <<<JS
deferred.push($.get("/check", {value: value}).done(function(data) {
if ('' !== data) {
messages.push(data);
}
}));
JS;
}
More about Deferred Validation
You need to render the model from controller. Without initializing the model in view. And in the controller you need to call the validate function
Are you sure the first parameter of addError shouldn't be like this
$this->addError(**'attribute'**, Yii::t('user', 'You entered an invalid date format.'));
I had common problem.
In your validation function:
public function checkDateFormat($attribute, $params)
{
// no real check at the moment to be sure that the error is triggered
$this->addError($attribute, Yii::t('user', 'You entered an invalid date format.'));
}
$params doesn`t get any value at all. It actually always equals to Null. You have to check for your attribute value in function:
public function checkDateFormat($attribute, $params)
{
if($this->birth_date == False)
{
$this->addError($attribute, Yii::t('user', 'You entered an invalid date format.'));
}
}
that`s how it worked for me.
If you don't use scenarios for your model, you must mark your atribute as 'safe':
['birth_date','safe'],
['birth_date', 'checkDateFormat'],
And, on the other hand, you can use this for date validation:
['birth_date','safe'],
[['birth_date'],'date', 'format'=>'php:Y-m-d'],
You can change format as you want.
**We should set attributes to the function to work with input value **
public function rules()
{
return [
['social_id','passwordCriteria'],
];
}
public function passwordCriteria($attribute, $params)
{
if(!empty($this->$attribute)){
$input_value = $this->$attribute;
//all good
}else{
//Error empty value
$this->addError('social_id','Error - value is empty');
}
}
Are you by any chance using client side validation? If you do then you have to write a javascript function that would validate the input. You can see how they do it here:
http://www.yiiframework.com/doc-2.0/guide-input-validation.html#conditional-validation
Another solution would be to disable client validation, use ajax validation, that should bring back the error too.
Also make sure that you have not overwritten the template of the input, meaning make sure you still have the {error} in there if you did overwrite it.
Your syntax on rules should be something like this man,
[['birth_date'], 'checkDateFormat']
not this
['birth_date', 'checkDateFormat']
So in your case, it should look like below
...
class SignupForm extends Model
{
public function rules()
{
// Notice the different with your previous code here
return [
[['birth_date'], 'checkDateFormat'],
// other rules
];
}
public function checkDateFormat($attribute, $params)
{
// no real check at the moment to be sure that the error is triggered
$this->addError($attribute, Yii::t('user', 'You entered an invalid date format.'));
}
}

Zend Framework: Form Validation - Please Fill In 'A' or 'B'

I'm writing an application with Zend Framework. I have a simple form that has two text boxes.
How do I add validation to the form so that at least one of the input boxes should be populated?
You could create a custom validator, something like:
class My_Validate_AtLeastOneNotEmpty implements Zend_Validate_Interface
{
protected $thisField;
protected $otherField;
protected $messages = array();
public function __construct($thisField, $otherField){
$this->thisField = $thisField;
$this->otherField = $otherField;
}
public function isValid($value, $context = null)
{
$validator = new Zend_Validate_NotEmpty();
if (!$validator->isValid($value) && !$validator->isValid($context['otherField'])){
$this->messages[] = 'At least one of the fields - '
. $thisField . ' or ' . $otherField
. ' - must be non-empty';
return false;
}
return true;
}
public function getMessages()
{
return $this->messages;
}
}
Then attach this validator to one of the elements, identifying both names:
// create the two fields
$fld1 = new Zend_Form_Element_Text('field1');
$fld2 = new Zend_Form_Element_Text('field2');
// add the validator to one of them, identifying both fields
$fld1->addValidator(new My_Validate_AtLeastOneNotEmpty('field1', 'field2'));
Typically, we would not need to identify the fieldname of the element to which we are attaching the validator. However, by passing both fieldnames to the validator, we can make the error message a little clearer for the user.
Create a radio group with two options, both unchecked. Hide the textfields. With an onclick event on the radio buttons you can show one of the textboxes. Now you can add a required validator to the radio buttons. A little workaround, but the result is the same.

Symfony: prevent form field from being saved

I have a form to change email, EmailChangeForm which extends the guard user form, sfGuardUserForm and uses two columns: email_address and password.
I want the form to check if the password is correct and if so, change the email to the new one.
My problem is that the form also saves the password field to the user object.
I know that since the password is checked, it cannot be changed in theory, but I still don't like it being re-saved with the new value from the form, so is there a way to make the form only save the email_address field?
I would suggest a sceleton like this :
class emailForm extends sfFrom {
public function configure(){
$this->widgetSchema['email'] = new sfWidgetFormInputText();
$this->widgetSchema['password'] = new sfWidgetFormInputPassword();
$this->validatorSchema['password'] = new myValidatorPassword();
}
}
class myValidatorPassword extends sfValidatorBase{
protected function doClean($value)
{
$clean = (string) $value;
// current user
$sf_guard_user = sfContext::getInstance()->getUser()->getGuardUser();
if($sf_guard_user)
{
// password is ok?
if ($sf_guard_user->checkPassword($value))
{
return $clean;
}
}
// Throw error
throw new sfValidatorError($this, 'invalid', array('value' => $value));
}
}
So in your action you can easily save the new password :
/***** snip *****/
if($this->form->isValid()){
// set and save new password to current user
$user = $this->getUser()->getGuardUser();
$user->setPassword($formValues["password"]);
$user->save();
/***** snip *****/
Of course this is a basic approach, improvements are always welcome :-)
First make sure you're using the useFields function in your EmailChangeForm class. With that you can define which fields you want to edit with your form (this is better than unset because if you add more fields you dont have to worry with useFields). Example:
$this->useFields(array(
'email'
));
DO NOT INCLUDE THE PASSWORD!
Second: In your template put an extra input field for your password with the same name schema (updatemail[password]).
Third: In your action before the $form->isValid method you add the following:
$params = $request->getParameter($form->getName();
unset($params['password'];
$form->bind($params), $request->getFiles($form->getName()));
if($form->isValid()) {...}
create the new form (editUserForm for example) that extend the base form and then unset the password by this code
class editUserForm extend baseForm{
public function configure(){
unset($this['password']);
}
}
all the name above is an example you must change it to your name.
I'm assuming there is only one field in EmailChangeForm, and that EmailChangeForm extends SfUserForm...
To eliminate all the fields except the email field add this to the configure method:
$this->useFields(array('email'));