При использовании форм Symfony2. 1, в какой момент я должен привязать пользовательские данные (объект) на основе значений формы к объекту моей формы?


Учитывая следующий сценарий, где я должен поместить логику, чтобы привязать отдел к обзору:

Сущности:

  • дилерский центр (имеет много отделов)
  • отдел (имеет один тип)
  • DepartmentType
  • Обзор (имеет один дилерский центр и один отдел)

На моей обзорной форме мне нужно, чтобы пользователь мог выбрать дилерский центр и тип отдела, а затем в какой-то форме обратного вызова или предварительной/последующей привязки, выяснить у них, какой отдел привязать к Обзор.

Мне также нужно, чтобы это произошло до проверки, чтобы я мог подтвердить, что отдел является дочерним предприятием дилерского центра.

Примечание: обзор относится как к дилерскому центру, так и к отделу, когда он может просто относиться к отделу, чтобы облегчить обход и другую логику, которую я продолжаю.


Есть два подхода, которые я пробовал до сих пор, но достиг тупиков / путаницы.

  • DataTransformer на DepartmentType на форме, не уверен, что я это понимаю правильно, мои методы transform / reverseTransform передавались в объект Review, а не в объект field.
  • PRE_BIND, происходит до проверки, но у меня есть только необработанные данные для работы, никаких объектов
  • POST_BIND, происходит после проверки: (

Для последнего шага проверки связи у меня есть относительно простой валидатор, который должен выполнить эту работу, но я не уверен, в какой момент я должен привязать данные к объекту, как это. Есть указания?

2 3

2 ответа:

Я бы выбрал стандартный (т. е. не доктринальный) тип выбора, содержащий выбор для представления каждого типа DepartmentType.

Затем используйте DataTransformer для преобразования выбранного параметра в соответствующий тип и наоборот.

Ваш пользовательский тип формы должен выглядеть примерно так:

class Department extends AbstractType
{
    private $em;

    public function __construct(EntityManager $em) {
        $this->em = $em;
    }

    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $transformer = new DepartmentToTypeTransformer($this->em);
        $builder->addViewTransformer($transformer, true);

        $builder->getParent()->addEventListener(FormEvents::PRE_BIND, function($event) use ($transformer) {
            $data = (object) $event->getData();
            $transformer->setDealership($data->dealership);
        });
    }

    public function getParent()
    {
        return 'choice';
    }

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

    public function setDefaultOptions(OptionsResolverInterface $resolver)
    {
        $choices = array();

        foreach ($this->em->getRepository('AcmeDemoBundle:DepartmentType')->findAll() as $type) {
            $choices[$type->getId()] = (string) $type;
        }

        $resolver->setDefaults(array(
            'choices' => $choices,
            'expanded' => true
        ));
    }
}
Обратите внимание на передачу дилерского центра в DataTransformer для использования в преобразовании.

И DataTransformer что-то вроде это:

class DepartmentToTypeTransformer implements DataTransformerInterface
{
    private $em;
    private $dealership;

    public function __construct($em)
    {
        $this->em = $em;
    }

    public function transform($department)
    {
        if (null === $department) {
            return $department;
        }

        return $department->getType()->getId();
    }

    public function reverseTransform($type)
    {
        if (null === $type) {
            return $type;
        }

        return $this->em->getRepository('AcmeDemoBundle:Department')->findOneBy(array(
            'dealership' => $this->getDealership(),
            'type' => $type
        ));
    }

    public function getDealership() {
        return $this->dealership;
    }

    public function setDealership($dealership) {
        $this->dealership = $dealership;
        return $this;
    }
}

Ваша путаница относительно того, что передается вашему трансформатору, скорее всего, вызвана тем, что трансформатор, который вы привязываете, добавляется к уже существующему поведению, попробуйте добавить true в качестве второго параметра addViewTransformer:

    $transformer = new DepartmentToTypeTransformer($this->em);
    $builder->addViewTransformer($transformer, true);

Из документов :

FormBuilder::addViewTransformer(
    DataTransformerInterface $viewTransformer,
    Boolean $forcePrepend = false
)

Appends / prepends a transformer to the view transformer chain.

Поскольку проверка также выполняется в прослушивателе POST_BIND, вы можете просто добавить свой прослушиватель POST_BIND с более высоким приоритетом, чем прослушиватель проверки (т. е. что-либо > 0).

Если вы пишете слушателю:

$builder->addEventListener(FormEvents::POST_BIND, $myListener, 10);

И если вы пишете подписчику:

public static function getSubscribedEvents()
{
    return array(
        FormEvents::POST_BIND => array('postBind', 10),
    );
}

public function postBind(FormEvent $event)
{
    ...
}