如何根据Symfony中的选择动态添加自定义类型?

时间:2017-09-17 21:04:23

标签: php forms symfony

我正在为symfony的演出创建一个票证预订表单。

每个演出都有多个演出数据。在db中,performanceData记录包含诸如日期,位置和可用票证类型列表之类的内容。每个performanceData记录的可用故障单类型可能不同。

当用户想要预订时,他必须选择性能数据。 根据此选择,他应该获得一个列表,其中包含所有可用的票证类型+每个票证类型a(数字)字段以选择票证数量。这就是我被困的地方。

我使用symfony文档创建了一个动态表单,以根据用户的选择在下拉列表中加载可用的故障单类型。但是如何使用可用的票证类型+数字字段添加列表?此列表应与'票据'映射。在预订实体中。

我的实体:

PerformanceData

/**
 * PerformanceData
 *
 * @ORM\Table(name="performance_data")
 * @ORM\Entity
 */
class PerformanceData
{
    /**
     * @var integer
     *
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;

    /**
     * @var \DateTime
     *
     * @ORM\Column(name="date", type="datetime", nullable=false)
     */
    private $date;

    /**
     * @var Performance
     *
     * @ORM\ManyToOne(targetEntity="Performance", inversedBy="performanceData")
     */
    private $performance;

    /**
     * @var \AppBundle\Entity\Location
     *
     * @ORM\ManyToOne(targetEntity="AppBundle\Entity\Location")
     * @ORM\JoinColumn(name="location_id", referencedColumnName="id")
     */
    private $location;

    /**
     * @var ArrayCollection
     *
     * @ORM\ManyToMany(targetEntity="AppBundle\Entity\TicketType")
     * @ORM\JoinTable(name="performance_data_ticket_type",
     *   joinColumns={
     *     @ORM\JoinColumn(name="performance_data_id", referencedColumnName="id")
     *   },
     *   inverseJoinColumns={
     *     @ORM\JoinColumn(name="ticket_type_id", referencedColumnName="id")
     *   }
     * )
     */
    private $availableTicketTypes;

    //getters/setters...

    /**
     * @return string
     */
    public function getDisplayText()
    {
        return $this->getPerformance()->getInfoTitle() . ' - ' . strftime('%A %d %B %Y', $this->getDate()->getTimestamp());
    }
}

预订

/**
 * Reservation
 *
 * @ORM\Table(name="reservation")
 * @ORM\Entity
 */
class Reservation
{
    /**
     * @var integer
     *
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;

    /**
     * @var PerformanceData
     *
     * @ORM\ManyToOne(targetEntity="PerformanceData")
     * @ORM\JoinColumn(name="performance_data_id", referencedColumnName="id")
     */
    private $performanceData;

    /**
     * @var ArrayCollection
     *
     * @ORM\OneToMany(targetEntity="AppBundle\Entity\ReservationTicket", mappedBy="reservation")
     */
    private $tickets;

    //getters/setters...
}

ReservationTicket

/**
 * ReservationTicket
 *
 * @ORM\Table(name="reservation_ticket")
 * @ORM\Entity
 */
class ReservationTicket
{
    /**
     * @var integer
     *
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;

    /**
     * @var integer
     *
     * @ORM\Column(name="amount", type="smallint", nullable=false)
     */
    private $numberOfTickets;

    /**
     * @var Reservation
     *
     * @ORM\ManyToOne(targetEntity="Reservation", inversedBy="tickets")
     * @ORM\JoinColumn(name="reservation_id", referencedColumnName="id")
     */
    private $reservation;

    /**
     * @var \AppBundle\Entity\TicketType
     *
     * @ORM\ManyToOne(targetEntity="AppBundle\Entity\TicketType")
     * @ORM\JoinColumn(name="ticket_type_id", referencedColumnName="id")
     */
    private $ticketType;

    //getters/setters...
}

我的预订类型表

/**
 * Class ReservationType
 * @package AppBundle\Form
 */
class ReservationType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {


        $builder->add('performanceData', EntityType::class,
                array(
                    'label'         => 'Voorstelling',
                    'class'         => 'AppBundle:PerformanceData',
                    'query_builder' => function (EntityRepository $er) {
                        return $this->getOpenReservations($er);
                    },
                    'group_by'      => function ($performanceData) {
                        if ($performanceData instanceof PerformanceData &&
                            $performanceData->getPerformance() instanceof Performance
                        ) {
                            return $performanceData->getPerformance()->getInfoTitle();
                        }

                        return 'Andere voorstellingen';
                    },
                    'choice_label'  => 'displayText',
                    'required'      => true,
                )
            );

        //event listener for ticket types
        $formModifier = function (FormInterface $form, PerformanceData $performanceData = null) {
            $availableTicketTypes = array();
            $reservationTickets   = array();

            if ($performanceData instanceof PerformanceData) {
                $availableTicketTypes = $performanceData->getAvailableTicketTypes();
            }

            foreach ($availableTicketTypes as $availableTicketType) {
                $reservationTickets[] =  $availableTicketType->getDisplayText();

                $form->add('tickets', ChoiceType::class, array(
                    'label' => 'tickets',
                    'choices' => $reservationTickets,
                    'mapped' => false
                ));
            }
        };

        $builder->addEventListener(
            FormEvents::PRE_SET_DATA,
            function (FormEvent $event) use ($formModifier) {
                // this would be your entity, i.e. SportMeetup
                $data = $event->getData();

                $formModifier($event->getForm(), $data->getPerformanceData(), $data);
            }
        );

        $builder->get('performanceData')->addEventListener(
            FormEvents::POST_SUBMIT,
            function (FormEvent $event) use ($formModifier) {
                // It's important here to fetch $event->getForm()->getData(), as
                // $event->getData() will get you the client data (that is, the ID)
                $performanceData = $event->getForm()->getData();

                // since we've added the listener to the child, we'll have to pass on
                // the parent to the callback functions!
                $formModifier($event->getForm()->getParent(), $performanceData);
            }
        );
    }

    /**
     * @param OptionsResolver $resolver
     */
    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults(
            array(
                'data_class' => Reservation::class
            )
        );
    }
}

谢谢!

编辑:

我认为我的问题有点模糊。这就是我想要实现的目标:

当用户选择演奏数据时:

  • 加载所有可用的故障单类型
  • 每种可用的故障单类型:动态添加一个'数字'输入字段以添加票数。
  • 将这些字段映射到预订实体中的故障单

我有一个预订票务实体:

/**
 * ReservationTicket
 *
 * @ORM\Table(name="reservation_ticket")
 * @ORM\Entity
 */
class ReservationTicket
{
    /**
     * @var integer
     *
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;

    /**
     * @var integer
     *
     * @ORM\Column(name="amount", type="smallint", nullable=false)
     */
    private $numberOfTickets;

    /**
     * @var Reservation
     *
     * @ORM\ManyToOne(targetEntity="Reservation", inversedBy="tickets")
     * @ORM\JoinColumn(name="reservation_id", referencedColumnName="id")
     */
    private $reservation;

    /**
     * @var \AppBundle\Entity\TicketType
     *
     * @ORM\ManyToOne(targetEntity="AppBundle\Entity\TicketType")
     * @ORM\JoinColumn(name="ticket_type_id", referencedColumnName="id")
     */
    private $ticketType;

2 个答案:

答案 0 :(得分:1)

如果要使用动态表单,则应使用JavaScript。 如您所知,Symfony是一个在服务器端工作的PHP框架。 您可以从控制器一次创建表单。事件在提交之前或提交之后调用...而不是在客户端操作期间。 要执行您想要的操作,您必须使用js获取性能数据值并将其发送(Ajax调用)到控制器,它将根据性能数据返回可用的票证。 并使用javascript

在表单中显示可用的票证

答案 1 :(得分:0)

我认为问题在于预留应该与PerformanceData和TicketType(两者)相关联,因此您的实体预留应该具有这两个具有关联的字段。

然后你应该使用EntityType,并使用你的实体作为类。 Symfony应自动制作选择列表:

http://symfony.com/doc/current/reference/forms/types/entity.html#using-a-custom-query-for-the-entities e.g。

$builder->add('users', EntityType::class, array(
    'class' => 'AppBundle:User',
    'query_builder' => function (EntityRepository $er) {
        return $er->createQueryBuilder('u')
            ->orderBy('u.username', 'ASC');
    },
    'choice_label' => 'username',
));

之后,您应该在表单上选择2个。您现在可以隐藏其中一个,如果有人从第一个选择列表中选择一些值,您可以显示带有过滤值的第二个列表。

恕我直言,这应该有所帮助。