При использовании форм Symfony2. 1, в какой момент я должен привязать пользовательские данные (объект) на основе значений формы к объекту моей формы?
Учитывая следующий сценарий, где я должен поместить логику, чтобы привязать отдел к обзору:
Сущности:
- дилерский центр (имеет много отделов)
- отдел (имеет один тип)
- DepartmentType
- Обзор (имеет один дилерский центр и один отдел)
На моей обзорной форме мне нужно, чтобы пользователь мог выбрать дилерский центр и тип отдела, а затем в какой-то форме обратного вызова или предварительной/последующей привязки, выяснить у них, какой отдел привязать к Обзор.
Мне также нужно, чтобы это произошло до проверки, чтобы я мог подтвердить, что отдел является дочерним предприятием дилерского центра.
Примечание: обзор относится как к дилерскому центру, так и к отделу, когда он может просто относиться к отделу, чтобы облегчить обход и другую логику, которую я продолжаю.
Есть два подхода, которые я пробовал до сих пор, но достиг тупиков / путаницы.
- DataTransformer на DepartmentType на форме, не уверен, что я это понимаю правильно, мои методы transform / reverseTransform передавались в объект Review, а не в объект field.
- PRE_BIND, происходит до проверки, но у меня есть только необработанные данные для работы, никаких объектов
- POST_BIND, происходит после проверки: (
Для последнего шага проверки связи у меня есть относительно простой валидатор, который должен выполнить эту работу, но я не уверен, в какой момент я должен привязать данные к объекту, как это. Есть указания?
2 ответа:
Я бы выбрал стандартный (т. е. не доктринальный) тип выбора, содержащий выбор для представления каждого типа DepartmentType.
Затем используйте DataTransformer для преобразования выбранного параметра в соответствующий тип и наоборот.
Ваш пользовательский тип формы должен выглядеть примерно так:
Обратите внимание на передачу дилерского центра в 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 что-то вроде это:
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) { ... }