Symfony2: добавление коллекции на основе структуры наследования таблиц в FormView
Я работаю над приложением Symfony2/Doctrine, которое использует наследование класса-таблицы (http://docs.doctrine-project.org/en/2.0.x/reference/inheritance-mapping.html#class-table- наследование) для управления Жалобами в Консультации. Каждый консультант может иметь много жалоб (OneToMany), и каждый тип жалобы имеет другую структуру и внешний вид. Жалобы - это коллекция и динамически добавляются в JS.
На этом этапе я могу продолжать жалобы и связывать их с Консулами, переработав их как соответствующие типы в контроллере до настойчивости. Я столкнулся с некоторыми проблемами с этим, и я планирую перенести это на событие формы (http://symfony.com/doc/current/cookbook/form/dynamic_form_generation.html) или что-то в этом роде для оптимизации процесса.
Однако проблема, с которой я столкнулась в данный момент, заключается в том, что я не могу отображать существующие жалобы в представлении с помощью FormView, потому что разработчик форм требует, чтобы я задал тип коллекции, которая будет отображаться. Если у каждого Консультата был только один тип Жалобы, это было бы хорошо, но они могли бы иметь несколько типов, а установка типа в построителе форм ограничивает меня одним типом.
Есть ли какой-то подход, который я могу предпринять, чтобы остановить FormView от tyring, чтобы преобразовать в строку в отсутствие какого-либо типа или какой-либо способ динамически определить и назначить тип на основе каждого жалоба (используя $complaints- > getComplaintType(), возможно)?
<?php
namespace Acme\ConsultBundle\Entity;
class Consult
{
/**
* @ORM\OneToMany(targetEntity="Acme\ConsultBundle\Entity\ComplaintBase", mappedBy="consult", cascade={"persist", "remove"})
*/
protected $complaints;
}
?>
<?php
namespace Acme\ConsultBundle\Entity;
/**
* Acme\ConsultBundle\Entity\ConsultBase
*
* @ORM\Entity
* @ORM\Table(name="ConsultComplaintBase")
* @ORM\HasLifecycleCallbacks
* @ORM\InheritanceType("JOINED")
* @ORM\DiscriminatorColumn(name="complaint_name", type="string")
* @ORM\DiscriminatorMap({
* "ComplaintDefault" = "Acme\ConsultBundle\Entity\ComplaintDefault",
* "ComplaintRosacea" = "Acme\ConsultBundle\Entity\ComplaintRosacea",
* "ComplaintBotox" = "Acme\ConsultBundle\Entity\ComplaintBotox",
* "ComplaintAcne" = "Acme\ConsultBundle\Entity\ComplaintAcne",
* "ComplaintUrticaria" = "Acme\ConsultBundle\Entity\ComplaintUrticaria",
* })
*/
abstract class ComplaintBase
{
/**
* @ORM\ManyToOne(targetEntity="Acme\ConsultBundle\Entity\Consult", inversedBy="complaints")
* @ORM\JoinColumn(name="consult_id", referencedColumnName="id")
*/
protected $consult;
/**
* @ORM\Column(type="string", length="255")
*/
protected $complaintType;
}
?>
<?php
namespace Acme\ConsultBundle\Form\Type;
class ConsultType extends AbstractType
{
public function buildForm(FormBuilder $builder, array $options)
{
$builder
->add('complaints', 'collection', array(
// 'type' => new ComplaintUrticariaType(),
'error_bubbling' => true,
'allow_add' => true,
'allow_delete' => true,
'by_reference' => false,
));
}
}
?>
Ответы
Ответ 1
Не совсем уверен, что он будет работать с коллекцией, но, безусловно, работает с одной формой. Попробуйте эту идею.
Сначала сделайте форму для вашей основной сущности ComplaintBase
class ComplaintForm extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$subscriber = new ComplaintSubscriber($builder);
$builder->addEventSubscriber($subscriber);
/* your fields */
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Acme\ConsultBundle\Entity\ComplaintBase',
));
}
}
Затем в подписчике вы можете определить дополнительные поля на основе указанного типа сущности.
class ComplaintSubscriber implements EventSubscriberInterface
{
private $factory;
private $builder;
public function __construct(FormBuilderInterface $builder)
{
$this->factory = $builder->getFormFactory();
$this->builder = $builder;
}
public static function getSubscribedEvents()
{
return array(
FormEvents::PRE_SET_DATA => 'preSetData',
);
}
public function preSetData(FormEvent $event)
{
$data = $event->getData();
$form = $event->getForm();
if (null === $data) {
return;
}
$class = get_class($data);
if( $class === 'Acme\ConsultBundle\Entity\ComplaintDefault' ) {
$this->processDefault($data, $form);
}
elseif( $class === 'Acme\ConsultBundle\Entity\ComplaintRosacea' ) {
$this->processRosacea($data, $form);
}
elseif( $class === 'Acme\ConsultBundle\Entity\ComplaintBotox' ) {
$this->processBotox($data, $form);
}
else {
#NOP
}
}
protected function processDefault(Entity\ComplaintDefault $node, FormInterface &$form)
{
#NOP
}
protected function processRosacea(Entity\ComplaintRosacea $node, FormInterface &$form)
{
$form->add($this->factory->createNamed('some_field', 'text'));
}
protected function processBotox(Entity\ComplaintBotox $node, FormInterface &$form)
{
$form->add($this->factory->createNamed('other_field', 'text'));
}
}