How can I setup custom error messages for each form field in Codeigniter? - forms

i'm trying to setup a codeigniter form with different error messages.
set_message(rule, msg) is setting up a message for the whole form.
I need:
$this->form_validation->set_rules('name', 'First Name', 'required');
$this->form_validation->set_message('name', 'required', 'Enter your Name');
$this->form_validation->set_rules('second', 'Variables', 'required');
$this->form_validation->set_message('second', 'required',
'The Variables are required');
Adding the %s into the message string is no help in this case, since the messages have to be completely different.
Possibly I could do something like:
controller
$this->form_validation->set_rules('name', 'Name',
'required|min_length[6]|max_length[12]');
$this->form_validation->set_rules('second', 'Variables',
'required|min_length[3]|max_length[5]');
$this->form_validation->set_message('required', 'required');
$this->form_validation->set_message('min_length', 'short');
$this->form_validation->set_message('max_length', 'long');
view
switch(form_error('name')) {
case '<p>required</p>':
echo 'Enter your Name';
break;
case '<p>short</p>':
echo 'min length 6';
break;
case '<p>long</p>':
echo 'min length 12';
break;
}
switch(form_error('second')) {
case '<p>required</p>':
echo 'The Variables are required';
break;
case '<p>short</p>':
echo 'min length 3';
break;
case '<p>long</p>':
echo 'min length 5';
break;
}
But isn't there a smarter way to do it?

I think a smarter way would be to use Codeigniter's callback feature (something similar to below). The following works but it may be possible to streamline it even more. If nothing else, it's a starting point.
Create two callback functions (I've named these custom_required and custom_check_length) and place them at the bottom of your controller (or wherever you feel necessary).
private function _custom_required($str, $func) {
switch($func) {
case 'name':
$this->form_validation->set_message('custom_required', 'Enter your name');
return (trim($str) == '') ? FALSE : TRUE;
break;
case 'second':
$this->form_validation->set_message('custom_required', 'The variables are required');
return (trim($str) == '') ? FALSE : TRUE;
break;
}
}
and...
private function _custom_check_length($str, $params) {
$val = explode(',', $params);
$min = $val[0];
$max = $val[1];
if(strlen($str) <= $max && strlen($str) >= $min) {
return TRUE;
} elseif(strlen($str) < $min) {
$this->form_validation->set_message('custom_check_length', 'Min length ' . $min);
return FALSE;
} elseif(strlen($str) > $max) {
$this->form_validation->set_message('custom_check_length', 'Max length ' . $max);
return FALSE;
}
}
These two functions take care of the set_message aspect of your form validation. To set the rules, you simply need to call these two functions by prefixing the function name with callback_.
So...
$this->form_validation->set_rules('name', 'Name', 'callback__custom_required[name]|callback__custom_check_length[6,12]');
$this->form_validation->set_rules('second', 'Second', 'callback__custom_required[second]|callback__custom_check_length[3,5]');
I hope the above helps in some way!!

you can set custom error like I mentioned below, no need to create custom function for this error message content change.
$validation = array(
array(
'field' => 'name',
'label' => 'NAME',
'rules' => 'trim|required',
"errors" => array('required' => " Enter your %s. ")
),
);
$this->form_validation->set_rules($validation);
if ($this->form_validation->run()) {}

You were almost there with your initial thought. All you needed was to execute validation after each group of set_message() changes. I find this approach much easier and faster than callback functions, unless you are going to use this exact customisation in many places.
$this->form_validation->set_rules('name', 'First Name', 'required|alpha')
$this->form_validation->set_message('name', 'required', 'Enter your Name');
$this->form_validation->set_message('name', 'alpha', 'Numbers in %s?');
$this->form_validation->run();
$this->form_validation->set_rules('second', 'Variables', 'required');
$this->form_validation->set_message('second', 'required', 'Variables required');
if ($this->form_validation->run()) {
...
}
This validation approach works on all versions of Codeigniter.

$this->form_validation->set_rules('name', 'Name', 'callback__custom_required[name]|callback__custom_check_length[6,12]');
$this->form_validation->set_rules('second', 'Second', 'callback__custom_required[second]|callback__custom_check_length[3,5]');**

Related

WooCommerce - Add a new field to the checkout and order email

Hi I am trying to add a shiiping email field to my checkout page and I want to have it shown on the order email as well.
After having looked around I finally came up with this code that I put in the functions.php: everything worked (I have the new field in the checkout page and I have it in the administrative panel of the orders). Still it doesn't appear on the notification email. What I did wrong?
Here below is my code
// Hook in the checkout page
add_filter( 'woocommerce_checkout_fields' , 'custom_override_checkout_fields' );
// Our hooked in function - $fields is passed via the filter!
function custom_override_checkout_fields( $fields ) {
$fields['shipping']['shipping_email'] = array(
'label' => __('Email', 'woocommerce'),
'placeholder' => _x('Email', 'placeholder', 'woocommerce'),
'required' => false,
'class' => array('form-row-wide'),
'clear' => true
);
return $fields;
}
/* Save Field in the DB as Order Meta Data*/
add_action('woocommerce_checkout_update_order_meta','my_custom_checkout_field_update_order_meta');
function my_custom_checkout_field_update_order_meta($order_id) {
if (!empty($_POST['shipping']['shipping_email'])) {
update_post_meta($order_id, 'Shipping email', esc_attr($_POST['shipping'] ['shipping_email']));
}
}
/* display it in the Order details screen*/
add_action('woocommerce_admin_order_data_after_billing_address', 'my_custom_billing_fields_display_admin_order_meta', 10, 1);
function my_custom_billing_fields_display_admin_order_meta($order) {
echo '
' . __('Shipping email') . ':
' . get_post_meta($order->id, '_shipping_email', true) . '
';
}
/**Add the field to order emails **/
add_filter('woocommerce_email_order_meta_keys', 'my_woocommerce_email_order_meta_keys');
function my_woocommerce_email_order_meta_keys( $keys ) {
$keys[] = 'Shipping email';
return $keys;
}
A small change is required to my_woocommerce_email_order_meta_keys function, following code will work
function my_woocommerce_email_order_meta_keys( $keys ) {
$keys['Shipping email'] = '_shipping_email';
return $keys;
}

Drupal custom form and autocomplete issue

Some how I managed to get it work. But still the result is not coming along with the autocomplete.
Posting my latest code now,
the textfield code
$form['town'] = array(
'#type' => 'textfield',
'#required' => TRUE,
'#autocomplete_path' => 'hfind/town/autocomplete',
);
menu function code
function hfind_menu() {
$items = array();
$items['hfind/town/autocomplete'] = array (
'title' => 'Autocomplete for cities',
'page callback' => 'hfind_town_autocomplete',
'access arguments' => array('use autocomplete'),
'type' => MENU_CALLBACK
);
return $items;
}
the callback function code
function hfind_town_autocomplete($string){
$matches = array();
$result = db_select('towns', 't')
->fields('t', array('town_name'))
->condition('town_name', '%' . db_like($string) . '%', 'LIKE')
->execute();
foreach ($result as $row) {
$matches[$row->city] = check_plain($row->city);
}
drupal_json_output($matches);
}
I hope this may the final edit.
The current situation is, autocomplete is working
The url is hfind/town/autocomplete/mtw
but it is not able to find any data from the database. I found why and unable to fix it.
It is because in the last function I've added above the $string needs to be the 'search query' but it is always querying the database as 'autocomplete'. I mean the $string variable always having the value 'autocomplete' instead of user typed value.
One more problem is, even after providing the permission to all types of user to access search autocomplete on the forms, guests users are not able to use the feature.
Please please someone help me..
`drupal_json_output()` instead of `drupal_to_js` and remove `print` .
<code>
hook_menu() {
$items['cnetusers/autocomplete'] = array(
'title' => 'Auto complete path',
'page callback' => 'cnetusers_employees_autocomplete',
'page arguments' => array(2, 3, 4, 5),
'access arguments' => array('access user profiles'),
'type' => MENU_CALLBACK,
);
return $item;
}
// my autocomplete function is like this
function cnetusers_employees_autocomplete() {
// write your sql query
$matches["$record->ename $record->elname [id: $record->uid]"] = $value;
}
if (empty($matches)) {
$matches[''] = t('No matching records found.');
}
drupal_json_output($matches);
}
$form['disc_info']['approval'] = array(
'#type' => 'textfield',
'#title' => t('Approval By'),
'#autocomplete_path' => 'cnetusers/autocomplete',
);
</code>

Cake PHP custom validation rule

I got a problem with a custom validation rule in Cake 2.X
I want to check if the entered zipcode is valid and therefore a function in the class zipcode is called from the class post.
But the validation returns false all the time.
Appmodel in class post (rule-3 is it):
'DELIVERYAREA' => array(
'rule-1' => array(
'rule' => array('between', 5, 5),
'message' => 'Bitte eine fünfstellige Postleitzahl eingeben'
),
'rule-2' => array(
'rule' => 'Numeric',
'message' => 'Bitte nur Zahlen eingeben'
),
'rule-3' => array(
'exists' => array(
'rule' => 'ZipExists',
'message' => 'Postleitzahl existiert nicht!'
)
)
),
Appmodel in class zipcode:
class Zipcode extends AppModel {
var $name = 'Zipcode';
var $validate = array(
'zipcode' => array(
'length' => array(
'rule' => array('maxLength', 5),
'message' => 'Bitte einen Text eingeben'
),
'exists' => array(
'rule' => array('ZipExists'),
'message' => 'Postleitzahl existiert nicht!'
)
)
);
function ZipExists($zipcode){
$valid = $this->find('count', array('conditions'=> array('Zipcode.zipcode' =>$zipcode)));
if ($valid >= 1){
return true;
}
else{
return false;
}
}
I hope it´s something stupidly easy?
Thanks in advance
I think this:
'Zipcode.zipcode' =>$zipcode
...needs to be this:
'Zipcode.zipcode' =>$zipcode['zipcode']
Careful what you expect inside the validation rule. Use debug() etc to find out what exactly is coming in. $data is always an array here.
public function zipExists($data) {
$zipcode = array_shift($data); // use the value of the key/value pair
$code = $this->find('first', array('conditions'=> array('Zipcode.zipcode' =>$zipcode)));
return !empty($code);
}
try this for only model validation.
function ZipExists(){
$valid = $this->find('count', array('conditions'=> array('Zipcode.zipcode' =>$this->data['Zipcode']['zipcode'])));
if ($valid >= 1){
return true;
}
else{
return false;
}
I found the solution.
Cake wants the custom validation rules to be in the certain class where the rule is called. So, when you call a custom rule in class post, the custom function has to be written down in class post, otherwise cake won´t find it and validate it to false everytime.
The magic to do here is to import the appmodel-class you want to use in the class you call the validation-function. That works with the following statement:
$Zipcode = ClassRegistry::init('Class to use - in my case "Zipcode"');
But if your tables are associated with each other with hasAny or belongsTo and stuff, the custom function works without that. Another important point you mustn´t miss is, that all validation functions has to be introduced with "public function xyz" otherwise cake won´t find them too.

How to verify password field in zend form?

In my form, I'm trying to verify that the user fills in the same value both times (to make sure they didn't make a mistake). I think that's what Zend_Validate_Identical is for, but I'm not quite sure how to use it. Here's what I've got so far:
$this->addElement('password', 'password', array(
'label' => 'Password:',
'required' => true,
'validators' => array(
'Identical' => array(What do I put here?)
)
));
$this->addElement('password', 'verifypassword', array(
'label' => 'Verify Password:',
'required' => true,
'validators' => array(
'Identical' => array(What do I put here?)
)
));
Do I need it on both elements? What do I put in the array?
For what its worth, support for comparing two identical form fields within a model was added to the 1.10.5 release. I wrote up a short tutorial on the matter, which you can access via the below link, but the bottom line is that the Zend_Validate_Identical validator has been refactored to accept a form field name as input. For instance, to compare the values of form fields pswd and confirm_pswd, you'll attach the validator to confirm_pswd like so:
$confirmPswd->addValidator('Identical', false, array('token' => 'pswd'));
Works like a charm.
See Validating Identical Passwords with the Zend Framework for a more complete example.
I can't test it at the moment, but I think this might work:
$this->addElement('password', 'password', array(
'label' => 'Password:',
'required' => true
));
$this->addElement('password', 'verifypassword', array(
'label' => 'Verify Password:',
'required' => true,
'validators' => array(
array('identical', true, array('password'))
)
));
After two days I found the right answer follow me step by step:
step 1:
create PasswordConfirmation.php file in root directory of your project with this path:
yourproject/My/Validate/PasswordConfirmation.php with this content below:
<?php
require_once 'Zend/Validate/Abstract.php';
class My_Validate_PasswordConfirmation extends Zend_Validate_Abstract
{
const NOT_MATCH = 'notMatch';
protected $_messageTemplates = array(
self::NOT_MATCH => 'Password confirmation does not match'
);
public function isValid($value, $context = null)
{
$value = (string) $value;
$this->_setValue($value);
if (is_array($context)) {
if (isset($context['user_password'])
&& ($value == $context['user_password']))
{
return true;
}
}
elseif (is_string($context) && ($value == $context)) {
return true;
}
$this->_error(self::NOT_MATCH);
return false;
}
}
?>
step 2:
Add two field in your form like this:
//create the form elements user_password
$userPassword = $this->createElement('password', 'user_password');
$userPassword->setLabel('Password: ');
$userPassword->setRequired('true');
$this->addElement($userPassword);
//create the form elements user_password repeat
$userPasswordRepeat = $this->createElement('password', 'user_password_confirm');
$userPasswordRepeat->setLabel('Password repeat: ');
$userPasswordRepeat->setRequired('true');
$userPasswordRepeat->addPrefixPath('My_Validate', 'My/Validate', 'validate');
$userPasswordRepeat->addValidator('PasswordConfirmation', true, array('user_password'));
$this->addElement($userPasswordRepeat);
now enjoy your code
class My_Validate_PasswordConfirmation extends Zend_Validate_Abstract
{
const NOT_MATCH = 'notMatch';
protected $_messageTemplates = array(
self::NOT_MATCH => 'Password confirmation does not match'
);
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::NOT_MATCH);
return false;
}
}
http://framework.zend.com/manual/en/zend.form.elements.html
$token = Zend_Controller_Front::getInstance()->getRequest()->getPost('password');
$confirmPassword->addValidator(new Zend_Validate_Identical(trim($token)))
->addFilter(new Zend_Filter_StringTrim())
->isRequired();
Use the above code inside the class which extends zend_form.
I was able to get it to work with the following code:
In my form I add the Identical validator on the second element only:
$this->addElement('text', 'email', array(
'label' => 'Email address:',
'required' => true,
'filters' => array('StringTrim'),
'validators' => array('EmailAddress')
));
$this->addElement('text', 'verify_email', array(
'label' => 'Verify Email:',
'required' => true,
'filters' => array('StringTrim'),
'validators' => array('EmailAddress', 'Identical')
));
And in the controller, just before calling isValid():
$validator = $form->getElement('verify_email')->getValidator('identical');
$validator->setToken($this->_request->getPost('email'));
I don't know if there is a more elegant way of doing this without having to add this code to the controller. Let me know if there is a better way to do this.
With Zend Framework 1.10 the code needed to validate the equality of two fields using Zend Form and Zend Validate is:
$form->addElement('PasswordTextBox',
'password',
array('label' => 'Password')
);
$form->addElement('PasswordTextBox',
'password_confirm',
array('label' => 'Confirm password',
'validators' => array(array('Identical', false, 'password')),
)
);
You can notice, in the validators array of the password_confirm element, that the Identical validator is passed as array, the semantics of that array is: i) Validator name, ii) break chain on failure, iii) validator options
As you can see, it's possible to pass the field name instead of retrieving the value.

Drupal - Include more than one user_profile_form on a page

Edit:
I think it is because the action is the same or something. I tried to modify the action using this:
function mytheme_user_profile_form($form) {
global $user;
$uid = $user->uid;
//print '<pre>'; print_r($form); print '</pre>';
$category = $form['_category']['#value'];
switch($category) {
case 'account':
$form['#action'] = '/user/'.$uid.'/edit?destination=user/'.$uid;
break;
case 'education':
$form['#action'] = '/user/'.$uid.'/edit/education?destination=user/'.$uid;
break;
case 'experience':
$form['#action'] = '/user/'.$uid.'/edit/experience?destination=user/'.$uid;
break;
case 'publications':
$form['#action'] = '/user/'.$uid.'/edit/publications?destination=user/'.$uid;
break;
case 'conflicts':
$form['#action'] = '/user/'.$uid.'/edit/conflicts?destination=user/'.$uid;
break;
}
//print '<pre>'; print_r($form); print '</pre>';
//print $form['#action'];
$output .= drupal_render($form);
return $output;
}
But, the form action, when the form is actually rendered is unchanged. They're all /user/%uid
Can I modify the form action?
I am including several different "categories" of the user profile form on one page, and the code will correctly output the forms I'm specifying. Each form is in a separate collapsible div.
My problem is twofold.
(1) The existing values for the fields aren't pre-populated and
(2) Clicking on "Save" for one section will result in a warning: Email field is required, regardless of which form you're actually saving
I am pretty sure that for problem #2, it is because the name of the button is the same in all cases, as is the form id.
print '<h3>– Account Settings</h3>';
print '<div class="expand">';
print(drupal_get_form('user_profile_form', $user, 'account'));
print '</div>';
print '<h3>– My Info</h3>';
print '<div class="expand">';
print(drupal_get_form('user_profile_form', $user, 'Personal'));
print '</div>';
print '<h3>– Experience</h3>';
print '<div class="expand">';
print(drupal_get_form('user_profile_form', $user, 'experience'));
print '</div>';
print '<h3>– Education</h3>';
print '<div class="expand">';
print(drupal_get_form('user_profile_form', $user, 'education'));
print '</div>';
Problem #1: ? Could you post the html source?
For problem #2:
OK, I'll step through the code here:
The validation handler for the user profile form (user_profile_form_validate()) calls
user_module_invoke('validate', $form_state['values'], $form_state['values']['_account'], $form_state['values']['_category']);
Which looks like
<?php
/**
* Invokes hook_user() in every module.
*
* We cannot use module_invoke() for this, because the arguments need to
* be passed by reference.
*/
function user_module_invoke($type, &$array, &$user, $category = NULL) {
foreach (module_list() as $module) {
$function = $module .'_user';
if (function_exists($function)) {
$function($type, $array, $user, $category);
}
}
}
?>
So, the validation handler for this form is going through every module looking for user hook functions and calling them with $type = 'validate'. (Note that 'category' param is optional here - contrib modules are not required to use it)
Let's look at user.module's user hook as an example to see what happens:
function user_user($type, &$edit, &$account, $category = NULL) {
if ($type == 'view') {
$account->content['user_picture'] = array(
'#value' => theme('user_picture', $account),
'#weight' => -10,
);
if (!isset($account->content['summary'])) {
$account->content['summary'] = array();
}
$account->content['summary'] += array(
'#type' => 'user_profile_category',
'#attributes' => array('class' => 'user-member'),
'#weight' => 5,
'#title' => t('History'),
);
$account->content['summary']['member_for'] = array(
'#type' => 'user_profile_item',
'#title' => t('Member for'),
'#value' => format_interval(time() - $account->created),
);
}
if ($type == 'form' && $category == 'account') {
$form_state = array();
return user_edit_form($form_state, (isset($account->uid) ? $account->uid : FALSE), $edit);
}
//<-- LOOK HERE -->
if ($type == 'validate' && $category == 'account') {
return _user_edit_validate((isset($account->uid) ? $account->uid : FALSE), $edit);
}
if ($type == 'submit' && $category == 'account') {
return _user_edit_submit((isset($account->uid) ? $account->uid : FALSE), $edit);
}
if ($type == 'categories') {
return array(array('name' => 'account', 'title' => t('Account settings'), 'weight' => 1));
}
}
So, it is only supposed to validate if the category == 'account'
In the function _use_edit_validate, we find:
// Validate the e-mail address:
if ($error = user_validate_mail($edit['mail'])) {
form_set_error('mail', $error);
}
There's your error message.
Since that form is only supposed to validate when the category == 'account', and your problem (#2) seems to be that it always validates regardless of the category, maybe your forms are not being rendered as unique form instances? Drupal might be rendering a complete form each time, and just setting a hidden form value to whatever the category is (like in this form's definition function in user_pages.inc $form['_category'] = array('#type' => 'value', '#value' => $category);)
It would be helpful to see the actual html source output.
==EDIT 10-15-09 in response to updated question===
OK, it looks like your method (editing $form['#action'] manually in the theme layer) may not be possible (see this post for reference). If you want to alter the form action you need to write a custom module that implements hook_form_alter() (it won't work in a theme template file). This function allows you to modify how a form is rendered, in your case the user modification form. There are more details on form modification here.
I am not 100% sure that's what you want to do though; (since it looks like you already must create a module) perhaps you want to hook into hook_user() instead; this function "... allows modules to react when operations are performed on user accounts.". You may be able to react to the category in this function and block/allow whichever user changes you like.
However, if it's just email address validation that is the problem, and if you are dealing with existing users, why don't you just make sure the email address is set before you save?