Mapping validators to entities
Validators can be mapped to any class. What is this good for? For example, after creating an entity instance based on an import (from XML, JSON, and so on), it is possible to verify whether the populated data is truly valid and complies with the validation parameters.
As previously mentioned, validation can be mapped to properties of any class. You just need to satisfy these three conditions:
- The class must implement the Entity_Validator_Interface interface.
- The class must use the Entity_Validator_Trait trait.
- The class properties you wish to validate must have a validator definition using the Entity_Validator_Definition attribute. A list of pre-configured validator types and their attributes can be found here.
The class then has a createValidator() method which, based on the definitions, creates an instance of the entity validator Entity_Validator associated with that specific entity instance.
On the generated entity validator, you then simply call the validate():bool method, and complete validation is performed.
Let's look at an example:
namespace JetApplication;
use Jet\BaseObject;
use Jet\Entity_Validator_Definition;
use Jet\Entity_Validator_Interface;
use Jet\Entity_Validator_Trait;
use Jet\Validator;
use Jet\Validator_Int;
class ExampleEntity extends BaseObject implements Entity_Validator_Interface
{
use Entity_Validator_Trait;
#[Entity_Validator_Definition(
type: Validator::TYPE_INT,
min_value: 10,
max_value: 999,
error_messages: [
Validator_Int::ERROR_CODE_OUT_OF_RANGE => 'Number is out of range (10-999)'
]
)]
protected int $int_property = 0;
public function getIntProperty(): int
{
return $this->int_property;
}
public function setIntProperty( int $int_property ): void
{
$this->int_property = $int_property;
}
}
$object = new ExampleEntity();
$object->setIntProperty( 10000 );
$validator = $object->createValidator();
if($validator->validate()) {
echo "OK - object is valid";
} else {
echo "Object is not valid<br><br>";
foreach( $validator->getErrors() as $property_path=>$validation_errors ) {
foreach( $validation_errors as $validation_error ) {
echo $property_path.': '.$validation_error->getMessage().'<br>';
}
}
}
However, let's demonstrate another very useful application. In practice, any entity is composed of a main entity and its sub-entities, and it is necessary to validate the entity as a whole. This is also possible; let's see an example:
namespace JetApplication;
use Jet\BaseObject;
use Jet\Entity_Validator_Definition;
use Jet\Entity_Validator_Interface;
use Jet\Entity_Validator_Trait;
use Jet\Validator;
use Jet\Validator_Int;
class ExampleEntity extends BaseObject implements Entity_Validator_Interface
{
use Entity_Validator_Trait;
#[Entity_Validator_Definition(
type: Validator::TYPE_INT,
min_value: 10,
max_value: 999,
error_messages: [
Validator_Int::ERROR_CODE_OUT_OF_RANGE => 'Number is out of range (10-999)'
]
)]
protected int $int_property = 0;
public function __construct( int $int_property )
{
$this->int_property = $int_property;
}
}
class ExampleMasterEntity extends BaseObject implements Entity_Validator_Interface
{
use Entity_Validator_Trait;
/**
* @var array<int,ExampleEntity>
*/
#[Entity_Validator_Definition(
is_sub_validators: true
)]
protected array $sub_entities = [];
public function addSubEntity( ExampleEntity $sub_entity ) : void
{
$this->sub_entities[] = $sub_entity;
}
}
$master_object = new ExampleMasterEntity();
$master_object->addSubEntity( new ExampleEntity( 10 ) );
$master_object->addSubEntity( new ExampleEntity( 500 ) );
$master_object->addSubEntity( new ExampleEntity( 0 ) );
$validator = $master_object->createValidator();
if($validator->validate()) {
echo "OK - object is valid";
} else {
echo "Object is not valid<br><br>";
foreach( $validator->getErrors() as $property_path=>$validation_errors ) {
foreach( $validation_errors as $validation_error ) {
echo $property_path.': '.$validation_error->getMessage().'<br>';
}
}
}
The output of the example validation will be:
Object is not valid /sub_entities/2/int_property: Number is out of range (10-999)