gpt4 book ai didi

php - Symfony2 表单和集合 - 一个 Order,OrderItem 实现

转载 作者:塔克拉玛干 更新时间:2023-11-03 05:41:53 24 4
gpt4 key购买 nike

所以我今天花了大约 5 到 6 个小时来与 Symfony2 表单作斗争,现在我想从社区的其他成员那里得到一些建议。我尝试了 3 种不同的方法来实现我所追求的目标,但没有成功。我通读了文档,用谷歌搜索了所有内容,询问了其他人,而且我的情况只比开始时好一点。
我的用例
我正在建立一个系统,您可以在其中订购门票。但核心问题是如何设计系统的订单部分。

  • 票务有一个名称,以及可用的开始和结束日期(还有其他内容,但让示例保持简单。
  • 订购 可以选择多张票,每张票都有一个数量。
  • 订单有 客户 .这部分很好,效果很好!

  • 在阅读并尝试不同的东西后,我收集到代表订单的票和数量,我需要另一个实体 订购单 对应来自 https://github.com/beberlei/AcmePizzaBundle 的 OrderItem比萨是我的票。
  • 订购单 有票和数量。

  • 在创建订单的订单页面上,我需要以下内容:
  • 客户详细信息表格 - 姓名、电子邮件、地址。这部分工作正常。
  • 门票表格 .我希望在文本框甚至字符串中显示票证名称; 不在选择框中 (这就是现在正在发生的事情)。我想要 数量 要在票证名称旁边指定。如果没有设置数量,这意味着没有选择票。
  • 票应该被过滤它们在哪里可用取决于今天的日期 - 这是通过在具有查询构建器闭包的表单类型上使用自定义存储库方法在其他地方(在创建它们的后端管理中)实现的。

  • 我的后端
    Order/OrderTicket/Ticket 设计主要基于 https://github.com/beberlei/AcmePizzaBundle
    /**
    * @ORM\Entity(repositoryClass="Foo\BackendBundle\Entity\TicketsRepository")
    * @ORM\HasLifecycleCallbacks
    * @ORM\Table(name="tickets")
    */
    class Tickets
    {
    // id fields and others

    /**
    * @Assert\NotBlank
    * @ORM\Column(type="string", nullable=true)
    */
    protected $name;

    /**
    * @ORM\Column(type="date", name="available_from", nullable=true)
    */
    protected $availableFrom;

    /**
    * @ORM\Column(type="date", name="available_to", nullable=true)
    */
    protected $availableTo;
    }
    订购单
    /**
    * @ORM\Table()
    * @ORM\Entity
    */
    class OrderTicket
    {
    // id field

    /**
    * @ORM\Column(name="quantity", type="integer")
    */
    private $quantity;

    /**
    * @ORM\ManyToOne(targetEntity="Tickets")
    */
    protected $ticket;

    /**
    * @ORM\ManyToOne(targetEntity="Orders", inversedBy="tickets")
    */
    protected $order;

    // getters and setters for quantity, ticket and order
    }
    命令
    /**
    * @ORM\Entity
    * @ORM\HasLifecycleCallbacks
    * @ORM\Table(name="orders")
    */
    class Orders
    {
    // id field and other stuff

    /**
    * @ORM\OneToMany(targetEntity="OrderTicket", mappedBy="order", cascade={"persist"})
    **/
    protected $tickets;

    /**
    * @ORM\ManyToOne(targetEntity="Customer", cascade={"persist"})
    */
    protected $customer;

    public function __construct()
    {
    $this->tickets = new \Doctrine\Common\Collections\ArrayCollection();
    }

    // getters, setters, add for Tickets and Customer
    }
    顾客
    /**
    * @ORM\Table()
    * @ORM\Entity
    */
    class Customer
    {
    // id, name, email, address fields

    }
    这会创建一个像这样的模式(表命名差异来自自动生成):
    CREATE TABLE `tickets` (
    `id` int(11) NOT NULL AUTO_INCREMENT,
    `name` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
    `available_from` date DEFAULT NULL,
    `available_to` date DEFAULT NULL,
    PRIMARY KEY (`id`)
    );
    CREATE TABLE `Customer` (
    `id` int(11) NOT NULL AUTO_INCREMENT,
    `email` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
    `name` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
    `address` longtext COLLATE utf8_unicode_ci NOT NULL,
    PRIMARY KEY (`id`)
    );
    CREATE TABLE `OrderTicket` (
    `id` int(11) NOT NULL AUTO_INCREMENT,
    `ticket_id` int(11) DEFAULT NULL,
    `order_id` int(11) DEFAULT NULL,
    `quantity` int(11) NOT NULL,
    PRIMARY KEY (`id`)
    );
    CREATE TABLE `orders` (
    `id` int(11) NOT NULL AUTO_INCREMENT,
    `customer_id` int(11) DEFAULT NULL,
    PRIMARY KEY (`id`)
    );
    形式
    class CustomerType extends AbstractType
    {
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
    $builder
    ->add('email')
    ->add('name')
    ->add('address')
    ;
    }

    public function setDefaultOptions(OptionsResolverInterface $resolver)
    {
    $resolver->setDefaults(array(
    'data_class' => 'Foo\BackendBundle\Entity\Customer'
    ));
    }

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

    class OrderTicketType extends AbstractType
    {
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
    $builder
    ->add('quantity', 'integer')
    ->add('ticket')
    ;
    }

    public function setDefaultOptions(OptionsResolverInterface $resolver)
    {
    $resolver->setDefaults(array(
    'data_class' => 'Foo\BackendBundle\Entity\OrderTicket'
    ));
    }

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

    class OrdersType extends AbstractType
    {
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
    $builder
    ->add('customer', new CustomerType())
    ->add('tickets', 'collection', array(
    'type' => new OrderTicketType(),
    'allow_add' => true,
    'allow_delete' => true,
    'prototype' => true,
    ))
    ;
    }

    public function setDefaultOptions(OptionsResolverInterface $resolver)
    {
    $resolver->setDefaults(array(
    'data_class' => 'Foo\BackendBundle\Entity\Orders',
    ));
    }

    public function getName()
    {
    return 'foo_backendbundle_orderstype';
    }
    }
    表格
    <form action="{{ path('index') }}" method="post" {{ form_enctype(form) }}>
    <h3>Tickets</h3>

    {{ form_errors(form) }}

    <table>
    <thead>
    <tr>
    <td>Ticket</td>
    <td>Quantity</td>
    </thead>
    <tbody>
    {% for ticketrow in form.tickets %}
    <tr>
    <td>{{ form_widget(ticketrow.ticket) }}</td>
    <td>{{ form_widget(ticketrow.quantity) }}</td>
    </tr>
    {% endfor %}
    </tbody>
    </table>

    <h3>Customer</h3>

    {% for customer in form.customer %}
    {{ form_row(customer) }}
    {% endfor %}
    </form>
    最后是 Controller
    class DefaultController extends Controller
    {
    /**
    * @Route("/", name="index")
    * @Template()
    */
    public function indexAction(Request $request)
    {
    $em = $this->getDoctrine()->getManager();
    // IMPORTANT - the Tickets are prefiltered for active Tickets, these have to be injected into the Order atm. In other places I use this method on the query builder
    $tickets = $em->getRepository('FooBackendBundle:Tickets')->findActive();

    // check no tickets

    $order = new Orders();

    // To prepopulate the order with the available tickets, we have to do it like this, due to it being a collection,
    // rather than using the forms query_builder like everywhere else
    foreach($tickets as $ticket) {
    $ot = new OrderTicket();
    $ot->setTicket($ticket);
    $ot->setQuantity(0);
    $ot->setOrder($order);
    $order->addTicket($ot);
    }

    $form = $this->createForm(new OrdersType(), $order);

    if ($request->isMethod('POST')) {

    $form->bind($request);

    // IMPORTANT here I have to remove the previously added Tickets where the quantity is 0 - as they're not wanted in the Order. Is there a better way to do this?
    // if the quantity of Ticket is 0, do not add to order
    // note we use the validation callback in Order to check total quantity of OrderTickets is > 0
    $order->removeTicketsWithNoQuantity();

    if ($form->isValid()) {

    $em->persist($order);
    $em->flush();

    return $this->redirect($this->generateUrl('order_show', array('id' => $order->getId())));
    }
    }

    return array('form' => $form->createView());
    }
    }
    概括
    这有效并且将正确保存订单,但我不确定这是做我想做的正确方法,它 没有按我想要的方式显示 .
    您可以在下面的图片中看到它的外观以及订单的执行方式。值得注意的是,在每个票证下拉列表中,是其余的票证,但它们处于非事件状态。
    订单页面:
    order1
    保存后的订单汇总页面:
    order2
    显示的3张票是经过过滤的,我只想要表格上的这些票。 我只想看到票名,而不是可编辑的下拉菜单 .
    核心问题是它们显示为可编辑的下拉菜单。我可能只想要一个包含门票名称的文本字符串,或者甚至是将来的门票价格。我不确定如何实现这一目标。我知道必须以某种方式呈现票证字段和关系,以便它可以绑定(bind)到 Controller 中。所以基本上我希望能够使用 Ticket 实体,它的字段与数量文本框位于同一行。
    因此,让我们摆脱 Symfony2 表单的困惑,并正确看待它 - 在正常世界中,显然我只会检索票证,然后对于每个票证,我会打印票证名称,任何其他我想要的东西,一个隐藏的票证 ID,然后是票证数量的输入。稍微回到 SF2 - 我想我需要在循环 OrderTicket 集合时可用的 Ticket 实体。
    请帮我!

    最佳答案

    基于以上代码的最简单的解决方案是为您的 Ticket 实体创建一个自定义类型,该类型仅将当前票证显示为标签,并为其创建一个数据转换器。

    namespace WineVision\BackendBundle\Form\Type;

    use Symfony\Component\Form\AbstractType;
    use Doctrine\Common\Persistence\ObjectManager;
    use Symfony\Component\Form\FormBuilderInterface;

    use WineVision\BackendBundle\Form\Transformer\TicketToIdTransformer;

    class TicketLabelType extends AbstractType
    {
    private $om;

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

    public function buildForm(FormBuilderInterface $builder, array $options)
    {
    $transformer = new TicketToIdTransformer($this->om);
    $builder->addViewTransformer($transformer);
    }

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

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

    然后在 Resources/Form/fields.html.twig 中创建一个小部件
    {% block ticket_label_type_widget %}
    {% spaceless %}
    <input type="hidden" {{ block('widget_attributes') }} {% if value is not empty %}value="{{ value }}" {% endif %} />
    <span class="ticketName">{{ form.vars.data.ticketNameMethod }}</span>
    {% endspaceless %}
    {% endblock %}

    TicketToIdTransformer:
    namespace WineVision\BackendBundle\Form\Transformer;

    use Symfony\Component\Form\DataTransformerInterface;
    use Symfony\Component\Form\Exception\UnexpectedTypeException;

    use Doctrine\Common\Persistence\ObjectManager;

    class TicketToIdTransformer implements DataTransformerInterface
    {
    private $om;

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

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

    if (!$ticket instanceof \WineVision\BackendBundle\Entity\Ticket) {
    throw new UnexpectedTypeException($ticket, '\WineVision\BackendBundle\Entity\Ticket');
    }


    return $ticket->getId();
    }

    public function reverseTransform($id)
    {

    if ('' === $id || null === $id) {
    return null;
    }

    return $this->om
    ->getRepository('WineVisionBackendBundle:Ticket')
    ->findOneBy(array('id' => $id));

    }
    }

    然后为您的 TicketType 创建服务并传递doctrine.orm.entity_manager 作为参数,并在您的 OrderTicketType 中使用
    $builder->add('ticket', 'ticket_label_type');

    这应该可以解决您上面给出的代码的问题。为了进一步扩展该解决方案,您不应使用每种票证类型预先填充每个订单,而是创建一个自定义集合类型,该集合类型使用表单事件将所有票证字段填充到集合中。

    希望这可以帮助!如果这里有任何语法错误,我深表歉意。我已经从我的一个应用程序中复制了代码,并根据您的需要对其进行了修改。

    关于php - Symfony2 表单和集合 - 一个 Order,OrderItem 实现,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14242259/

    24 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com