Symfony 3:表单中的allow_delete不起作用

时间:2016-12-11 17:10:48

标签: php forms doctrine-orm symfony

我在Symfony 3中的表单有问题。我有一对多连接(Doctrine 2)。它处理订单(订单),产品(产品)和加入实体( OrderProduct ),它按顺序保存产品数量。

添加和更新订单条目的表单,使用订单产品集合。这一切都基于文档(link)。

在表单中,我有一个按钮,用于添加产品(从文档中添加< li>到DOM),每个添加的按钮都有一个按钮 (从文档中删除DOM中的< li>)。这部分工作 - 添加和删除DOM。

添加产品有效(在新订单中,与编辑时相比)。

但我的问题是删除。在 $ editForm-> getData() 仍然显示

OrderProduct表格     

namespace AppBundle\Form;

use AppBundle\Entity\ProductType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;

class OrderProductType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('amount')
            ->add('product')
        ;
    }

    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults(
            array(
                'data_class' => 'AppBundle\Entity\OrderProduct',
            )
        );
    }

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

订购单     

namespace AppBundle\Form;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\CollectionType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;

class OrderType extends AbstractType
{

    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('customer')
            ->add('date', null, array('widget' => 'single_text'))
            ->add('payment', null, array('widget' => 'single_text'))
            ->add('processed', null, array('widget' => 'single_text'))
            ->add(
                'orderProducts',
                CollectionType::class,
                array(
                    'entry_type'    => OrderProductType::class,
                    'allow_add'     => true,
                    'allow_delete'  => true,
                    'by_reference'  => false,
                    'prototype'     => true,
                    'delete_empty'  => true,
                    'entry_options' => array('data_class' => 'AppBundle\Entity\OrderProduct'),
                )
            );

    }

    public function configureOptions(OptionsResolver $resolver)
    {
    }

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

}

OrderController中的当前操作 (添加产品,不删除)     public function editAction(Request $ request,$ orderId){

    $em = $this->getDoctrine()->getManager();
    $order = $em->getRepository('AppBundle:Order')->find($orderId);

    if (!$order) {
        throw $this->createNotFoundException('No order found for id '.$orderId);
    }

    $editForm = $this->createForm(OrderType::class, $order);
    $editForm->add('submit', SubmitType::class);

    $editForm->handleRequest($request);

    if ($editForm->isSubmitted() && $editForm->isValid()) {

        $order = $editForm->getData();

        //print '<pre>';
        //var_dump($order->getOrderProducts());
        //die();

        $orderProducts = $order->getOrderProducts();

        $em->persist($order);

        foreach ($orderProducts as $oneOrderProduct) {
            $oneOrderProduct->setOrder($order);
            $em->persist($oneOrderProduct);
        }

        //print '<pre>';
        //var_dump($order->getOrderProducts());
        //die();

        $em->flush();

        return $this->redirectToRoute('one_order', array('orderId' => $order->getId()));
    }


    return $this->render(
        'order/new.html.twig', array(
        'form' => $editForm->createView(),
    ));

}

我知道我必须在editAction中从Order中删除已删除的OrderProducts,但现在我不能,因为从表单中发送所有OrderProducts。

订单实体     

namespace AppBundle\Entity;

use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
use Doctrine\Common\Collections\ArrayCollection;
use AppBundle\Entity\OrderProduct;
use Symfony\Component\Form\Extension\Core\Type\CollectionType;


/**
 * @ORM\Entity
 * @ORM\Table(name="order_")
 */
class Order
{

    /**
     * @ORM\Column(type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;
    /**
     * @ORM\ManyToOne(targetEntity="Customer", inversedBy="orders")
     * @ORM\JoinColumn(name="customer_id", referencedColumnName="id")
     */
    private $customer;
    /**
     * @ORM\Column(type="date")
     */
    private $date;
    /**
     * @ORM\Column(type="date")
     */
    private $payment;
    /**
     * @ORM\Column(type="date")
     */
    private $processed;

    public function __toString()
    {
        return strval($this->getId());
    }

    /**
     * @ORM\OneToMany(targetEntity="OrderProduct", mappedBy="order")
     */
    private $orderProducts;

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


    /**
     * Get id
     *
     * @return integer
     */
    public function getId()
    {
        return $this->id;
    }

    /**
     * Set date
     *
     * @param \DateTime $date
     *
     * @return Order
     */
    public function setDate($date)
    {
        $this->date = $date;

        return $this;
    }

    /**
     * Get date
     *
     * @return \DateTime
     */
    public function getDate()
    {
        return $this->date;
    }

    /**
     * Set payment
     *
     * @param \DateTime $payment
     *
     * @return Order
     */
    public function setPayment($payment)
    {
        $this->payment = $payment;

        return $this;
    }

    /**
     * Get payment
     *
     * @return \DateTime
     */
    public function getPayment()
    {
        return $this->payment;
    }

    /**
     * Set processed
     *
     * @param \DateTime $processed
     *
     * @return Order
     */
    public function setProcessed($processed)
    {
        $this->processed = $processed;

        return $this;
    }

    /**
     * Get processed
     *
     * @return \DateTime
     */
    public function getProcessed()
    {
        return $this->processed;
    }

    /**
     * Set customer
     *
     * @param \AppBundle\Entity\Customer $customer
     *
     * @return Order
     */
    public function setCustomer(\AppBundle\Entity\Customer $customer = null)
    {
        $this->customer = $customer;

        return $this;
    }

    /**
     * Get customer
     *
     * @return \AppBundle\Entity\Customer
     */
    public function getCustomer()
    {
        return $this->customer;
    }

    /**
     * Add orderProduct
     *
     * @param \AppBundle\Entity\OrderProduct $orderProduct
     *
     * @return Order
     */
    public function addOrderProduct(\AppBundle\Entity\OrderProduct $orderProduct)
    {
        $this->orderProducts[] = $orderProduct;

        return $this;
    }

    /**
     * Remove orderProduct
     *
     * @param \AppBundle\Entity\OrderProduct $orderProduct
     */
    public function removeOrderProduct(\AppBundle\Entity\OrderProduct $orderProduct)
    {
        $this->orderProducts->removeElement($orderProduct);
    }

    /**
     * Get orderProducts
     *
     * @return \Doctrine\Common\Collections\Collection
     */
    public function getOrderProducts()
    {
        return $this->orderProducts;
    }
}

但是在POST中没关系as you can see here,在这里我删除了4个产品中的2个。表单处理中出现问题。

2 个答案:

答案 0 :(得分:0)

如果by_reference设置为false,则基础Order实体必须在您的案例中有一个名为[removeOrderProduct]的方法。可能还有一个问题是您没有在configureOptions方法中指定data_class选项。在您的情况下,如果Order实体位于'AppBundle \ Entity \ Order'中,则configureOptions方法应包含:

public function configureOptions(OptionsResolver $resolver)
{
    $resolver->setDefaults(array(
        'data_class' => 'AppBundle\Entity\Order',
    ));
}

我建议你也在OrderProductType类中做同样的事情。 我的猜测是,既然你没有在OrderType类中指定'data_class'选项,'orderProducts'字段中的by_reference选项可能无法找出在哪里查找[removeOrderProduct]方法。所以设置该选项并确保在Order实体类中有该方法。

如果这不是问题,那么您应该提供有关订单实体的更多信息以及您在何处调用getData方法。

更新

查看您的代码我无法识别问题,这可能导致实体无法删除。但是我在你的代码中发现了一些奇怪的东西:

在控制器中,当处理表单提交时,没有必要调用getData方法:在调用handleRequest之后,$ order对象会更新并保存新信息(因为对象是通过引用传递的,所以表单不能应用更改而不更改原始$ order也是如此)。因此,不需要$ order = $ form-&gt; getData(),因为您之前已经定义了$ order变量,它保存了对表单映射已发布值的同一对象的引用。

如果这没有用,我建议你加模;遍布各处的语句,只是为了确保在每一步调用正确的方法。例如添加模具;到removeOrderProduct方法,检查它是否命中。如果它被击中,问题就不会从您提供给我们的数据中显而易见,因此需要进一步调试。

现在也可能不是问题,但是如果你想在提交后删除列表中不存在的产品,你必须调用$ order-&gt; getOrderProducts并将每个项目添加到一个新的集合中之前的orderProducts(在提交之前)并将其与提交后的值进行比较,以确定哪些需要删除。

答案 1 :(得分:0)

我昨晚解决了这个问题:)

这是有效的控制器操作:

/**
 * @Route("/{orderId}/edit", name="edit_order", requirements={"orderId": "\d+"})
 * @param Request $request
 * @param         $orderId
 *
 * @return \Symfony\Component\HttpFoundation\RedirectResponse|\Symfony\Component\HttpFoundation\Response
 */
public function editAction(Request $request, $orderId)
{
    $em = $this->getDoctrine()->getManager();
    $order = $em->getRepository('AppBundle:Order')->find($orderId);

    if (!$order) {
        return $this->redirectToRoute('new_order');
    }

    $originalOrderProducts = new ArrayCollection();

    foreach ($order->getOrderProducts() as $orderProduct) {
        $originalOrderProducts->add($orderProduct);
    }

    $editForm = $this->createForm(OrderType::class, $order);
    $editForm->add('submit', SubmitType::class);
    $editForm->handleRequest($request);

    if ($editForm->isSubmitted() && $editForm->isValid()) {

        $orderProducts = $order->getOrderProducts();

        foreach ($originalOrderProducts as $oneOriginalOrderProduct) {
            if (false === $order->getOrderProducts()->contains($oneOriginalOrderProduct)) {
                $order->removeOrderProduct($oneOriginalOrderProduct);
                $em->remove($oneOriginalOrderProduct);
                $em->flush();
            }
        }

        foreach ($orderProducts as $oneOrderProduct) {
            if ($oneOrderProduct->getAmount() == 0) {
                $order->removeOrderProduct($oneOrderProduct);
                $em->remove($oneOrderProduct);
            } else {
                if (!$originalOrderProducts->contains($oneOrderProduct)) {
                    $oneOrderProduct->setOrder($order);
                }
                $em->persist($oneOrderProduct);
            }
            $em->persist($order);
            $em->flush();
        }

        return $this->redirectToRoute('order');
    }

    return $this->render('order/new.html.twig', array('form' => $editForm->createView()));

}