Symfony2: Как использовать ограничения для пользовательского типа составной формы?

Вот вопрос, над которым я давно ломаю голову. Пожалуйста, имейте в виду, что я (пока) не эксперт по Symfony2, так что я мог где-то допустить ошибку новичка.

Поле1: Стандартный тип поля Symfony2 text

Поле2: Пользовательский тип поля compoundполе с text полем + checkbox полем)

предварительный просмотр

Моя цель: добавление ограничений в поле autoValue для работы с autoValue's text input child

Причина, по которой ограничения не работают, вероятно, заключается в том, что NotBlank ожидает строковое значение, а внутренние данные этого поля формы представляют собой массив array('input'=>'value', 'checkbox' => true). Это значение массива преобразуется обратно в строку с пользовательским DataTransformer. Однако я подозреваю, что это происходит ПОСЛЕ проверки поля на соответствие известным ограничениям.

Как вы видите ниже в прокомментированном коде, я смог заставить ограничения работать с текстовым вводом, однако только тогда, когда они жестко закодированы в тип формы autoValue, и я хочу проверить ограничения основного поля.

Мой (упрощенный) пример кода для контроллера и поля:

.

Код контроллера

Настройка быстрой формы для целей тестирования.

<?php
//...
// $entityInstance holds an entity that has it's own constraints 
// that have been added via annotations

$formBuilder = $this->createFormBuilder( $entityInstance, array(
    'attr' => array(
        // added to disable html5 validation
        'novalidate' => 'novalidate'
    )
));

$formBuilder->add('regular_text', 'text', array(
    'constraints' => array(
        new \Symfony\Component\Validator\Constraints\NotBlank()
    )
));

$formBuilder->add('auto_text', 'textWithAutoValue', array(
    'constraints' => array(
        new \Symfony\Component\Validator\Constraints\NotBlank()
    )
));

.

Исходные файлы TextWithAutoValue

src/My/Component/Form/Type/TextWithAutoValueType.php

<?php

namespace My\Component\Form\Type;

use My\Component\Form\DataTransformer\TextWithAutoValueTransformer;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;

class TextWithAutoValueType extends AbstractType
{
    /**
     * {@inheritdoc}
     */
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder->add('value', 'text', array(
            // when I uncomment this, the NotBlank constraint works. I just
            // want to validate against whatever constraints are added to the
            // main form field 'auto_text' instead of hardcoding them here
            // 'constraints' => array(
            //     new \Symfony\Component\Validator\Constraints\NotBlank()
            // )
        ));

        $builder->add('checkbox', 'checkbox', array(
        ));

        $builder->addModelTransformer(
            new TextWithAutoValueTransformer()
        );
    }

    public function getName()
    {
        return 'textWithAutoValue';
    }
}

src/My/Component/Form/DataTransformer/TextWithAutoValueType.php

<?php

namespace My\Component\Form\DataTransformer;

use Symfony\Component\Form\DataTransformerInterface;

class TextWithAutoValueTransformer 
    implements DataTransformerInterface
{
    /**
     * @inheritdoc
     */
    public function transform($value)
    {
        return array(
            'value'    => (string) $value,
            'checkbox' => true
        );
    }

    /**
     * @inheritdoc
     */
    public function reverseTransform($value)
    {
        return $value['value'];
    }
}

src/My/ComponentBundle/Resources/config/services.yml

parameters:

services:
    my_component.form.type.textWithAutoValue:
        class: My\Component\Form\Type\TextWithAutoValueType
        tags:
            - { name: form.type, alias: textWithAutoValue }

src/My/ComponentBundle/Resources/views/Form/fields.html.twig

{% block textWithAutoValue_widget %}
    {% spaceless %}

    {{ form_widget(form.value) }}
    {{ form_widget(form.checkbox) }}
    <label for="{{ form.checkbox.vars.id}}">use default value</label>

    {% endspaceless %}
{% endblock %}

.

Вопрос

Я читаю документы и Google уже несколько часов и не могу понять, как копировать, связывать или ссылаться на исходные ограничения, которые были добавлены при создании этой формы.

-> Кто-нибудь знает, как это сделать?

-> За бонусные баллы; как включить ограничения, которые были добавлены к связанному объекту основной формы? (через аннотации к классу сущностей)

ПС

Извините, что вопрос получился таким длинным, надеюсь, мне удалось прояснить мою проблему. Если нет, пожалуйста, спросите меня для более подробной информации!


person Maurice    schedule 31.01.2014    source источник
comment
+1. У меня был тот же вопрос, но на него нет ответа.   -  person ferdynator    schedule 31.01.2014
comment
@byf-ferdy спасибо, похоже на похожий вопрос, но я не уверен, что это то же самое. Я считаю, что в моем случае может быть больше вариантов обходного пути, поскольку между ними есть настраиваемый тип формы.   -  person Maurice    schedule 01.02.2014


Ответы (2)


Я предлагаю вам снова прочитать документацию о проверке. первый.

Что мы можем сделать из этого, так это то, что проверка в основном происходит для классов, а не для типов форм. Это вы упустили из виду. Что вам нужно сделать, это:

  • Чтобы создать класс данных для вашего TextWithAutoValueType, например, с именем src/My/Bundle/Form/Model/TextWithAutoValue. Он должен содержать свойства с именами text и checkbox и их методы установки/получения;
  • Чтобы связать этот класс данных с вашим типом формы. Для этого необходимо создать метод TextWithAutoValueType::getDefaultOptions() и заполнить параметр data_class. Перейдите здесь для получения дополнительной информации об этом методе;
  • Создайте проверку для вашего класса данных. Для этого вы можете использовать аннотации или файл Resources/config/validation.yml. Вместо того, чтобы связывать ваши ограничения с полями вашей формы, вы должны связать их со свойствами вашего класса:

validation.yml:

src/My/Bundle/Form/Model/TextWithAutoValue:
    properties:
        text:
            - Type:
                type: string
            - NotBlank: ~
        checkbox:
            - Type:
                type: boolean

Изменить:

Я предполагаю, что вы уже знаете, как использовать один тип формы в другом. При определении конфигурации проверки вы можете использовать очень полезную вещь, называемую группами проверки. . Вот простой пример (в файле validation.yml, так как я не очень хорошо разбираюсь в аннотациях проверки):

src/My/Bundle/Form/Model/TextWithAutoValue:
    properties:
        text:
            - Type:
                type: string
                groups: [ Default, Create, Edit ]
            - NotBlank:
                groups: [ Edit ]
        checkbox:
            - Type:
                type: boolean

Существует параметр groups, который можно добавить к каждому ограничению. Это массив, содержащий имена групп проверки. При запросе проверки объекта вы можете указать, с каким набором групп вы хотите выполнить проверку. Затем система посмотрит в файле проверки, какие ограничения следует применить.

По умолчанию для всех ограничений установлена ​​группа «По умолчанию». Эта группа также используется при выполнении обычной проверки.

  • Вы можете указать группы по умолчанию для определенного типа формы в MyFormType::getDefaultOptions(), установив параметр validation_groups (массив строк - имена групп проверки),
  • При добавлении одного типа формы к другому в MyFormType::buildForm() вы можете использовать определенные группы проверки.

Это, конечно, стандартное поведение для всех опций типа формы. Пример:

$formBuilder->add('auto_text', 'textWithAutoValue', array(
    'label' => 'my_label',
    'validation_groups' => array('Default', 'Edit'),
));

Что касается использования различных сущностей, вы можете нагромождать свои классы данных, следуя той же архитектуре, что и ваши нагроможденные формы. В приведенном выше примере тип формы, использующий textWithAutoValueType, должен иметь класс data_class со свойством auto_text и соответствующим методом получения/установки.

В файле проверки ограничения Valid смогут выполнять каскадную проверку. Свойство с Valid определит класс свойства и попытается найти соответствующую конфигурацию проверки для этого класса и применить ее с теми же группами проверки:

src/My/Bundle/Form/Model/ContainerDataClass:
    properties:
        auto_text:
            Valid: ~ # Will call the validation conf just below with the same groups

src/My/Bundle/Form/Model/TextWithAutoValue:
    properties:
        ... etc
person Zephyr    schedule 03.02.2014
comment
Спасибо, то, что вы говорите, имеет смысл. Как мне повторно использовать этот тип TextWithAutoValue для разных сущностей с разными ограничениями? - person Maurice; 03.02.2014
comment
Спасибо за разработку. Я понимаю работу всего, что вы говорите, вплоть до самой последней части. Звучит как работающее решение. Я поиграюсь с этим и посмотрю, к чему я сталкиваюсь. - person Maurice; 03.02.2014
comment
Я собираюсь наградить вас наградой за самый полезный ответ. Я не заставил его работать так, как хотел, но опять же, может не быть ответа на мой вопрос. В очередной раз благодарим за помощь. - person Maurice; 10.02.2014

Как описано здесь https://speakerdeck.com/bschussek/3-steps-to-symfony2-form-mastery#39 (слайд 39) Бернхарда Шуссека (основного автора расширения symofny form), трансформер никогда не должен изменять информацию, а только изменять ее представление.

Добавляя информацию (checkbox' => true), вы что-то делаете не так.

В редактировании:

public function buildForm(FormBuilderInterface $builder, array $options)
{
    $builder->add('value', 'text', $options);

    $builder->add('checkbox', 'checkbox', array('mapped'=>false));

    $builder->addModelTransformer(
        new TextWithAutoValueTransformer()
    );
}
person Asmir Mustafic    schedule 03.02.2014
comment
Честный эногух. Это всего лишь моя попытка получить работающий флажок в интерфейсе. Мне это нужно только для интерфейса, мне не нужно это как часть сущности или что-то в этом роде. Я просто использую его для автоматического заполнения сопровождающего текстового поля, когда оно отмечено. Я добавил его в форму, потому что хочу, чтобы он проверялся/не проверялся между POST-запросами. Знаете ли вы способ сделать это? - person Maurice; 03.02.2014
comment
Если вы используете класс TextWithCheckbox, как описано Zephyr, вы можете установить истинное значение по умолчанию для флажка в конструкторе класса. - person Veve; 14.05.2015