Magento multi fields validator for RIB

Magento come with a great form validator : VarienForm. These is a lot of existing validator, for every usage and you can easyly add some. This is how you can do it with a commun example, how to check RIB bank account.

First, our html code

<ul class="inlineblockli">
	<li>
		<label for="code_bank_pb"><?php echo $this->__('bank code') ?></label>
                <input type="text" class="inputText inputTextB validate-digits required-entry validate-length minimum-length-5 maximum-length-5" id="code_bank_pb" maxlength="5" name="prelevement[code_bank_pb]">
	</li>
	<li>
		<label for="code_guichet_pb"><?php echo $this->__('branch code') ?></label>
                <input type="text" class="inputText inputTextB validate-digits required-entry validate-length minimum-length-5 maximum-length-5" id="code_guichet_pb" maxlength="5" name="prelevement[code_guichet_pb]">
	</li>
	<li>
		<label for="num_compte"><?php echo $this->__('Account number to be debited') ?></label>
                <input type="text" class="inputText inputTextBig3 validate-digits required-entry validate-length minimum-length-11 maximum-length-11" id="num_compte" maxlength="11" name="prelevement[num_compte]">
	</li>
	<li>
		<label for="key_rib"><?php echo $this->__('RIB key') ?></label>
                <input type="text" class="inputText inputTextSm3 validate-digits required-entry validate-compte validate-length minimum-length-2 maximum-length-2" id="key_rib" maxlength="2" name="prelevement[key_rib]">
	</li>
</ul>

We first do a lenght validation with these 3 class : validate-length minimum-length-11 maximum-length-11

Then we validate the rib with this class : validate-compte
You will tell me this validator doesn’t exeist. That’s right, let’s create it.

<script type="text/javascript">
    //<![CDATA[
    Validation.addAllThese(
        [
            ['validate-compte', '<?php echo Mage::helper('rating')->__('Please check your RIB account') ?>', function(v) {
                var code_banque = jQuery('#code_bank_pb').val();
                var code_guichet = jQuery('#code_guichet_pb').val();
                var numero_compte = jQuery('#num_compte').val();
                var cle           = jQuery('#key_rib').val();

                var CompteTmp = code_banque + code_guichet + numero_compte + cle;

                while(CompteTmp.length > 9)
                {
                    var CompteTmp2 = CompteTmp.substr(0,9) % 97;
                    CompteTmp = CompteTmp2 + CompteTmp.substr(9,CompteTmp.length);
                }
                var CompteTmp2 = CompteTmp.substr(0,9) % 97;

                if(CompteTmp2 % 97 == 0)
                {
                    return true;
                }
                return false;
            }]
        ]
    );
    //]]>
</script>

Some explainations :

jQuery('#code_bank_pb').val();

Magento use prototype, tu use jQuery, use jQuery instead of $

For the multi field validation, I grab the different fields from this

var code_banque   = jQuery('#code_bank_pb').val();
var code_guichet  = jQuery('#code_guichet_pb').val();
var numero_compte = jQuery('#num_compte').val();
var cle           = jQuery('#key_rib').val();

I don’t know if this is the cleaner way to do (not really usable for another code) but it works.
If you always use the same input id, you can reuse this code on another site.

Fatal error : Declaration of {{path}}::validate() must be compatible with that of Sonata\AdminBundle\Admin\AdminInterface::validate()

You are trying to setup a custom validator on Sonata and you get this error :

Fatal error: Declaration of Adin\AdminBundle\Admin\AnnonceAdmin::validate() must be compatible with that of Sonata\AdminBundle\Admin\AdminInterface::validate() in /home/www/arlogis/src/Adin/AdminBundle/Admin/AnnonceAdmin.php on line 250

Don’t panic, just add this at the begining of your class

use Sonata\AdminBundle\Validator\ErrorElement;

in our example it’s in this file : /AdminBundle/Admin/AnnonceAdmin.php

must be compatible with that of Sonata\AdminBundle\Admin\AdminInterface::validate() error message

You are using sonata and want to validate some fields.
You innocently add the validate function to your Admin file

function validate(ErrorElement $errorElement, $object)
{
}

Then, you get this error :

Fatal error: Declaration of Adin\AdminBundle\Admin\AnnonceAdmin::validate() must be compatible with that of Sonata\AdminBundle\Admin\AdminInterface::validate() in /home/www/arlogis/src/Adin/AdminBundle/Admin/AnnonceAdmin.php on line 251 

You checked to AdminInterface file the declaration is the same.

To fix it, you need to use additionnal namespace :

use Sonata\AdminBundle\Validator\ErrorElement;
use Symfony\Component\Validator\ValidatorInterface;

Symfony validator custom error message

If you need to customize your validator error messages, you can use the setMessage() function.
On your Form class (ex: /lib/form/doctrine/DemandeForm.class.php)

//sfValidatorInteger
$this->validatorSchema['produit_ean']->setMessage('invalid', 'ean should be integer');
$this->validatorSchema['produit_ean']->setMessage('min', 'ean must be at least %min%');
$this->validatorSchema['produit_ean']->setMessage('max', 'ean must be at least %max%');

//sfValidatorDate
$this->validatorSchema['date']->setMessage('bad_format', 'Format must be dd/mm/YYYY');
$this->validatorSchema['date']->setMessage('max', 'Date should be after than 01/01/2013');
$this->validatorSchema['date']->setMessage('min', 'Date should be before than 01/02/2013');

//required
$this->validatorSchema['date']->setMessage('required', 'Date is mandatory');

Symfony 1.4 multi fields validator

In your form class (lib/form/doctrine/classForm.class.php)

  $this->validatorSchema->setPostValidator(
      new sfValidatorMultiFields(
        array('field1', 'field2', 'field3'),
        array('labels' => array('name1', 'name2', 'name3')),
        array('global_invalid' => 'Multi fields error')));

save this class in a lib folder, for exemple apps/frontend/lib/sfValidatorMultiFields.class.php

<?
class sfValidatorMultiFields extends sfValidatorSchema
{
 
  public function __construct(array $fields, $options = array(), $messages = array())
  {
    $this->addOption('fields', $fields);

    $this->addMessage('invalid', 'Error with this field');
    $this->addMessage('global_invalid', 'Multi fields error');

    parent::__construct(null, $options, $messages);
  }

  protected function doClean($values)
  {
    $valid = false;
    foreach ($this->getOption('fields') as $field) {
	//put here your validation code
        // ex: $values[$field] ....
    }
    //another example, at least one field in not empty
    if($values['field1'] != '' || $values['field2'] != '' || $values['field3'] != '')
    {
        $valid = true;
    }

    if ($valid)
    {
      $errorSchema = new sfValidatorErrorSchema($this);

        // The global error
        $errorSchema->addError(new sfValidatorError($this, 'global_invalid', array()));
      }

      // Fields error
      $error = new sfValidatorError($this, 'invalid', array()));

      // Add the error for each defined fields
      foreach ($this->getOption('fields') as $field)
      {
        $errorSchema->addError($error, $field);
      }

      throw $errorSchema;
    }

    return $values;
  }
}
?>

The keys points are :
– use $this->validatorSchema->setPostValidator() in your form class
– all fields are now available from the $values array ($values[‘field1′], $values[‘field2′] …)