Jeremy
Jeremy

Reputation: 2669

Using Symfony 2 Forms to validate API inputs

I'm building an API using Symfony2, Doctrine and the FOSRestBundle. I would like to use the forms component to validate API requests that create or modify records and am having a few problems. I have been using the validation component on it's own but would like to move to using the forms component as it moves all validation logic out of the controller, better handles binding the request data to my entities and better aggregates error messages.

The issues I'm having are to do with fields that are not required, either when a record is being created through POST or when it's being updated through PUT. Ideally I'd like it so that a non-required field does not have to be submitted at all through HTTP but this causes the form validation to fail. For example one of the fields on an entity I'm using is a DateTime field called endTime and this is not required. If a parameter called endTime is not present in the POST or PUT request Symfony2 binds the value null from the Request to the field. When this is converted to a DateTime instance it is cast to the current date time, which is not what I want at all.

Is there any way to tell Symfony to not bind values to the entity if they don't exist in the HTTP request? This should still be safe as validation would still fail based on the annotations in the entity class. I could override the bind method but this seems like a lot of work...

Thanks for any ideas.

Upvotes: 3

Views: 2024

Answers (3)

StuBez
StuBez

Reputation: 1364

You can achieve this using a combination of custom form classes and validation groups.

First, create the form (Your\Bundle\Form\CreateFormType), which extends Symfony\Component\Form\AbstractType builds the form with the appropriate elements in buildForm:


    $builder
        ->add('title')
        ->add('firstNames');

You can then add validation for each of your entity properties. Each of the properties that should be validated together will need to share the same groups.

E.g.


    Your\Bundle\Entity\User:
        properties:
            title:
                - NotBlank:
                    groups: [register]
                    message: Please provide your title
            firstNames:
                - NotBlank:
                    groups:  [register]
                    message: Please provide your first name(s)

You would also have another group (and custom form) for update, which would contain the properties that were present in that request.

In your form class, you configure which validation group(s) are used in setDefaultOptions():


    $resolver->setDefaults(array(
        // ...
        'validation_groups' => array('register'),
        // ...
    ));

Upvotes: 2

AdrienBrault
AdrienBrault

Reputation: 7745

You can create a Form EventSubscriber, that on the preBind event, replaces non submitted fields by their default value (ie, the values of the object being bound), instead of null.

Here's the EvenSubscriber I've created: https://gist.github.com/3766678 . By default with my implementation, only non required fields will have the default value instead of null ...

You, and anyone, are free to use this class, as long as you keep the author tag, indeed ;-)

Upvotes: 2

DrFrow
DrFrow

Reputation: 86

What about creating an array with the not-required fields as keys, then merge you request POST or PUT on top of that array and bind that to the form. That way the hint required fields are there for the form and would have the request data if provided.

Upvotes: 2

Related Questions