Использование форм Symfony 2 для проверки входных данных API

Я создаю API, используя Symfony2, Doctrine и FOSRestBundle. Я хотел бы использовать компонент форм для проверки запросов API, которые создают или изменяют записи, и у меня есть несколько проблем. Я использую компонент проверки самостоятельно, но хотел бы перейти к использованию компонента форм, поскольку он перемещает всю логику проверки из контроллера, лучше обрабатывает привязку данных запроса к моим объектам и лучше объединяет сообщения об ошибках.

Проблемы, с которыми я сталкиваюсь, связаны с полями, которые не требуются, либо когда запись создается с помощью POST, либо когда она обновляется с помощью PUT. В идеале я бы хотел, чтобы необязательные поля вообще не нужно было отправлять через HTTP, но это приводит к сбою проверки формы. Например, одно из полей сущности, которую я использую, — это поле DateTime с именем endTime, и это не требуется. Если параметр с именем endTime отсутствует в запросе POST или PUT, Symfony2 привязывает значение null из запроса к полю. Когда это преобразуется в экземпляр DateTime, он преобразуется в текущую дату и время, а это совсем не то, что я хочу.

Есть ли способ сказать Symfony не привязывать значения к объекту, если они не существуют в HTTP-запросе? Это по-прежнему должно быть безопасным, поскольку проверка все равно не удастся на основе аннотаций в классе сущностей. Я мог бы переопределить метод привязки, но это кажется большой работой...

Спасибо за любые идеи.


person Jeremy    schedule 22.09.2012    source источник
comment
Я использую тот же подход, и он работает безупречно: если вы не добавляете поле в конструкторе форм, оно вообще не привязано.   -  person moonwave99    schedule 22.09.2012
comment
Я не использовал построитель форм, а создавал отдельный класс формы. Это должно позволить мне использовать форму в NelmioAPIDocBundle для автоматического создания документов. Похоже, что удаление элементов из формы в зависимости от того, что отправлено, может быть тем, что нужно...   -  person Jeremy    schedule 22.09.2012
comment
Такой пакет предполагает использование ваших собственных FormTypes, [input="Your\Namespace\Form\Type\YourType"] — просто не добавляйте ненужные поля в метод buildForm().   -  person moonwave99    schedule 22.09.2012
comment
moonwave99 - не все так просто. Существуют поля, обязательные для запроса POST (создание ресурса), которые затем становятся необязательными для запроса PUT (обновление ресурса). Любое из двух других решений будет работать лучше...   -  person Jeremy    schedule 22.09.2012


Ответы (3)


Как насчет создания массива с необязательными полями в качестве ключей, затем объедините запрос POST или PUT поверх этого массива и привяжите его к форме. Таким образом, обязательные поля подсказки существуют для формы и будут содержать данные запроса, если они будут предоставлены.

person DrFrow    schedule 22.09.2012
comment
Это похоже на возможное решение. Я мог бы сравнить отправленные параметры с полями формы и удалить все поля формы, которые не были отправлены. Проверка должна оставаться неизменной, поскольку она отделена от формы. Попробую в понедельник: спасибо! - person Jeremy; 22.09.2012
comment
Спасибо - эта идея работает отлично. Я использовал идею AdrienBrault о прослушивателе событий, но использовал код для удаления элементов формы в зависимости от того, что было отправлено, как вы предложили. Спасибо еще раз. - person Jeremy; 23.09.2012

Вы можете создать Form EventSubscriber, который в событии preBind заменяет неотправленные поля их значением по умолчанию (т. е. значениями привязываемого объекта), а не нулевым значением.

Вот созданный мною EvenSubscriber: https://gist.github.com/3766678. По умолчанию в моей реализации только необязательные поля будут иметь значение по умолчанию вместо нуля...

Вы и кто угодно можете свободно использовать этот класс, если вы действительно сохраняете тег автора ;-)

person AdrienBrault    schedule 22.09.2012
comment
Это еще одно хорошее решение. Я могу отредактировать код, чтобы использовать прослушиватель событий, но удалить все неотправленные поля из формы. Таким образом, данные не будут привязаны к объекту, но проверка все равно будет проходить/не проходить на основе аннотаций в объекте. Спасибо за идею и код! - person Jeremy; 22.09.2012
comment
я считаю, что это путь, прослушиватель событий. Хотя это может быть сложно, но, возможно, в конце концов, это самое чистое решение. - person GorillaApe; 23.09.2012
comment
Спасибо AdrianBrault. Я хотел бы также отметить ваш ответ как решение, поскольку я использовал подписчика событий, как вы предложили, но с идеей DrFrow об удалении элементов формы в зависимости от того, что было отправлено. Вы также предоставили хороший кусок ответа. :) - person Jeremy; 23.09.2012
comment
Удаление элементов формы, значение которых не было отправлено, вызовет у вас проблемы, если вам потребуется отобразить какое-либо представление формы позже. (В моем случае взамен xml/json представление формы) - person AdrienBrault; 23.09.2012
comment
Не должно быть проблемой в моем случае. Я использую FOSRestBundle и возвращаю форму из контроллера, если проверка не удалась. FOSRestBundle обрабатывает установку ответа 400 и сбор ошибок из формы. Поскольку проверка выполняется отдельно от формы, это не вызывает никаких проблем, если элементы удаляются из формы — правильные ошибки все еще отображаются. Вы знаете другой способ добиться этого? Есть ли способ запретить форме обрабатывать элементы, которые не были отправлены? - person Jeremy; 29.09.2012

Этого можно добиться с помощью комбинации пользовательских классов форм. и группы проверки.

Сначала создайте форму (Your\Bundle\Form\CreateFormType), которая расширяет Symfony\Component\Form\AbstractType, строит форму с соответствующими элементами в buildForm:


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

Затем вы можете добавить проверку для каждого из ваших свойств объекта. Каждое из свойств, которые должны быть проверены вместе, должно быть в одних и тех же группах.

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)

У вас также будет другая группа (и пользовательская форма) для update, которая будет содержать свойства, присутствующие в этом запросе.

В своем классе формы вы настраиваете, какие группы проверки используются в setDefaultOptions():


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

person StuBez    schedule 19.11.2012