Множественные зависимые отношения «один ко многим» в формах Symfony

Я пытаюсь заставить форму с несколькими отношениями (множественные, зависимые отношения «один ко многим») работать, но безуспешно. Я использую Symfony 2.3 с FOSUserbundle.

Пользователь объекта

    use FOS\UserBundle\Entity\User as BaseUser;
    [...]

    /**
     * @ORM\Entity
     * @Gedmo\Loggable
     * @ORM\Table(name="ta_user", indexes={@ORM\Index(name="IDX_LOGIN_TOKEN", columns={"login_token"})})
     */
    class User extends BaseUser
    {
            [...]

        /**
         * @ORM\OneToMany(targetEntity="UserLifestyle", mappedBy="user", fetch="LAZY", cascade={"persist", "remove"})
         */
        protected $lifestyle;

UserManager

    use Doctrine\ORM\EntityManager;
    use FOS\UserBundle\Entity\UserManager as BaseUserManager;
    use Acme\UserBundle\Entity\LifestyleQuestion;
    use Acme\UserBundle\Entity\UserLifestyle;
    [...]

    class UserManager extends BaseUserManager {
        public function createUser() {
            $user = parent::createUser();
            $lifestyle = new UserLifestyle();
            $lifestyle->setQuestion($this->objectManager->getReference('Acme\UserBundle\Entity\LifestyleQuestion', 1));
            $user->addLifeStyle($lifestyle);
            $lifestyle = new UserLifestyle();
            $lifestyle->setQuestion($this->objectManager->getReference('Acme\UserBundle\Entity\LifestyleQuestion', 2));
            $user->addLifeStyle($lifestyle);
            $lifestyle = new UserLifestyle();
            $lifestyle->setQuestion($this->objectManager->getReference('Acme\UserBundle\Entity\LifestyleQuestion', 3));
            $user->addLifeStyle($lifestyle);
            return $user;
        }

Entity UserLifestyle

    /**
     * @ORM\Entity
     * @Gedmo\Loggable
     * @ORM\Table(name="ta_user_lifestyle")
     */
    class UserLifestyle
    {
        /**
         * @ORM\Id
         * @ORM\Column(type="smallint")
         * @ORM\GeneratedValue(strategy="AUTO")
         */
        protected $id;

        /**
         * @ORM\ManyToOne(targetEntity="User", inversedBy="lifestyle")
         * @ORM\JoinColumn(name="user_id")
         */
        protected $user;

        /**
         * @ORM\ManyToOne(targetEntity="LifestyleQuestion", inversedBy="answeredByUser")
         * @ORM\JoinColumn(name="question_id")
         */
        protected $question;

        /**
         * @ORM\ManyToOne(targetEntity="LifestyleAnswer", inversedBy="userAnswers")
         * @ORM\JoinColumn(name="answer_id")
         * @Gedmo\Versioned
         */
        protected $answer;

Затем есть тип формы

    use Symfony\Component\Form\AbstractType;
    use Symfony\Component\Form\FormBuilderInterface;
    use Symfony\Component\OptionsResolver\OptionsResolverInterface;
    use Doctrine\ORM\EntityRepository;

    class RegistrationType extends AbstractType
    {
        public function buildForm(FormBuilderInterface $builder, array $options)
        {
            $builder
                ->add('email', NULL, array('label' => 'E-Mail'))
                            [...]
                ->add('lifestyle', 'collection', array(
                    'type' => new RegistrationLifestyleType(),
                    'allow_add' => false,
                    'allow_delete' => false,
                    'label' => false,
                ))

и теперь должно быть связанное RegistrationLifestyleType. Но я понятия не имею, как это должно выглядеть. Я ожидаю, что в моей регистрационной форме будет три поля выбора, показывающих вопрос (в виде метки) и набор ответов (в виде поля выбора), связанных с этими вопросами. UserManager назначает вновь созданному пользователю три вопроса, поэтому можно получить вопрос с помощью:

    $lifestyles = $user->getLifestyles();
    foreach ($lifestyles as $lifestyle) {
        $question = $lifestyle->getQuestion(); // echo $question->getQuestion();
        $answers = $lifestyle->getQuestion()->getAnswers(); // loop through $answers and echo $answer->getAnswer();
    }

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


person rabudde    schedule 10.09.2013    source источник


Ответы (1)


Нашел решение, может кому пригодится. Проблема кажется в том, что LifestyleQuestion и LifestyleAnswer являются отношениями 1:n для одного и того же объекта (UserLifestyle), поэтому Symfony не знает, как с этим справиться, даже если я уже установил LifestyleQuestion на конкретный вопрос в UserManager. Что касается https://stackoverflow.com/a/9729888/672452, необходимо использовать прослушиватели форм, поэтому родительский объект доступен в подформе. Итак, вот мой «простой» RegistrationLifestyleType (без использования какого-либо внедренного контейнера или менеджера):

use Symfony\Component\Form\AbstractType;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
use Symfony\Component\Form\FormBuilderInterface;
use Doctrine\ORM\EntityRepository;
use Symfony\Component\Form\FormEvents;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Security\Core\SecurityContext;

class RegistrationLifestyleType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options) {
        $builder->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event) use ($builder) {
            $form = $event->getForm();
            $lifestyle = $event->getData();
            if (!($lifestyle instanceof \Acme\UserBundle\Entity\UserLifestyle) || !$lifestyle->getQuestion()) return;
            $label = $lifestyle->getQuestion()->getQuestion();
            $questionId = $lifestyle->getQuestion()->getId();
            $form->add('answer', 'entity', array(
                'class' => 'AcmeUserBundle:LifestyleAnswer',
                'empty_value' => '',
                'property' => 'answer',
                'query_builder' => function(EntityRepository $er) use ($questionId) {
                    return $er
                        ->createQueryBuilder('t1')
                        ->andWhere('t1.question = :question')
                        ->setParameter('question', $questionId)
                        ->orderBy('t1.answer', 'ASC')
                    ;
                },
                'label' => $label,
            ));
        });
    }

    public function setDefaultOptions(OptionsResolverInterface $resolver)
    {
        $resolver->setDefaults(array(
            'data_class' => 'Acme\UserBundle\Entity\UserLifestyle',
            'error_bubbling' => false,
        ));
    }
}
person rabudde    schedule 11.09.2013