Models
Most of the time, you want to validate a model's data.
In the Symfony Boilerplate, we distinguish three kinds of models:
- The TDBM models for business data.
- The
Storable
models for uploads. - The
Proxy
models for data that do not fit in previous scenarios.
#
TDBM Models#
MigrationsThe first stone for validating your TDBM models occurs in the Doctrine migrations.
note
📣  See the Database chapter for more details about Doctrine migrations.
For instance, let's take a look at the Doctrine migration for the users
table:
Here we are already defining rules:
- Scalar values (string, int, etc.).
- Nullable or not.
- Default values.
- Unique values.
#
PHP ClassesOf course, most of these rules are not user-friendly nor developer-friendly as they occur on the database level.
Yet, after applying this migration, TDBM is able to generate two PHP classes:
BaseUser
.User
that extendsBaseUser
.
Let's take a look at the constructor's signature from the BaseUser
class:
Here we can see that non-nullable properties are mandatory. Also, all the properties have an explicit type.
For getters and setters, it works the same:
Overall, it greatly improves the developer experience as you cannot put a wrong type nor miss a mandatory property when creating/updating an instance of a TDBM model. 😉
#
AnnotationsThat being said, it's still not enough. For instance, how to make sure a value is unique? Or a string is not superior to 256 characters?
You could let the database tell you about these issues, but that's usually done in a non developer-friendly way.
Thankfully, the Symfony Validation bundle provides most of the rules (aka constraints) you may want to apply to a TDBM model's property.
You may also add your own rules.
note
📣  See the Constraints chapter of the official documentation for the list of available rules.
note
📣  The folder src/api/src/Domain/Constraint contains our custom-made constraints.
As we cannot modify the BaseUser
class, we have to override the getters in the User
class.
For instance, let's take a look at the email
property getter:
In addition to type hint (non-nullable string), we add three rules:
- The email cannot be blank.
- The email cannot have a length superior to 255 characters.
- The email has to be valid.
note
📣  The message attribute contains a translation key. See the i18n chapter for more details.
You may also add a validation annotation to the class itself:
In this scenario, we use our custom-made Unicity
constraint that verify if a value does not already exist in the
database.
#
DAOsIn addition to the model classes, TDBM also generates the DAO classes.
Like the models, there are two of them:
BaseUserDao
.UserDao
that extendsBaseDao
.
In the later, we have to inject a ValidatorInterface
:
The ValidatorInterface
provides the method validate
that returns the list of all violations according to the model
constraints:
By convention, it's great to add a validate
method in your DAOs:
This method throws an exception if there are any violations in the model.
Last but not least, you should override the save
method from the base DAO:
This approach has two HUGE benefits:
- You centralize the action of validating at one place.
- You always validate a model before saving it in the database.
#
Storable Modelsnote
📣  See the Uploads chapter for more details about uploads storage.
#
PHP ClassA storable is a wrapper around an upload. You may want to validate its extension, size, etc.
You have to extend the Storable
class with a custom class:
Here you may override or add custom getters.
Indeed, like the TDBM models, a storable may uses Symfony Validation annotations. 😉
For instance, let's say you want to validate the upload's extension:
#
StorageA storage is like a DAO but for storables.
It provides methods for validating one or more storables:
Like the save
method from a DAO
, its write
and writeAll
methods also call the validation methods:
#
Proxy ModelsProxy models are PHP classes that does not reflect a database's table nor an upload.
In other words, they are plain old PHP objects.
However, you may use Symfony Validation annotations on these models getters and validate them
using the ValidatorInterface
and InvalidModel
classes. 😉
note
📣  Don't forget to add the @Type
and @Field
annotations if the model should be available in GraphQL.