Lithium: How to call one validation filter from inside another
-
30-05-2021 - |
Question
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;
}
}
?>
OTHER TIPS
I ended up creating a separate method that contains the functionality I need and then calling it from my validation filter. I've trimmed down my base model to hold only the relevant data in it. Hope it helps someone who has a similar problem.
<?php
namespace app\extensions\data;
class Model extends \lithium\data\Model {
public static function __init() {
parent::__init();
Validator::add('unique', function($value, $rule, $options) {
$model = $options['model'];
return ($model::exists($value, $rule, $options, $model)) ? false : true;
});
Validator::add('exists', function($value, $rule, $options) {
$model = $options['model'];
return ($model::exists($value, $rule, $options, $model)) ? true : false;
});
}
public static function exists($value, $rule, $options, $model) {
$field = $options['field'];
$primary = $model::meta('key');
if (isset($options['conditions']) && !empty($options['conditions'])) {
//go here only of `conditions` are given
foreach ($options['conditions'] as $field => $check) {
if (!is_numeric($field)) {
if (is_array($check)) {
/**
* 'conditions' => array(
* 'Users.age' => array('>' => 18) //condition with custom operator
* )
*/
$conditions[$field] = $check;
}
} else {
/**
* Regular lithium conditions array:
* 'conditions' => array(
* 'Users.email' //no key ($field) defined
* )
*/
$conditions[$check] = $value;
}
}
} else {
//since `conditions` is not set, we assume
$modelName = $model::meta('name');
$conditions["$modelName.$field"] = $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]);
}
return $model::count($conditions);
}
}
?>