I am querying database records to check if a username already exists this way:
$q = Doctrine_Query::create()
->from('Tcc_Model_User u')
->where('u.Username = ?', $input['Username']);
$object = $q->fetchOne();
if(is_object($object)) {
// not sure what to do here
} else {
// ok to record
What I cannot make it work is how to show a message that username is already taken next to the Username field of the submitted form. Could please anyone help me? Really appreciated any help. Thank you
EDITED:
Thanks to Drew010 I solved the above problem by adding:
$form->getElement('Username')->addError('Sorry, that username is already taken!');
in the first of the conditional statement. Thank again drew010.
Since you are using Zend_Form, there is a validator that can do this for you.
See Zend_Validate_DbNoRecordExists
They give an example for validating that a given username does not exist in the database.
Here is an example from one of my applications, the third validator checks that the username does not already exist:
$this->addElement('text', 'username', array(
'label' => 'User Name:',
'required' => true,
'validators' => array('NotEmpty'),
'decorators' => $this->elementDecorators,
'validators' => array(
array('StringLength', true, array('min' => 4,
'messages' => array(
Zend_Validate_StringLength::TOO_SHORT =>
'Account username must be at least %min% characters'
)
)),
array('Regex', true, array('pattern' => '/^\w(?:[\w\d\.\-_]+)(?:\w|\d)$/',
'messages' => array(
Zend_Validate_Regex::NOT_MATCH =>
"The username contained invalid characters"
)
)),
array('Db_NoRecordExists', true, array(
'table' => 'accounts', 'field' => 'username',
'messages' => array(
Zend_Validate_Db_NoRecordExists::ERROR_RECORD_FOUND =>
"An account with the username '%value%' is already registered"
)
))
)
));
Also, for future reference, if you do extended validation you can't do with a validator, you can add an error to a specific form element like this:
if ($something_was_wrong) {
$form->getElement('username')
->addError('Username already exists!');
}
I would create a separate Doctrine-based validator modeled on the Zend_Validate_Db_NoRecordExists validator and add that validator directly to the element
The validator:
class My_Validate_UsernameAvailable extends Zend_Validate_Abstract
implements Zend_Validate_Interface
{
const NOT_AVAILABLE = 'usernameAvailableNot';
protected $_messageTemplates = array(
self::NOT_AVAILABLE => "Username is not available",
);
public function isValid($value)
{
$q = Doctrine_Query::create()
->from('Tcc_Model_User u')
->where('u.Username = ?', $value);
if ($q->fetchOne()){
$this->_error(self::NOT_AVAILABLE);
return false;
}
return true;
}
}
Then in the form, attached to the username element:
'validators' => array(
// your other valiadators
array(new My_Validate_UsernameAvailable(), true),
),
Then there is no extra handling required in the controller.
Related
I am building my first Drupal 7 module and am having trouble with the screen to edit a fieldable entity. I am using field_attach_form and it is working great for all accept one field which is displaying the field default rather than the current content of that field for that entity.
I have a text field, a number field, a number of Boolean fields and the one list_text field which is failing.
Any ideas what I a doing incorrectly? Code below is what I think is needed but please do let me know if you need more.
Code to create the field in hook_enable:
if (!field_info_field('field_available')) {
$field = array (
'field_name' => 'field_available',
'type' => 'list_text',
'settings' => array(
'allowed_values' => array('No', 'Provisionally', 'Yes'),
),
);
field_create_field($field);
Code to create the instance, also in hook_enable:
if (!field_info_instance('appointments_status', 'field_available', 'appointments_status')) {
$instance = array(
'field_name' => 'field_available',
'entity_type' => 'appointments_status',
'bundle' => 'appointments_status',
'label' => t('Available?'),
'required' => TRUE,
'default_value' => array(array('value' => 'No')),
'description' => t('Set to No if appointments with this status make this slot unavailable, Provisionally means that it will only reserve a space temporarily'),
);
field_create_instance($instance);
This entity has only the one bundle with the same name as the entity.
The code to create the URL in hook_menu:
$items['admin/appointments/appointments_statii/%/edit'] = array(
'title' => 'Edit appointment status',
'description' => 'Edit the parameters of the selected status code',
'page callback' => 'drupal_get_form',
'page arguments' => array('appointments_status_edit_form',3),
'access arguments' => array('access administration pages'),
'type' => MENU_CALLBACK,
);
The form function is:
function appointments_status_edit_form($form, &$form_state) {
// Get the status id from the form_state args
$status_id = $form_state['build_info']['args'][0];
// Load the chosen status entity
$status = entity_load_single('appointments_status', $status_id);
// Set up the fields for the form
field_attach_form('appointments_status', $status, $form, $form_state);
$form['submit'] = array(
'#type' => 'submit',
'#value' => 'Save changes',
'#weight' => 99,
);
return $form;
}
I have used the Devel module's dpm to check that the data is loaded correctly by entity_load_single and it is.
Thanks
Rory
I have answered my own question!
I was also programmatically loading some entities and was not loading this field with the numbers that a list_text field stores, instead I was loading the visual text.
I used a metadata wrapper and the code looked like this:
$w_appointments_status->$appointments_availability= 'Yes';
I changed it to:
$w_appointments_status->$appointments_availability = 2;
In this example 'Yes' was the third allowed value - hence 2.
So the code in my question was in fact correct although I have since added 'widget' and 'formatter' parameters to the instance.
I am sorry if this got some of you scratching your heads thinking ' but that code is correct'!!
Regards
Rory
I am using 'MultivalidatableBehavior' in cakephp. Though it worked fine with previous version. But its not working with Cakephp 2.4.0 version. Is their any changes with this behaviour. Because When I use this code.
var $validationSets = array( 'ruleset mentioned in it. ');
Though my request go through the Behaviour class but its not putting validation on the desired field. I have also checked $this->request->data Its also valid and for same Model where ruleset has been written.
I tried to debug using die in my MultivalidatableBehavior class. Though during valiation my request falls in the function of setValidation(&$model, $rules = array()) {. Please suggest if it is compatible with greater version of cakephp2.3.
My tried code..
Model Code :
var $actsAs = array('Multivalidatable');
var $validationSets = array(
'login' => array(
'username' => array('required' => array('rule' => array('notEmpty'), 'message' => 'Username is required !!')),
'password' => array('required' => array('rule' => array('notEmpty'), 'message' => 'Password is required !!')),
),
);
Controller Call.
$this->User->setValidation('login'); Fields are also with same name.
Its not validating If I put if($this->User->setValidation('login')) it returns false.
Thanks to Piotr Beschel for helping me out. I shouldn't use any sort of behaviour. It can also be achieved without behaviour. My Model code would be
public $validationAdmin = array(
'adminLogin' => array(
'username' => array('required' => array('rule' => array('notEmpty'), 'message' => 'Please enter your username !!')),
'password' => array('required' => array('rule' => array('notEmpty'), 'message' => 'Please enter your password !!')),
),
'adminForgotPassword' => array(
'email' => array('required' => array('rule' => array('notEmpty'), 'message' => 'Please enter your email address !!'),
'email' => array('rule' => array('email'), 'message' => 'Please enter valid mail address')),
),
);
That is simply creating variable named $valdiationAdmin I have named it as admin to know following rule set is for admin.
My sample controller code would be.
$this->Admin->validate = $this->Admin->validationAdmin['adminLogin'];
Just this way. If form has data to save it would move like butter. In case data is just validating I have to force form to validate it(In case login where data is not saved).
if ($this->Admin->validates()) {
} else {
$errors = $this->Admin->validationErrors;
$this->set('validationErrors', $errors);
}
It would validate my form. Even add error message to form field in ctp file. I need not to write $this->form->error('fieldname');
Thanks again to all the viewer. Suggest me something if more better. I am thinking to validate data of other form if associated with one form, with same implementation. Help if you can.
The method
$this->User->setValidation('login')
only chooses which validation Set will be used.
Use:
if ($this->User->validates()) {
...
to get the result of the validation.
Here is the validation rule:
'name'=>array(
'Please enter customer\'s name'=>array(
'rule'=>'notEmpty',
'message'=>'Please enter customer\'s name'
),
'Unique' => array(
'rule' => array('nameIsUniqueForCompany'),
'message' => 'Customer with these initials already exists'
)
),
public function initialsAreUniqueForCompany($data){
$company_id = $this->data['Customer']['company_id'];
$initials = $this->data['Customer']['initials'];
if($this->find('first', array('conditions'=>array('initials'=>$initials, 'company_id'=>$company_id))))
{
return false;
}
return true;
}
The problem is that the rule is applied to a current object. Let's say I want to update a customer named 'ABC', but I just change his phone number. The application will then look if the name ABC already exists and it will ofcourse find it and will not update.
any ideas?
Your implementation is flawed:
By design your validation rule will always only work for add, not for edit.
On edit, you need to also take the current ID into consideration.
Check out my http://www.dereuromark.de/2011/10/07/maximum-power-for-your-validation-rules/ post. The part about uniqueness contains a working approach.
For you that means, using my enhanced unique validation method:
'name' => array(
'validate' => array(
'rule' => array('validateUnique', array('company_id')),
'message' => 'Customer with these initials already exists',
),
),
Basically, you always submit the id on edit (via hidden form field - automatically done by baked forms anyway), and remove it from the find result by using "id != current" in your conditions:
$this->alias . '.id !=' => $id
Reasoning: The currently edited record should not trigger the validation rule error, of course :)
You can pass something like this:
'email' => array(
'unique' => array(
'rule' => 'isUnique',
'message'=>'This e-mail has been used already',
'on'=>'create'
)
)
There is a node 'on'=>'create' in the array, which apply the rule only when the record has been created. Ofcourse there is also 'on'=>'update' and this will apply when the record is updated.
For update validation you should consider custom rule where you are checking row id to be different.
In your controller
public function edit($id = null) {
if ($this->request->isPut() || $this->request->isPost()) {
if ($this->YourModel->save()){
// do what ever you want
}
} else {
$this->request->data = $this->YourModel->read(null, $id)
}
}
and in your edit.ctp file
echo $this->Form->create('YourModel');
echo $this->Form->hidden('id');
please let me know if you found any problem, glad to help you.
you can try
'subscription_name' => array
(
'required'=>true,
'rule' => 'notEmpty',
'message' => 'Please enter Package name.',
'Unique'=>array(
'rule' => 'isUnique',
'message'=>'package name is already exit..'
)
),
I am developing simple guestbook module for one of my customers and as a spam prevention I decided to use an array of simple questions. For this I have to pick random question from the array and set the ID of this question (array element ID) in form so I can check the answer in submit handler. The problem is that the form is regenerated before the submit handler is evaluated so the random value changes.
Shortly: I am getting other value of $form_state['values']['queston_id'] in submit handler function that it is in form. Why is that and how can I change this?
Thanks a lot!
This is my module:
function gb_menu() {
$items = array();
$items['gb'] = array(
'title' => t('Guestbook'),
'description' => t('Guestbook page'),
'page callback' => 'gb_guestbook',
//'page arguments' => array('gb_guestbook'),
'access arguments' => array('view guestbook'),
);
return $items;
}
function gb_guestbook() {
dpm('generating page');
$page = NULL;
$page .= drupal_render(drupal_get_form('gb_guestbook_form'));
$page .= 'list of guestbook messages here';
return $page;
}
function gb_guestbook_form($form, &$form_state) {
$form['name'] = array(
'#type' => 'textfield',
'#title' => t('Name'),
'#description' => t('Please, enter your name.'),
);
$form['message'] = array(
'#type' => 'textarea',
'#cols' => 5,
'#title' => t('Message'),
'#description' => t('Please, enter your message.'),
);
$questions = gb_get_question();
$question_id = rand(0, count($questions)-1);
$question = $questions[$question_id];
$form['question_id'] = array(
'#type' => 'hidden',
'#value' => $question_id,
);
$form['spam'] = array(
'#title' => $question['question'],
'#description' => t('Please, answer the simple question so we know that you are a human.'),
'#type' => 'textfield',
);
$form['submit'] = array(
'#type' => 'submit',
'#value' => t('Submit'),
);
return $form;
}
function gb_guestbook_form_submit($form, &$form_state) {
// spam check
$values = $form_state['values'];
$questions = gb_get_question();
$answers = $questions[$values['question_id']]['answers'];
if (!in_array(strtolower($values['spam']), $answers)) {
drupal_set_message(t('You did not reply the answer correctly. Are you sure it was not typo?'), 'error');
}
else {
drupal_set_message(t('Thanks for the contribution!'), 'status');
// processing input
}
}
function gb_get_question() {
return array(
array (
'question' => t('What is the capital of Slovakia?'),
'answers' => array('bratislava'),
),
array (
'question' => t('What is the name of this band?'),
'answers'=> array('divozel'),
),
array (
'question' => t('What is the biggest town in east part of Slovakia?'),
'answers' => array('košice','kosice'),
),
);
}
You should probably do this check in gb_guestbook_form_VALIDATE instead.
I'd do this in a validation hook. Also another issue in the function gb_guestbook_form_submit
You get the questions but not the answers array and then you are trying to compare them. This could also be the source of your issue, you capture questions but not answers and then compare to the empty array of answers.
Wrap your random question generator in a conditional statement that checks if your question has been defined in the form yet.
$question_id = NULL;
$questions = gb_get_question();
if (!isset($form['question_id'])) {
$question_id = rand(0, count($questions)-1);
}
else {
$question_id = $form['question_id']['#value'];
}
$question = $questions[$question_id];
Not sure why my other answer isn't working for you, but here's another approach. The idea would be to store the question id in $form_state['storage'].
First, correct your form declaration so it passes in $form_state as a reference.
function gb_guestbook_form($form, &$form_state) {
Now check the $form_state['storage'] array for your question id, and set it if you haven't already, before you set the form value question_id.
$questions = gb_get_question();
if (!isset($form_state['storage']['question_id'])) {
$form_state['storage']['question_id'] = array_rand($questions);
}
$form['question_id'] = array(
'#type' => 'hidden',
'#value' => $form_state['storage']['question_id'],
);
Your problem (and mine) is describe here:
http://www.sparklepod.com/myblog/drupal-form-same-form-for-build-and-validation/
In short. On submit the form is rebuild before the validation code is run. For me the solutions in the blog is not working for my Drupal 7 site.
What I have done is added the following line to the forum build:
$form_state['cache'] = true;
That will cache the form and the on _submit I will set it to false.
I'm using a simple registration form for CakePHP.
This is my edit view:
<h2>Add User</h2>
<?php echo $form->create('User');
echo $form->input('first_name');
echo $form->input('last_name');
echo $form->input('email');
echo $form->input('address');
echo $form->input('phone');
echo $form->end('Edit');?>
These are my validation rules in User.php
class User extends AppModel {
var $name = 'User';
var $validate = array(
(... more rules here ...)
'password' => array('rule' => array('confirmPassword', 'password'),
'message' => 'Passwords do not match'),
'password_confirm' => array('rule' => 'alphanumeric',
'required' => true)
);
It's a stupid question, but when I use the edit form, the saving of the data fails, because I require a password input in the last rule. That rule was added for the register page, of course.
I've found billions of ways to bypass this problem (disable validation on saving, selecting which fields to save, ...), but I wonder what the "correct" CakePHP way of doing this is? Seems like such a simple problem, but I can't find any documentation on how it has to be done the clean way.
To make my question more clear: I don't want an edit user page where passwords can be changed.
the Cake way is to simply set the validation rule to apply only on create, allowing you to freely edit your data without having to validate the field again.
var $validate = array(
'fieldName' => array(
'rule' => 'ruleName'
'required' => true,
'allowEmpty' => false,
'on' => 'create', // or update <-- here
'message' => 'Your Error Message'
)
);
And of course, if you don't want the password field edited here, just remove the appropriate input
I use multiple validation sets:
In your model, or app_model, override the validates function with this:
function validates($options = array()) {
// copy the data over from a custom var, otherwise
$actionSet = 'validate' . Inflector::camelize(Router::getParam('action'));
if (isset($this->validationSet)) {
$temp = $this->validate;
$param = 'validate' . $this->validationSet;
$this->validate = $this->{$param};
} elseif (isset($this->{$actionSet})) {
$temp = $this->validate;
$param = $actionSet;
$this->validate = $this->{$param};
}
$errors = $this->invalidFields($options);
// copy it back
if (isset($temp)) {
$this->validate = $temp;
unset($this->validationSet);
}
if (is_array($errors)) {
return count($errors) === 0;
}
return $errors;
}
In your controller:
class UsersController extends AppController {
function forgot() {
$this->User->set($this->data);
$this->User->validationSet = 'forgotpassword';
if ($this->User->validates()) {
// send email to reset password and show success message
}
}
}
And finally back in your model, build a validation array, you can have your default validation array, then add a supplemental array such as
class User extends AppModel {
var $validate = array( ... );
var $validateForgotpassword = array( ... );
}
More details here:
http://snook.ca/archives/cakephp/multiple_validation_sets_cakephp
(note the code sample in the article has a bug in it, so be sure to change $param = 'validate' . $validationSet; to $param = 'validate' . $this->validationSet;)
remove your 'required' => true, it is not doing what you think its doing.
'required' => true means that the field should be present in any data that is saved. you are not passing it.