Skip to content

[WIP] Documenting the Validator component #3710

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions components/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ The Components
stopwatch
templating/index
translation/index
validator/index
yaml/index

.. include:: /components/map.rst.inc
5 changes: 5 additions & 0 deletions components/map.rst.inc
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,11 @@
* :doc:`/components/translation/introduction`
* :doc:`/components/translation/usage`

* :doc:`/components/validator/index`

* :doc:`/components/validator/introduction`
* :doc:`/components/validator/resources`

* :doc:`/components/yaml/index`

* :doc:`/components/yaml/introduction`
Expand Down
8 changes: 8 additions & 0 deletions components/validator/index.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
Validator
=========

.. toctree::
:maxdepth: 2

introduction
resources
77 changes: 77 additions & 0 deletions components/validator/introduction.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
.. index::
single: Validator
single: Components; Validator

The Validator Component
=======================

The Validator component provides tools to validate values following the
`JSR-303 Bean Validation specification`_.

Installation
------------

You can install the component in 2 different ways:

* :doc:`Install it via Composer </components/using_components>` (``symfony/validator`` on `Packagist`_);
* Use the official Git repository (https://github.com/symfony/Validator).
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

are we doing everywhere the https:// and not the git@github.com? just curious

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using git@github.com requires setting up SSH for your account, which does not provide any value when accessing public reposiitories in a read-only way (it used to be reserved to collaborators btw, but I think this has changed)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah and it is far a better practice to get people into contributing and learning. We shouldn't assume all are read-only people 👶

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the component repos are read-only, so it's only for read-only people. And if you want to contribute, you look into the contributing docs and not this docs

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And the https url is not a read-only URL either (and anyway, even contributors should not push to the symfony repo directly but to their fork, as everything goes through PRs even for the core team)


Usage
-----

The Validator component allows you to use very advanced validation rules, but
it is also really easy to do easy validation tasks. For instance, if you want
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would say "do easy validation tasks with it"... Don´t you think the sentence is a bit weird right now?

to validate a string is at least 10 character long, the only code you need is::
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn´t it be validate that a string...?


use Symfony\Component\Validator\Validation;
use Symfony\Component\Validator\Constraints\Length;

$validator = Validation::createValidator();

$violations = $validator->validateValue('Bernhard', new Length(array('min' => 10)));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

would be good to put an example that fails to show some more detail on the output

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The string is 8 characters long, while the constraints expects a minimum of 10 character. So it already fails. However, I agree that we should put some "show error" logic in here (I copied this example from the README).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

actually, it's already in there....


if (0 !== count($violations)) {
// there are errors, now you can show them
foreach ($violations as $violation) {
echo $violation->getMessage().'<br>';
}
}

Retrieving a Validator Instance
-------------------------------

The :class:`Symfony\\Component\\Validator\\Validator` class is the main access
point of the Validator component. To create a new instance of this class, it
is recommend to use the :class:`Symfony\\Component\Validator\Validation`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is recommended to use

class.

You can get a very basic ``Validator`` by calling
:method:`Validation::createValidator() <Symfony\\Component\\Validator\\Validation::createValidator>`::

use Symfony\Component\Validator\Validation;

$validator = Validation::createValidator();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could you please provide a tip saying how it is done in symfony or maybe the service factory is shared but each time it creates a validator or how it is done, would be a good complement.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe let's also mention what the deps that configure a validator are:

    public function __construct(
        MetadataFactoryInterface $metadataFactory,
        ConstraintValidatorFactoryInterface $validatorFactory,
        TranslatorInterface $translator,
        $translationDomain = 'validators',
        array $objectInitializers = array()
    )


The created validator can be used to validate strings, array, numbers, but it
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

arrays instead of array

can't validate classes. To be able to do that, you have to configure the ``Validator``
class. To do that, you can use the :class:`Symfony\\Component\\Validator\\ValidatorBuilder`.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To do that is said twice... I am not native but what about something like "In order to achieve that"

This class can be retrieved by using the
:method:`Validation::createValidatorBuilder() <Symfony\\Component\\Validator\\Validation::createValidatorBuilder>`
method::

use Symfony\Component\Validator\Validation;

$validator = Validation::createValidatorBuilder()
// ... build a custom instance of the Validator
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

better than ... is to add a simple example

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

as you don't know what it does, I don't think it's valuable at this point.

->getValidator();

What things you can configure will be documented in the following sections.

Sections
--------

* :doc:`/components/validator/resources`
* :doc:`/components/validator/metadata`
* :doc:`/components/validator/validating_values`

.. _`JSR-303 Bean Validation specification`: http://jcp.org/en/jsr/detail?id=303
182 changes: 182 additions & 0 deletions components/validator/resources.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
.. index::
single: Validator; Loading Resources

Loading Resources
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A better name would be Mapping Constraints rather than Loading Resources IMO. The goal of this process is to map the constraints on your code, not to load some resources.

Resources are loaded to know the constraints, not the opposite. Loading Resources is a primary goal for the Config component, not for the Validator component.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had a discussion about this myself too :)

I want to have a clear split in the documention:

  • Defining constraints for classes
  • Using loaders to load the definitions
  • Using the validator methods to validate classes using the loaded definitions

I think "Mapping Constraints" covers part 1 and 2, so it is not a clear split. "Loading Resources", on its way is maybe too specific.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

agree with @wouterj it makes sense, i guess he still needs to complete the PR to see the second part more clearly

=================

The Validator uses metadata to validate a value. This metadata defines how a
class, array or any other value should be validated. When validating a class,
each class contains its own specific metadata. When validating another value,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The metadata can also be passed to the validate methods

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not when validating a class

the metadata must be passed to the validate methods.

Class metadata should be defined somewhere in a configuration file, or in the
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should -> can

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

well, it should be defined in a configuration file or the object isn't validated :)

class itself. The ``Validator`` needs to be able to retrieve this metadata
from the file or class. To do that, it uses a set of loaders.

.. seealso::

You'll learn how to define the metadata in :doc:`metadata`.

The StaticMethodLoader
----------------------

The most basic loader is the
:class:`Symfony\\Component\\Validator\\Mapping\\Loader\\StaticMethodLoader`.
This loader will call a static method of the class in order to get the
metadata for that class. The name of the method is configured using the
:method:`Symfony\\Component\\Validator\\ValidatorBuilder::addMethodMapping`
method of the Validator builder::

use Symfony\Component\Validator\Validation;

$validator = Validation::createValidatorBuilder()
->addMethodMapping('loadValidatorMetadata')
->getValidator();

Now, the retrieved ``Validator`` tries to find the ``loadValidatorMetadata()``
method of the class to validate to load its metadata.

.. tip::

You can call this method multiple times to add multiple supported method
names. You can also use
:method:`Symfony\\Component\\Validator\\ValidatorBuilder::addMethodMappings`
to set an array of supported method names.

The FileLoaders
---------------

The component also provides 2 file loaders, one to load Yaml files and one to
load XML files. Use
:method:`Symfony\\Component\\Validator\\ValidatorBuilder::addYamlMapping` or
:method:`Symfony\\Component\\Validator\\ValidatorBuilder::addXmlMapping` to
configure the locations of these files::

use Symfony\Component\Validator\Validation;

$validator = Validation::createValidatorBuilder()
->addYamlMapping('config/validation.yml')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe let's go with the example of the min length and display what is inside the config/validation.yml in terms of this example, i think it should help readers a lot to get up to following the documentation with this example.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that'll be in the next chapter (as said in the intro...)

->getValidator();

.. tip::

Just like with the method mappings, you can also use
:method:`Symfony\\Component\\Validator\\ValidatorBuilder::addYamlMappings` and
:method:`Symfony\\Component\\Validator\\ValidatorBuilder::addXmlMappings`
to configure an array of file paths.

The AnnotationLoader
--------------------

At last, the component provides an
:class:`Symfony\\Component\\Validator\\Mapping\\Loader\\AnnotationLoader`.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe let's add a note that it works together with the reader and the loader just loads the annotations but does not read them, for that it needs that collaborator

This loader uses an annotation reader to parse the annotations of a class.
Annotations are placed in doc block comments (`/** ... */`) and start with an
``@``. For instance::

// ...

/**
* @Assert\NotBlank()
*/
protected $name;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why protected? is this how all examples are provided in the documentation? just curious since i recently saw a note on changing visibility in other parts

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I always use protected when writing the documentation. We might create a normalization PR to fix that though :)


To enable the annotation loader, call the
:method:`Symfony\\Component\\Validator\\ValidatorBuilder::enableAnnotationMapping`
method. It takes an optional annotation reader instance, which defaults to
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it will give an error if the dependency is not required explicitly as it is a suggested dependency when used standalone https://github.com/symfony/symfony/blob/master/src/Symfony/Component/Validator/composer.json#L28

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so maybe let's add a note that this must be specified in their project root composer.json , oh sorry just saw the thing below, nevermind 👶

``Doctrine\Common\Annotations\AnnotationReader``::

use Symfony\Component\Validator\Validation;

$validator = Validation::createValidatorBuilder()
->enableAnnotationMapping()
->getValidator();

To disable the annotation loader after it was enabled, call
:method:`Symfony\\Component\\Validator\\ValidatorBuilder::disableAnnotationMapping`.

.. note::

In order to use the annotation loader, you should have installed the
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This sentence sounds weird to me, I would say you should have xxx and yyy packages installed from Packagist, but again I am not native :)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, I'm not a native either, but the current thing sounds better to me. What's the correct way @weaverryan ? :)

``doctrine/annotations`` and ``doctrine/cache`` packages from Packagist.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i wonder if the autoload.php for some projects is still needed to load the annotation reader, or if any comment should be made here, just to ensure or avoid some pitfalls for not being able to make it work.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good idea!

Using Multiple Loaders
----------------------

The component provides a
:class:`Symfony\\Component\\Validator\\Mapping\\Loader\\LoaderChain` class to
chain multiple loaders. This means you can configure as many loaders as you
want at the same time.

The ``ValidatorBuilder`` will already take care of this when you configure
multiple mappings::

use Symfony\Component\Validator\Validation;

$validator = Validation::createValidatorBuilder()
->enableAnnotationMapping()
->addMethodMapping('loadValidatorMetadata')
->addXmlMapping('config/validation.xml')
->getValidator();

Caching
-------

Using many loaders to load metadata from different places is very easy when
creating the metadata, but it can easily slow down your application since each
file needs to be parsed, validated and converted to a
:class:`Symfony\\Component\\Validator\\Mapping\\ClassMetadata` instance. To
solve this problems, you can configure a cacher which will be used to cache
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should just be cache and no cacher but i give you the reasons sometimes the classes are not named Cacher

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

problems -> problem

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's still a cacher. We don't talk about the name here, just what it does.

My name is Wouter, yet I'm still a person... In the same way, something called YamlFile can be a cacher.

And using "cache" here seems like we configure the cache system behind it, strictly spoken we configure the adapter.

the ``ClassMetadata`` after it was loaded.

The Validator component comes with a
:class:`Symfony\\Component\\Validator\\Mapping\\Cache\\ApcCache`
implementation. You can easily create other cachers by creating a class which
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cachers by implementing :class...

implements :class:`Symfony\\Component\\Validator\\Mapping\\Cache\\CacheInterface`.

.. note::

The loaders already use a singleton load mechanism. That means that the
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This means instead of that means?

loaders will only load and parse a file once and put that in a property,
which will then be used the next time it is asked for metadata. However,
the Validator still needs to merge all metadata of one class from every
loader when it is requested.

To set a cacher, call the
:method:`Symfony\\Component\\Validator\\ValidatorBuilder::setMetadataCache` of
the Validator builder::

use Symfony\Component\Validator\Validation;
use Symfony\Component\Validator\Mapping\Cache\ApcCache;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about the doctrine cache, is that by default? what is it used for and the differences between it and the apc cache.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

doctrinecache is integrated in Symfony 2.5 (see also the PR description)


$validator = Validation::createValidatorBuilder()
// ... add loaders
->setMetadataCache(new ApcCache('some_apc_prefix'));
->getValidator();

Using a Custom MetadataFactory
------------------------------

All loaders and the cacher are passed to an instance of
:class:`Symfony\\Component\\Validator\\Mapping\\ClassMetadataFactory`. This
class is responsible for creating a ``ClassMetadata`` instance from all the
configured resources.

You can also use a custom metadata factory implementation by creating a class
which implements
:class:`Symfony\\Component\\Validator\\MetadataFactoryInterface`. You can set
this custom implementation using
:method:`Symfony\\Component\\Validator\\ValidatorBuilder::setMetadataFactory`::

use Acme\Validation\CustomMetadataFactory;
use Symfony\Component\Validator\Validation;

$validator = Validation::createValidatorBuilder()
->setMetadataFactory(new CustomMetadataFactory(...));
->getValidator();

.. caution::

Since you are using a custom metadata factory, you can't configure loaders
and cachers using the ``add*Mapping()`` methods anymore. You now have to
inject them into your custom metadata factory yourself.