Search code examples
phpvalidationfilterlithium

Lithium: How to call one validation filter from inside another


I have a base model that I extend from. In it, I have defined two validation filters. One checks if a record is unique, the other checks if a record exists. They work the exact same way except ones return value will be the opposite of the other.

So, it doesn't sound right to write the same code twice to only return a different value. I'd like to know how I can call one custom validator from another.

Here's my code for the unique validator:

<?php
Validator::add('unique', function($value, $rule, $options) {
    $model = $options['model'];
    $primary = $model::meta('key');

    foreach ($options['conditions'] as $field => $check) {
        if (!is_numeric($field)) {
            if (is_array($check)) {
                /**
                 * array(
                 *   'exists',
                 *   'message'    => 'You are too old.',
                 *   'conditions' => array(
                 *       
                 *       'Users.age' => array('>' => '18')
                 *   )
                 * )
                 */
                $conditions[$field] = $check;
            }
        } else {
            /**
             * Regular lithium conditions array:
             * array(
             *   'exists',
             *   'message'    => 'This email already exists.',
             *   'conditions' => array(
             *       'Users.email' //no key ($field) defined
             *   )
             * )
             */
            $conditions[$check] = $value;
        }
    }

    /**
     * Checking to see if the entity exists.
     * If it exists, record exists.
     * If record exists, we make sure the record is not checked
     * against itself by matching with the primary key.
     */
    if (isset($options['values'][$primary])) {
        //primary key value exists so it's probably an update
        $conditions[$primary] = array('!=' => $options['values'][$primary]);
    }

    $exists = $model::count($conditions);
    return ($exists) ? false : true;
});
?>

exists should work like this:

<?php
Validator::add('exists', function($value, $rule, $options) {
    $model = $options['model'];
    return !$model::unique($value, $rule, $options);
});
?>

But obviously, it can't be done that way. Would I have to define the validation function as an anonymous function, assign it to a variable and pass that in instead of the closure? Or is there a way I can call unique from within exists?


Solution

  • The anonymous function method would work. And then you could use that variable in another anonymous function you define for the 'exists' validator. Here's another idea that incorporates it into your base model class:

    <?php
    
    namespace app\data\Model;
    
    use lithium\util\Validator;
    
    class Model extends \lithium\data\Model {
    
        public static function __init() {
            static::_isBase(__CLASS__, true);
            Validator::add('unique', function($value, $rule, $options) {
                $model = $options['model'];
                return $model::unique(compact('value') + $options);
            });
            Validator::add('exists', function($value, $rule, $options) {
                $model = $options['model'];
                return !$model::unique(compact('value') + $options);
            });
            parent::__init();
        }
    
        // ... code ...
    
        public static function unique($options) {
            $primary = static::meta('key');
    
            foreach ($options['conditions'] as $field => $check) {
                if (!is_numeric($field)) {
                    if (is_array($check)) {
                        /**
                         * array(
                         *   'exists',
                         *   'message'  => 'You are too old.',
                         *   'conditions' => array(
                         *
                         *     'Users.age' => array('>' => '18')
                         *   )
                         * )
                         */
                        $conditions[$field] = $check;
                    }
                } else {
                    /**
                     * Regular lithium conditions array:
                     * array(
                     *   'exists',
                     *   'message'  => 'This email already exists.',
                     *   'conditions' => array(
                     *     'Users.email' //no key ($field) defined
                     *   )
                     * )
                     */
                    $conditions[$check] = $options['value'];
                }
            }
    
            /**
             * Checking to see if the entity exists.
             * If it exists, record exists.
             * If record exists, we make sure the record is not checked
             * against itself by matching with the primary key.
             */
            if (isset($options['values'][$primary])) {
                //primary key value exists so it's probably an update
                $conditions[$primary] = array('!=' => $options['values'][$primary]);
            }
    
            $exists = $model::count($conditions);
            return ($exists) ? false : true;
        }
    
    }
    
    ?>