Complex validation rules
Usually a rule validation class doesn't need too much to do its job but there are situations when your validator needs access to external resources (eg: a database connection). In this case, they cannot be registered as classes on the rule factory. Still, you have a few choices:
- use the validator as a callback
- pass the dependencies as options
- extend the
RuleFactoryclass
1. Using the validator as a callback
namespace MyApp\Validation\Rule;
use Sirius\Validation\Validator\AbstractRule;
class UniqueUsername extends AbstractRule{
protected $db;
function __construct($options = array(), $dbConn) {
parent::__construct($options);
$this->db = $dbConn;
}
function validate($value) {
// here you check if the username is taken or not
}
}
Because the factory instantiates the rules only using the options as a single parameter, you cannot do
$ruleFactory->register('username', 'MyApp\Validation\Rule\UniqueUsername');
So you have to do attach the validator as a callback to your validator object
use MyApp\Validation\Rule\UniqueUsername;
// create an instance of the validator
$UniqueUsername = new UniqueUsername($dbConn);
// or get it from the container
$UniqueUsername = $dependencyInjectionContainer->get('UniqueUsername');
$validator = new Validator($ruleFactory);
// the second parameter for the add() can be the name of a rule or a callback
$validator->add('username', [$UniqueUsername, 'validate'];);
2. Pass the dependencies as options
namespace MyApp\Validation\Rule;
use Sirius\Validation\Validator\AbstractRule;
class UniqueUsername extends AbstractRule{
function __construct($options) {
// $options contain the dependencies
}
function validate($value) {
$this->options['db_connection']->query('SELECT * FROM users WHERE username=?', $value);
// continue here
}
}
and in your validator you do something like
$dbConn = $serviceLocator->get('dbconnection');
$validator->add('username', 'MyApp\Validation\Rule\UniqueUsername', [
'db_connection' => $dbConn
];);
3. Extend the RuleFactory class
This solution depends on your application structure but assuming you have a dependency injection container you can use a different RuleFactory object that is capable of creating rules using a service locator or even the dependency injection container.
The example below does not use a real-life implementation, it's just food for thought
namespace MyApp\Validation;
class RuleFactory extends \Sirius\Validation\RuleFactory {
protected $dic;
function setDic(DependecyInjectionContainerInterface $dic) {
$this->dic = $dic;
}
protected function constructValidatorByNameAndOptions($name, $options) {
$validatorClass = $this->validatorsMap[$name];
$validator = $this->dic->createInstanceWithParams($validatorClass, [$options];);
return $validator;
}
}
Configure the service in the container
$container->set('RuleFactory', function($container) {
$factory = new MyApp\Validation\RuleFactory();
$factory->setDic($container);
return $factory;
});
Configure the validation rule in the container
$container->set('MyApp\Validation\Rule\UniqueUsername', function($options) use ($container) {
$rule = new MyApp\Validation\Rule\UniqueUsername($options);
$rule->setDbConn($container->get('db_connection'));
return $rule;
});
You must make sure that your validation uses the proper RuleFactory
use Sirius\Validation\Validator;
$validator = new Validator($container->get('RuleFactory'));
$validator->add('username', 'MyApp\Validation\Rule\UniqueUsername');