.. include:: /Includes.rst.txt .. _validating-domain-objects: Validating domain objects ========================= We have discussed both Extbase and Fluid in some detail, but have spent very little time discussing the domain and how we go about ensuring its consistency. We often assume that domain objects are consistent and adhere to our rules at all times. Unfortunately, this is not achieved automatically. So it is important to define these rules explicitly. In the blog example for example we can create the following rules: * The field ``username`` and ``password`` of the user object must have at least 5 characters. Furthermore the username must not contain special characters. * The field ``email`` of the user object must contain a valid email address. These rules must apply at all times for the user object. A user object is only valid if it complies to these validation rules. These rules are called *invariants* because they must be valid during the entire lifetime of the object. At the beginning of your project, it is important to consider which invariants your domain objects will consist of. The next stage is to add these invariants to Extbase in an appropriate way. Extbase provides *validators* for checking the invariants - these are PHP classes in which the invariants are implemented in code. We will show you in the following example how you can use a validator for the checking of invariants and how you can give the user the ability to correct an error if and when it occurs. Validators for checking of Invariants ------------------------------------- A validator is a PHP class that has to check a certain invariant. All validators that are used in Extbase extensions have to implement the interface :php:`\TYPO3\CMS\Extbase\Validation\Validator\ValidatorInterface`. The interface requires validators to implement two methods: - :php:`validate($value)` - :php:`getOptions()` The main method is `validate`, which is called by the framework. The value which is to be validated is passed along to said method and it's the validator's job to check if that value is valid. .. note:: Although the interface states, that the method `validate` should return a :php:`\TYPO3\CMS\Extbase\Error\Result` object, it's not common practice to do so because most people who create custom validators extend the class :php:`\TYPO3\CMS\Extbase\Validation\Validator\AbstractValidator`. This enables you to call the addError()` method and let the abstract validator take care of returning a proper result object to the validation framework. If the logic of your validator allows for loose/variable validation checks, validator options might come in handy. Extbase ships with a :php:`StringLength` validator which offers the options `minimum` and `maximum` that let you define the string length the validator should use to check the incoming value against. .. tip:: You will find the complete reference of the :php:`ValidatorInterface` in the :ref:`validation-api`. For example, a validator which checks whether the passed string is an email address looks like this: :: public function validate($value) { if (!is_string($value) || !$this->validEmail($value)) { $this->addError( $this->translateErrorMessage( 'validator.emailaddress.notvalid', 'extbase' ), 1221559976); } } protected function validEmail($emailAddress) { return \TYPO3\CMS\Core\Utility\GeneralUtility::validEmail($emailAddress); } If ``$value`` is neither a string nor a valid email address, the validator adds an error by calling `$this->addError()`. .. tip:: The method ``addError()`` expects an error message and an error code. The latter should be unique, therefore we recommend to use the UNIX timestamp of the creation time of the source code. With the help of the error code the error can be definitely identified, for example in bug reports. By default, Extbase will not call your validator if the value to validate is empty. This is configured through the property ``$acceptsEmptyValues`` which is set to ``true`` by default. In the package :php:`\TYPO3\CMS\Extbase\Validation\Validator\*` Extbase offers many validators for default requirements like the validation of emails, numbers and strings. When does validation take place? -------------------------------- Domain objects in Extbase are validated only at one point in time: When they get inserted into a controller action. With the help of figure 9-1 we can show what happens before the action is called. .. figure:: /Images/9-CrosscuttingConcerns/figure-9-1.png :align: center Figure 9-1: Data flow of a request before the action is called When a user sends a request, Extbase first determines which action within the controller is responsible for this request. As Extbase knows the names and types of the arguments of the action it can create objects from the incoming data. This operation will be described in detail in the section "Argument mapping" later on. Now the main step for us is as follows: The created objects are to be validated. That is the invariants must be checked. If all arguments are successfully validated, the requested action of the extension is called and it can continue processing the given objects. For example, it might pass it to the view ready for displaying. .. tip:: Certainly it would be helpful if the validation is also be done during the persisting of the objects to the database. At the moment it is not done since the data is stored in the database after sending the answer back to the browser. Therefore the user could not be informed in case of validating errors. In the meantime a second validating when persisting the objects is built into FLOW, so this will be expected in Extbase in the medium term. When an error occurs during validation, the method `errorAction()` of the current controller is called. The default ``errorAction()`` redirects the user to the last used form when possible, in order to give them a chance to correct the errors. .. tip:: You may ask how the `errorAction()` knows which form was the last displayed one. This information is created by the ``form`` ViewHelper. He adds automatically the property ``__referrer`` to every generated form, which contains information about the current extension, controller and action combination. This data can be used by the `errorAction()` to display the erroneous form again. Registering validators ---------------------- Now we know how validators are working and when they are called. However we have to connect our domain model with the validators to define which part of the model is has to be checked by which validator. Therefore there are three possibilities which we define in the following: * validating in the domain model with annotations * validating in the domain model with our own validator class * validating of controller arguments Validating in the domain model with annotations ----------------------------------------------- In most cases it is sufficient to validate the properties of a domain object separately. When all properties are validated with success the complete domain object is also successfully validated. When a property can not be validated, the overall validation of the domain object fails. To define how a property of our domain object should be validated we use *annotations* inside our source code. Annotations are machine readable "annotations" in the source code that are placed in comment blocks and start with the character ``@``. For the validation the ``@TYPO3\CMS\Extbase\Annotation\Validate`` annotation is available. With it we can specify which validator is to be used for checking the annotated property. Let us take a look at this using part of the domain model ``Post`` inside the blog example:: = 2) { return; } $this->addError('The title was not of the type [Topic]:[Title].', 1221563773); } } Now you have seen how you can validate particular properties of the domain model. The next section shows how complex domain objects are validated. Validating in the domain model with your own validator class ------------------------------------------------------------ The ability to register validators in the model are especially useful when individual properties of the model need to be validated. However, sometimes it is necessary to validate the relationship between two or more properties of a model class. For example for a user registration it is reasonable that in the user object the property ``$password`` and ``$passwordConfirmed`` exists which should be identical. Therefore the individual validators for ``$password`` respectively ``$passwordConfirmation`` can not help, because they have no access to each other. You need an option to validate a domain object *as a whole*. For this you can create your own validator class for every object in the domain model which validates the object as a whole and with it has access to all object properties where possible. Equipped with this knowledge we can implement a ``UserValidator`` which compares ``$password`` with ``$passwordConfirmation``. At first we must check if the given object is of the type ``user`` - after all the validator can be called with any object and has to add an error in such case:: addError('The given Object is not a User.', 1262341470); } } } If ``$user`` is not an instance of the user object an error message is directly created with ``addError()``. The validator does not validate the object any further. .. tip:: The method ``addError()`` gets two parameters - the first is an error message string while the second is an error number. The Extbase developers always uses the current UNIX timestamp when calling ``addError()``. By this it is secured that the validation errors can be unique identified. Now we have created the foundation of our validator and can start with the proper implementation of it - the check for equality between the passwords. This is made quickly:: addError('The given Object is not a User.', 1262341470); return; } if ($user->getPassword() !== $user->getPasswordConfirmation()) { $this->addError('The passwords do not match.', 1262341707); } } } Because we have access to the complete object the checking for equality of ``$password`` and ``$passwordConfirmation`` is very straightforward. Use the newly created validator by annotating the corresponding controller, for example: :: view->assign('blog', $blog); } The blog object that we want to edit is passed and given to the view. The Fluid template than looks like this (slightly shortened and reduced to the important):: Note that the ``blog`` object to be edited is bound to the form with ``object="{blog}"``. With this you can reference a property of the linked object with help of the ``property`` attribute of the form elements. Also the name of the form (name="blog") is important because it is used as a variable name for the object to be sent. When submitting the form the ``updateAction`` is called with the ``blog`` object as parameter. :: public function updateAction(\MyVendor\BlogExample\Domain\Model\Blog $blog) { $this->blogRepository->update($blog); } So the name of the argument is ``$blog`` because the form has the name blog. When no validation errors occur, the blog object will be persisted with its changes. Now have a look what happens when the user inserts erroneous data into the form. In this case an error occurs when validating the ``$blog`` arguments. Therefore instead of the `updateAction`, the `errorAction` is called. This action routes the request with ``forward()`` to the last used action because in case of an error the form should be displayed again. Additionally, an error message is generated and given to the controller. Ergo: In case of a validation error the `editAction` is displayed again. As we want to display the erroneous object again it is important that the ``updateAction`` and ``editAction`` use the same argument names. In our example the argument is called ``$blog`` in both cases, so we are on the safe side. Now we have another problem: The ``editAction`` validates all parameters, but our blog object is not valid - we are trapped in an endless loop. Therefore we have to suppress the argument validation for the ``editAction``. For this we need the annotation :php:`@TYPO3\CMS\Extbase\Annotation\IgnoreValidation` – the comment block of the ``editAction`` must be changed like this: :: view->assign('blog', $blog); } } Now the ``blog`` object is not validated in the ``editAction``. So also a non valid ``blog`` object is displayed correct. .. tip:: If Extbase throws the exception \TYPO3\CMS\Extbase\Mvc\Exception\InfiniteLoopException it signs that the :php:`@TYPO3\CMS\Extbase\Annotation\IgnoreValidation` annotation is missing. Fluid automatically adds the CSS class ``f3-form-error`` to all erroneous fields - so you can frame them in red for example using CSS. There is also a ``flashMessages`` ViewHelper which outputs the error messages of the validation. Case study: Create an object ---------------------------- In the last section you have seen how to edit a blog object with a form. Now we will show you how to create a new blog object with a form. Also for creating a blog object two actions are involved. The `newAction` shows a form for creating an object and the `createAction` finally stores the object. The only difference to the editing of an object is that the `newAction` is not always given an argument: when first displaying the form it is logical that there is no object available to be displayed. Therefore the argument must be marked as optional. Here you will see all that we need. At first the controller code: :: view->assign('newBlog', $newBlog); } /** * This action creates the blog and stores it. * * @param \MyVendor\BlogExample\Domain\Model\Blog $newBlog */ public function createAction(\MyVendor\BlogExample\Domain\Model\Blog $newBlog) { $this->blogRepository->add($newBlog); } } The Fluid template for the `newAction` looks like this (in short form):: What is the summary of what we have done? Again it is important that the `newAction` and the `createAction` have the same argument name. This also has to conform with the name of the Fluid template (``newBlog`` in the example). Also the parameter for the `newAction` must be marked as optional and the validation of the parameter must be suppressed with :php:`@TYPO3\CMS\Extbase\Annotation\IgnoreValidation`. Finally you can output validation errors in the template using the ``flashMessages`` ViewHelper when saving the data. In figure 9-2 you can find an overview of the behavior of Extbase when displaying, editing respectively creating of domain objects in the frontend. .. figure:: /Images/9-CrosscuttingConcerns/figure-9-2.png :align: center Figure 9-2: Data flow of the form display and saving. When a validating error occurs it is displayed again. .. _mapping-arguments: Mapping arguments ----------------- In this section we will describe in detail what happens during a request before the respective action is called, especially when sending a form. Because the HTTP protocol (and PHP) can only transfer arrays and strings, a large array with data is transferred when sending a form. In the action, domain objects are often expected as input parameter, so somehow the array must become an object. That is done by Extbase during the *Argument Mappings*. It makes it possible that as a user of Extbase you not only work with arrays, but you can change objects in forms or give over a complete object as *parameter* in links. Lets have a look at all of this in a concrete example: We pick up the blog example extension and edit a blog object, like you got to know in the last section ("Case study: Edit an existing object"). When you edit a blog you see a form in which you can change the properties of the blog, in our case ``title`` and ``description``. The Fluid form looks like this (shortened to the essential):: If the form is submitted the data will be sent in the following manner to the server:: tx_blogexample_pi1[blog][__identity] = 5 tx_blogexample_pi1[blog][title] = My title tx_blogexample_pi1[blog][description] = Description First of all the data is tagged with a prefix that contains the name of the extension and the plugin (``tx_blogexample_pi1``). This makes sure that two extensions have no impact on each other. Furthermore all changed properties of the blog object are transferred in an array, in our case ``title`` and ``description``. As we want to change a blog object, we also need the identity of the blog object. In order to do this, Fluid automatically adds the ``__identity`` property for the ``blog`` object and fills it with the UID of the blog. Now on the server side a ``blog`` object must be created out of this information. This is the job of the property mapper. Its operation method is shown in figure 9-3. For every argument it must be decided first whether a new object has to be created or if the work is based on an existing object. This will be decided based on the identity property ``__identity``. If this is not in the input data a new object is created. Otherwise the framework knows the object identity and can continue working with it. .. tip:: When you take a look at what is transferred to the server by the new action of the blog example, you will find that no identity properties are transferred - in this case a new object is created as intended. In the blog example from above the __identity property is available, therefore the object with the corresponding UID is fetched from the repository and used for further modification. When no properties should be changed the object is given as argument to the action. So that is always persistent, that is changes to this object are saved automatically. !!!Sentence not clear .. figure:: /Images/9-CrosscuttingConcerns/figure-9-3.png :align: center Figure 9-3: The internal control flow of the property mapper. In our case not only is the ``__identity`` property sent, but also a new ``title`` and ``description`` for our blog. For safety reasons a *copy* of the persistent object is applied. The properties of the copy are changed as given in the request, in our case ``title`` and ``description`` are set new. The generated copy is yet a transient object (see section "live cycle of objects" in chapter 2), that is changes on the object are *not* automatically persisted. The changed copy is given to the action as argument. Now we have to explicitly tell our controller that we want to replace the existing persistent ``blog`` object with our modified ``blog`` object. For this, the repository provides the method update():: $this->blogRepository->update($blog); With this the changed object will be made into the persistent object: The changes are now permanently stored. .. sidebar:: Copies of objects Why a copy of an object is created when it is to be changed? Lets assume that the persistent object would be directly changed. In this case an empty controller would be updating persistent objects:: public function updateAction(\MyVendor\BlogExample\Domain\Model\Blog $blog) { // object will be automatically persisted } This is not transparent and difficult to understand. Besides that, this procedure introduces a big safety issue: When the original object is changed it would be impossible to cancel the persisting of the changes. For this reason a copy of the object is returned for changed objects, so the developer of the extension has to decide explicit whether or not the changes are to be made persistent. We want to assume a refinement of the argument mapping: When a link to an action is generated and the link contains an object as parameter the identity of the object is transferred automatically. In the following example the UID is transferred instead of the ``blog`` object:: Show Blog The generated URL contains the identity of the blog object: ``tx_blogexample_pi1[blog]=47``. That is a short form of ``tx_blogexample_pi1[blog][__Identity]=47``. Therefore the property mapper gets the blog object with the identity 47 from the repository and returns it directly without copying before. Now that you know about argument mapping in greater detail an can begin to use it in your own projects. After you have learned how you can make sure any invariants of domain objects, the focus will be directed to the secure programming of the complete extension.