Jason C Wed Sep 19 15:24:38 -0400 2012

Subject: Custom error messages

Hello!

I've been looking into PHP active record's error validation and had a question. When specifying a custom error message is it possible to ignore PHP active record's way of putting the attibutes name in front of the error message?

For instance:

static validates_presence_of = array(
array('name', 'message'=>' is a required field')
)

Would eventually give you 'Name is a required field' after the validation. Now, what I want to be able to do is override this message so that if Name doesn't fit in the context (say the client wants the Name field on a form to say First and Last Name) I want to be able to say:

MyModelName::validates_presence_of['name']['message'] = 'The First and Last Name field is required'; // i know this specifally won't work like this, but it illustrates the point.

So once the validation runs I want to get back 'The First and Last Name field is required' instead of what PHP AR would give back, which is: 'Name The First and Last Name field is required'

Is there a convenient way to do this that doesn't require writing a class to interface with the Error/Validation class to accomplish this? I've partially written one, but since I'm new to the scene I figured it's a strong possibility that someone else has accomplished this already.

Thank you very much for your time.


Derek E Sun Sep 23 00:30:37 -0400 2012

I would also like to know this. I can't seem to find any info about this. I'm working on a project that needs to be in English and French. So for example, the error messages for name, will now need to be nom.

Derek E Sun Sep 23 02:00:33 -0400 2012

Ok. I found a way around this. It wasn't easy, and it's in fact a bit dirty. But it gets the job done, and when you have a project launching next week like I do, it'll work.

I have a language file that I load up with a bunch of variables. If I need english, I'll get en.php or fr.php for French.

Inside of there, the language variables look like this:

$_LANG['cant_be_blank'] = "can't be blank";
$_COLUMN['business_name'] = 'Business Name';

Then I have two functions to get them:

function lang_var($language_id) {
if ($GLOBALS['_LANG'][$language_id]) {
return $GLOBALS['_LANG'][$language_id];
} else {
return 'Language variable not set.';
}
}
function column_to_lang($language_id) {
if ($GLOBALS['_COLUMN'][$language_id]) {
return $GLOBALS['_COLUMN'][$language_id];
} else {
return $language_id;
}
}

Now my validations looks like this:

static $validates_presence_of = array(
array('business_name', 'message' => 'cant_be_blank' ),
);

We can now just think of them as keys, which are going to get the data from that en.php file. The most important part is this: $model->errors->get_raw_errors()

So now my error display now looks like this:

if ($model->errors) {
foreach ($model->errors->get_raw_errors() as $column_name => $errors) {
$field_name = column_to_lang($column_name);
foreach ($errors as $error) {
$lang_var = lang_var($error);
print "<li>{$field_name} $lang_var</li>";
}
}
}
Jason C Mon Sep 24 10:26:46 -0400 2012

Thank you so much for the reply!

I really like the way that you tackled this. I wound up writing a class that I interface with to do all the validation. Essentially this class allows me to do normal validation, but then also allows me to change the attributes name (e.g. name to first name) and/or allows me to set a custom error message explicitly.

The way this works is that I would specify the following somewhere

$form_validator = new f_validation();

if($form_validator->is_post()){
$form_validator->set_custom_attribute_name('name', 'First Name'); // change the name attribute to First Name

$form_validator->set_custom_error('Person', 'validates_presence_of', 'name', 'The First Name field must be ...');
$form_validator->validate();
$form_validator->get_errors();
}

I'm not completely done with this, but it's a start. Thanks again for your input.

::::The code::::

class F_validation{

private $errors = array();
private $custom_detection_string = '%custom_error_string%';
private $new_attribute_names = array();
/**
 * Constructor
*/
public function __construct(){}
/**
 * Determines if there is post data, if so, we're posting
 * @return boolean 
*/
public function is_post(){
if(count($_POST) > 0){
return true;
}
else{
return false;
}
}
/**
 * Determines if there are errors from the validation
 * @return boolean
*/
public function are_errors(){
if(count($this->errors) > 0){
return true;
}
else{
return false;
}
}
/**
 * Say you want the error message for the Student::name attribute to be
 *        First name is required - instead of the default - Name is required
 * Then you would use
 *        $this->f_validation->set_custom_attibute_name('Student', 'name', 'First Name');
 * 
 * @param String $current_name
 *        The current name of the attribute, i.e. the attribute you're effecting
 * 
 * @param String $new_name 
 *        The new name of the attribute that you want to come out from the error message
 * 
*/
public function set_custom_attribute_name($current_name, $new_name){
$this->new_attribute_names[$current_name] = $new_name;
}
/**
 * Note - this only works for validations that have a 'message' included with them, so for instance the validations such as validates_size_of 
 * where the error messages are buried within the 'too_short' and 'too_long' indexes this wouldn't work and would need to be written.
 * 
 * @param String $model name
 *        the name of the model you want to access
 * @param String $validation_rule
 *        The rule that you want to change
 * 
 * @param type $attribute_name
 *        The name of the attribute within the rule
 * 
 * @param type $custom_text 
 *        The overriding text
*/
public function set_custom_error($modelname, $validation_rule, $attribute_name, $custom_text){
// if you ever need to debug this, dump $modelname::${$validation_rule} to understand the error structure that comes along with the static errors set by PHP AR
if(isset($modelname::${$validation_rule}) && is_array($modelname::${$validation_rule})){
foreach($modelname::${$validation_rule} as $index_to_index_rule=>$index_to_a_rule){
foreach($index_to_a_rule as $value){
if($attribute_name == $value){
$modelname::${$validation_rule}[$index_to_index_rule]['message'] = $this->custom_detection_string.$custom_text;
}
}
}
}
}
/**
 * Validation function were all the magic happens
 * Takes an input of x models and runs the validation on them
 * This uses our custom error reporting to set the error messages accordingly
 * @return boolean 
*/
public function validate(){
$args = func_get_args();
$is_valid = true;
foreach($args as $model){
if($model->is_valid() == false){
$is_valid = false;
foreach($model->errors->get_raw_errors() as $attribute_name => $error_array){
foreach($error_array as $error){
// we have a new attribute name for this error
if(isset($this->new_attribute_names[$attribute_name])){
$this->errors[] = $this->new_attribute_names[$attribute_name].' '.$error;
}
// we are overriding the error message with a custom message
else if(preg_match("/".$this->custom_detection_string."/", $error)){
$this->errors[] = str_replace($this->custom_detection_string, '', $error);
}
// do everything normally
else{
$this->errors[] = $this->format_error($attribute_name, $error);
}
}
}
}
}
return $is_valid;
}
/**
 * Gets all the error messages
 * @return type 
*/
public function get_errors(){
return $this->errors;
}
/**
 * Intelligently format the error message with the attributes
 * 
 * @param type $name
 * @param type $error
 * @return type 
*/
private function format_error($name, $error){
return ucfirst(str_replace('_', ' ', strtolower($name))).' '.$error;
}
/**
 * Clears out everything so the validation can run anew
*/
public function clear(){
$this->errors = array();
$this->new_attribute_names = array();
}
}

?>

Raja Amer Khan Tue May 21 05:59:30 -0400 2013

Hi,

I know it's a bit late reply but today I also got into similar situation. What I wanted to achieve was to display my custom field name instead of the table's attribute.

Here is how I done it:

add this class in Validations.php

/** 
 * Custom Class
 * This class is used for personal modifications.
 */
class Custom {
    /**
     * Returns real field name of table removing everything after the '+' sign
     * Updates the argument variable with the custom name
     * @param  string name of the attribute
     * @return string
     */
    public static function add_custom_attribute_name(&$attribute) {
        $real_attribute = $attribute;    
        if($pos = strpos($attribute, '+')) {            
            $real_attribute     = substr($attribute,0,$pos);            // real attribute name
            $attribute             = substr($attribute,$pos+1);            // custom attribute name
        }
        return $real_attribute;
    }
}

Now find this:

$this->model->$attribute

Replace it with:
$this->model->{Custom::add_custom_attribute_name($attribute)}

(Note: Replace all occurrences in Validations.php)

Done!

How to use it?
In your TABLE_MODEL.php

static $validates_presence_of = array(
        array('first+First name'),
        array('last+Last name'),
        array('email')
);

Raja Amer Khan Tue May 21 06:00:16 -0400 2013

Double post. Please delete.

(1-5/5)