页面提交后向 Symfony2 表单集合类型添加其他项目
Add additional items to a Symfony2 Form Collection type after page submit
我有一个基于购买想法的表格。它具有购买项目、付款金额和付款类型(VISA、Mastercard、现金)。我的表格预装了 2 个购买项目,但是如果用户选择卡支付类型(VISA 或万事达卡)类型,我会尝试添加一个额外的购买项目。
我正尝试通过控制器添加这个额外的购买项目。
真正的问题是,我应该在 Controller Action 中的何处实现此功能...还是作为 Form Type 上的 EventListener 更好?
当表格与额外的卡费购买项目一起提交时,我收到以下错误...
Catchable Fatal Error: Argument 1 passed to Aazp\BookingBundle\Entity\PurchaseItem::__construct() must be an instance of Aazp\BookingBundle\Entity\Product, none given, called in /project/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/Type/FormType.php on line 141 and defined in /project/src/Aazp/BookingBundle/Entity/PurchaseItem.php line 20
预订控制器
namespace Aazp\BookingBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Aazp\BookingBundle\Entity\Booking;
use Aazp\BookingBundle\Entity\Passenger;
use Aazp\BookingBundle\Entity\Payment;
use Aazp\BookingBundle\Entity\Purchase;
use Aazp\BookingBundle\Entity\PurchaseItem;
use Aazp\BookingBundle\Form\PurchaseItemType;
use Aazp\BookingBundle\Form\PurchaseType;
use Doctrine\Common\Collections\ArrayCollection;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Session\Session;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;
class BookingController extends Controller
{
public function passengerPaymentAction($passenger_id)
{
$request = Request::createFromGlobals();
$purchaseBalance = 0.0;
//Query the selected Passenger
$em = $this->getDoctrine()->getManager();
$passenger = $request->attributes->get('passenger', $em->getRepository('AazpBookingBundle:Passenger')->find($passenger_id));
if (!$passenger) {
throw $this->createNotFoundException('Unable to find Passenger entity.');
}
$purchase = $passenger->getPurchase();
//Has this Passenger made a Payment. If yes then Purchase exists.
if($purchase === NULL) //If Purchase does not exist then preload the form with default products.
{
$purchase = new Purchase();
$product_category_photo = $em->getRepository('AazpBookingBundle:ProductCategory')->findOneByName('FLIGHT-PHOTO');
$product_photo_option = $em->getRepository('AazpBookingBundle:Product')->findOneByProductCategory($product_category_photo);
if (!$product_photo_option) {
throw $this->createNotFoundException('Unable to find Flight Photo Product entity.');
}
$purchase_item_flight = new PurchaseItem($passenger->getFlight());
$purchase_item_photo_option = new PurchaseItem($product_photo_option);
//Add Purchase Items to the Purchase
$purchase->addPurchaseItem($purchase_item_flight);
$purchase->addPurchaseItem($purchase_item_photo_option);
//Set the Purchase on the Passenger
$passenger->setPurchase($purchase);
}
//Ajax call triggered by onChange event on PaymentType radio button in form
//Add additional Purchase Item for Card Type Payment
if($form->get('paymentType')->getData()->getId() > 1)
{
//PaymentType selected/modified then calculate Payment Fee
$product_category_card_fee = $em->getRepository('AazpBookingBundle:ProductCategory')->findOneByName('CARD-FEE');
$product_card_fee = $em->getRepository('AazpBookingBundle:Product')->findOneByProductCategory($product_category_card_fee);
if (!$productcard_fee) {
throw $this->createNotFoundException('Unable to find Card Fee Product entity.');
}
$purchase_item_card_fee = new PurchaseItem($product_card_fee);
//Add Purchase Items to the Purchase
$purchase->addPurchaseItem($purchase_item_card_fee);
$passenger->setPurchase($purchase);
return $this->render('AazpBookingBundle:Purchase:summary.html.twig', array(
'passenger' => $passenger,
'form' => $form->createView(),
));
}
$form = $this->createForm(new PurchaseType($em), $purchase);
$form->handleRequest($request);
//If form is Valid create Payment and persist.
if ($form->isValid())
{
$payment = new Payment();
$payment->setAmount($form->get('paymentAmount')->getData());
$payment->setPaymentType($form->get('paymentType')->getData());
$payment->setDescription($form->get('description')->getData());
$passenger->getPurchase()->addPayment($payment);
$passenger->getBooking()->setStatus(Booking::STATUS_CONFIRMED);
$em->persist($passenger->getPurchase());
$em->flush();
$this->get('session')->getFlashBag()->add('message', 'Payment '.$payment->getAmount().' CHF has been successful!');
return $this->redirect($this->generateUrl('booking_show', array ('id'=> $passenger->getBooking()->getId())));
}
return $this->render('AazpBookingBundle:Purchase:summary.html.twig', array(
'passenger' => $passenger,
'form' => $form->createView(),
));
}
}
PurchaseItemType
namespace Aazp\BookingBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
use Aazp\BookingBundle\Entity\ProductRepository;
class PurchaseItemType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('product', 'entity', array('label' => 'Flight', 'class' => 'AazpBookingBundle:Product','property' => 'name', 'empty_value' => 'Please Select', 'required' => false, ));
$builder->add('amount', 'number', array('precision' => 2));
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array('data_class' => 'Aazp\BookingBundle\Entity\PurchaseItem', ));
}
public function getName()
{
return 'purchaseItem';
}
}
购买类型
namespace Aazp\BookingBundle\Form;
use Doctrine\ORM\EntityManager;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;
use Aazp\BookingBundle\Entity\PurchaseItem;
class PurchaseType extends AbstractType
{
protected $em;
function __construct(EntityManager $em)
{
$this->em = $em;
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('purchaseItems', 'collection', array('type' => new PurchaseItemType(), 'allow_add' => true, 'allow_delete' => true, 'by_reference' => false));
$builder->add('paymentType', 'entity', array('label' => 'Payment Type', 'class' => 'AazpBookingBundle:PaymentType','property' => 'name', 'mapped' => false, 'expanded' => true));
$builder->add('paymentAmount', 'number', array('precision' => 2, 'data' => 0.0, 'mapped' => false));
$builder->add('description', 'text', array('mapped' => false, 'required' => false));
$builder->add('cancel', 'submit', array('attr' => array('formnovalidate' => true, 'data-toggle' => 'modal', 'data-target' => '#cancelWarning', )));
$builder->add('pay', 'submit');
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array('data_class' => 'Aazp\BookingBundle\Entity\Purchase', ));
}
public function getName()
{
return 'purchase';
}
}
采购实体
<?php
namespace Aazp\BookingBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Gedmo\Mapping\Annotation as Gedmo;
use Symfony\Component\Validator\Constraints as Assert;
use Aazp\MainBundle\Entity\BaseEntity;
/**
* @ORM\Entity(repositoryClass="Aazp\BookingBundle\Entity\PurchaseRepository")
* @ORM\Table(name="purchase")
* @Gedmo\SoftDeleteable(fieldName="deleted")
*/
class Purchase extends BaseEntity
{
/**
* Constructor
*/
public function __construct()
{
$this->purchaseItems = new \Doctrine\Common\Collections\ArrayCollection();
$this->payments = new \Doctrine\Common\Collections\ArrayCollection();
}
/**
* @ORM\Column(type="integer")
* @ORM\Id
* @ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* @ORM\ManyToMany(targetEntity="PurchaseItem", cascade={"all"})
* @ORM\JoinTable(name="purchase_purchase_items",
* joinColumns={@ORM\JoinColumn(name="purchase_id", referencedColumnName="id")},
* inverseJoinColumns={@ORM\JoinColumn(name="purchase_item_id", referencedColumnName="id", unique=true)}
* )
**/
protected $purchaseItems;
/**
* @ORM\ManyToMany(targetEntity="Payment", inversedBy="purchases", cascade={"all"})
* @ORM\JoinTable(name="purchases_payments",
* joinColumns={@ORM\JoinColumn(name="purchase_id", referencedColumnName="id")},
* inverseJoinColumns={@ORM\JoinColumn(name="payment_id", referencedColumnName="id")}
* )
**/
protected $payments;
/**
* @ORM\OneToOne(targetEntity="Passenger", mappedBy="purchase")
**/
protected $passenger;
/**
* Get id
*
* @return integer
*/
public function getId()
{
return $this->id;
}
/**
* Add purchaseItems
*
* @param \Aazp\BookingBundle\Entity\PurchaseItem $purchaseItems
* @return Purchase
*/
public function addPurchaseItem(\Aazp\BookingBundle\Entity\PurchaseItem $purchaseItems)
{
$this->purchaseItems[] = $purchaseItems;
return $this;
}
/**
* Remove purchaseItems
*
* @param \Aazp\BookingBundle\Entity\PurchaseItem $purchaseItems
*/
public function removePurchaseItem(\Aazp\BookingBundle\Entity\PurchaseItem $purchaseItems)
{
$this->purchaseItems->removeElement($purchaseItems);
}
/**
* Get purchaseItems
*
* @return \Doctrine\Common\Collections\Collection
*/
public function getPurchaseItems()
{
return $this->purchaseItems;
}
/**
* Add payments
*
* @param \Aazp\BookingBundle\Entity\Payment $payments
* @return Purchase
*/
public function addPayment(\Aazp\BookingBundle\Entity\Payment $payments)
{
$this->payments[] = $payments;
return $this;
}
/**
* Remove payments
*
* @param \Aazp\BookingBundle\Entity\Payment $payments
*/
public function removePayment(\Aazp\BookingBundle\Entity\Payment $payments)
{
$this->payments->removeElement($payments);
}
/**
* Get payments
*
* @return \Doctrine\Common\Collections\Collection
*/
public function getPayments()
{
return $this->payments;
}
/**
* Set passenger
*
* @param \Aazp\BookingBundle\Entity\Passenger $passenger
* @return Purchase
*/
public function setPassenger(\Aazp\BookingBundle\Entity\Passenger $passenger = null)
{
$this->passenger = $passenger;
return $this;
}
/**
* Get passenger
*
* @return \Aazp\BookingBundle\Entity\Passenger
*/
public function getPassenger()
{
return $this->passenger;
}
}
发生这种情况是因为默认 data_class
选项不带参数调用该构造函数。如果你的classAazp\Booking Bundle\Entity\PurchaseItem
在构造函数中有参数,需要使用empty_data
选项实例化:
// Aazp\BookingBundle\Form\PurchaseItemType.php
$resolver->setDefaults([
'empty_data' => function (FormInterface $form) {
return new PurchaseItem($form->get('product')->getData());
},
]);
您可以进一步了解此选项 here。
我有一个基于购买想法的表格。它具有购买项目、付款金额和付款类型(VISA、Mastercard、现金)。我的表格预装了 2 个购买项目,但是如果用户选择卡支付类型(VISA 或万事达卡)类型,我会尝试添加一个额外的购买项目。 我正尝试通过控制器添加这个额外的购买项目。
真正的问题是,我应该在 Controller Action 中的何处实现此功能...还是作为 Form Type 上的 EventListener 更好?
当表格与额外的卡费购买项目一起提交时,我收到以下错误...
Catchable Fatal Error: Argument 1 passed to Aazp\BookingBundle\Entity\PurchaseItem::__construct() must be an instance of Aazp\BookingBundle\Entity\Product, none given, called in /project/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/Type/FormType.php on line 141 and defined in /project/src/Aazp/BookingBundle/Entity/PurchaseItem.php line 20
预订控制器
namespace Aazp\BookingBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Aazp\BookingBundle\Entity\Booking;
use Aazp\BookingBundle\Entity\Passenger;
use Aazp\BookingBundle\Entity\Payment;
use Aazp\BookingBundle\Entity\Purchase;
use Aazp\BookingBundle\Entity\PurchaseItem;
use Aazp\BookingBundle\Form\PurchaseItemType;
use Aazp\BookingBundle\Form\PurchaseType;
use Doctrine\Common\Collections\ArrayCollection;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Session\Session;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;
class BookingController extends Controller
{
public function passengerPaymentAction($passenger_id)
{
$request = Request::createFromGlobals();
$purchaseBalance = 0.0;
//Query the selected Passenger
$em = $this->getDoctrine()->getManager();
$passenger = $request->attributes->get('passenger', $em->getRepository('AazpBookingBundle:Passenger')->find($passenger_id));
if (!$passenger) {
throw $this->createNotFoundException('Unable to find Passenger entity.');
}
$purchase = $passenger->getPurchase();
//Has this Passenger made a Payment. If yes then Purchase exists.
if($purchase === NULL) //If Purchase does not exist then preload the form with default products.
{
$purchase = new Purchase();
$product_category_photo = $em->getRepository('AazpBookingBundle:ProductCategory')->findOneByName('FLIGHT-PHOTO');
$product_photo_option = $em->getRepository('AazpBookingBundle:Product')->findOneByProductCategory($product_category_photo);
if (!$product_photo_option) {
throw $this->createNotFoundException('Unable to find Flight Photo Product entity.');
}
$purchase_item_flight = new PurchaseItem($passenger->getFlight());
$purchase_item_photo_option = new PurchaseItem($product_photo_option);
//Add Purchase Items to the Purchase
$purchase->addPurchaseItem($purchase_item_flight);
$purchase->addPurchaseItem($purchase_item_photo_option);
//Set the Purchase on the Passenger
$passenger->setPurchase($purchase);
}
//Ajax call triggered by onChange event on PaymentType radio button in form
//Add additional Purchase Item for Card Type Payment
if($form->get('paymentType')->getData()->getId() > 1)
{
//PaymentType selected/modified then calculate Payment Fee
$product_category_card_fee = $em->getRepository('AazpBookingBundle:ProductCategory')->findOneByName('CARD-FEE');
$product_card_fee = $em->getRepository('AazpBookingBundle:Product')->findOneByProductCategory($product_category_card_fee);
if (!$productcard_fee) {
throw $this->createNotFoundException('Unable to find Card Fee Product entity.');
}
$purchase_item_card_fee = new PurchaseItem($product_card_fee);
//Add Purchase Items to the Purchase
$purchase->addPurchaseItem($purchase_item_card_fee);
$passenger->setPurchase($purchase);
return $this->render('AazpBookingBundle:Purchase:summary.html.twig', array(
'passenger' => $passenger,
'form' => $form->createView(),
));
}
$form = $this->createForm(new PurchaseType($em), $purchase);
$form->handleRequest($request);
//If form is Valid create Payment and persist.
if ($form->isValid())
{
$payment = new Payment();
$payment->setAmount($form->get('paymentAmount')->getData());
$payment->setPaymentType($form->get('paymentType')->getData());
$payment->setDescription($form->get('description')->getData());
$passenger->getPurchase()->addPayment($payment);
$passenger->getBooking()->setStatus(Booking::STATUS_CONFIRMED);
$em->persist($passenger->getPurchase());
$em->flush();
$this->get('session')->getFlashBag()->add('message', 'Payment '.$payment->getAmount().' CHF has been successful!');
return $this->redirect($this->generateUrl('booking_show', array ('id'=> $passenger->getBooking()->getId())));
}
return $this->render('AazpBookingBundle:Purchase:summary.html.twig', array(
'passenger' => $passenger,
'form' => $form->createView(),
));
}
}
PurchaseItemType
namespace Aazp\BookingBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
use Aazp\BookingBundle\Entity\ProductRepository;
class PurchaseItemType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('product', 'entity', array('label' => 'Flight', 'class' => 'AazpBookingBundle:Product','property' => 'name', 'empty_value' => 'Please Select', 'required' => false, ));
$builder->add('amount', 'number', array('precision' => 2));
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array('data_class' => 'Aazp\BookingBundle\Entity\PurchaseItem', ));
}
public function getName()
{
return 'purchaseItem';
}
}
购买类型
namespace Aazp\BookingBundle\Form;
use Doctrine\ORM\EntityManager;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;
use Aazp\BookingBundle\Entity\PurchaseItem;
class PurchaseType extends AbstractType
{
protected $em;
function __construct(EntityManager $em)
{
$this->em = $em;
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('purchaseItems', 'collection', array('type' => new PurchaseItemType(), 'allow_add' => true, 'allow_delete' => true, 'by_reference' => false));
$builder->add('paymentType', 'entity', array('label' => 'Payment Type', 'class' => 'AazpBookingBundle:PaymentType','property' => 'name', 'mapped' => false, 'expanded' => true));
$builder->add('paymentAmount', 'number', array('precision' => 2, 'data' => 0.0, 'mapped' => false));
$builder->add('description', 'text', array('mapped' => false, 'required' => false));
$builder->add('cancel', 'submit', array('attr' => array('formnovalidate' => true, 'data-toggle' => 'modal', 'data-target' => '#cancelWarning', )));
$builder->add('pay', 'submit');
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array('data_class' => 'Aazp\BookingBundle\Entity\Purchase', ));
}
public function getName()
{
return 'purchase';
}
}
采购实体
<?php
namespace Aazp\BookingBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Gedmo\Mapping\Annotation as Gedmo;
use Symfony\Component\Validator\Constraints as Assert;
use Aazp\MainBundle\Entity\BaseEntity;
/**
* @ORM\Entity(repositoryClass="Aazp\BookingBundle\Entity\PurchaseRepository")
* @ORM\Table(name="purchase")
* @Gedmo\SoftDeleteable(fieldName="deleted")
*/
class Purchase extends BaseEntity
{
/**
* Constructor
*/
public function __construct()
{
$this->purchaseItems = new \Doctrine\Common\Collections\ArrayCollection();
$this->payments = new \Doctrine\Common\Collections\ArrayCollection();
}
/**
* @ORM\Column(type="integer")
* @ORM\Id
* @ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* @ORM\ManyToMany(targetEntity="PurchaseItem", cascade={"all"})
* @ORM\JoinTable(name="purchase_purchase_items",
* joinColumns={@ORM\JoinColumn(name="purchase_id", referencedColumnName="id")},
* inverseJoinColumns={@ORM\JoinColumn(name="purchase_item_id", referencedColumnName="id", unique=true)}
* )
**/
protected $purchaseItems;
/**
* @ORM\ManyToMany(targetEntity="Payment", inversedBy="purchases", cascade={"all"})
* @ORM\JoinTable(name="purchases_payments",
* joinColumns={@ORM\JoinColumn(name="purchase_id", referencedColumnName="id")},
* inverseJoinColumns={@ORM\JoinColumn(name="payment_id", referencedColumnName="id")}
* )
**/
protected $payments;
/**
* @ORM\OneToOne(targetEntity="Passenger", mappedBy="purchase")
**/
protected $passenger;
/**
* Get id
*
* @return integer
*/
public function getId()
{
return $this->id;
}
/**
* Add purchaseItems
*
* @param \Aazp\BookingBundle\Entity\PurchaseItem $purchaseItems
* @return Purchase
*/
public function addPurchaseItem(\Aazp\BookingBundle\Entity\PurchaseItem $purchaseItems)
{
$this->purchaseItems[] = $purchaseItems;
return $this;
}
/**
* Remove purchaseItems
*
* @param \Aazp\BookingBundle\Entity\PurchaseItem $purchaseItems
*/
public function removePurchaseItem(\Aazp\BookingBundle\Entity\PurchaseItem $purchaseItems)
{
$this->purchaseItems->removeElement($purchaseItems);
}
/**
* Get purchaseItems
*
* @return \Doctrine\Common\Collections\Collection
*/
public function getPurchaseItems()
{
return $this->purchaseItems;
}
/**
* Add payments
*
* @param \Aazp\BookingBundle\Entity\Payment $payments
* @return Purchase
*/
public function addPayment(\Aazp\BookingBundle\Entity\Payment $payments)
{
$this->payments[] = $payments;
return $this;
}
/**
* Remove payments
*
* @param \Aazp\BookingBundle\Entity\Payment $payments
*/
public function removePayment(\Aazp\BookingBundle\Entity\Payment $payments)
{
$this->payments->removeElement($payments);
}
/**
* Get payments
*
* @return \Doctrine\Common\Collections\Collection
*/
public function getPayments()
{
return $this->payments;
}
/**
* Set passenger
*
* @param \Aazp\BookingBundle\Entity\Passenger $passenger
* @return Purchase
*/
public function setPassenger(\Aazp\BookingBundle\Entity\Passenger $passenger = null)
{
$this->passenger = $passenger;
return $this;
}
/**
* Get passenger
*
* @return \Aazp\BookingBundle\Entity\Passenger
*/
public function getPassenger()
{
return $this->passenger;
}
}
发生这种情况是因为默认 data_class
选项不带参数调用该构造函数。如果你的classAazp\Booking Bundle\Entity\PurchaseItem
在构造函数中有参数,需要使用empty_data
选项实例化:
// Aazp\BookingBundle\Form\PurchaseItemType.php
$resolver->setDefaults([
'empty_data' => function (FormInterface $form) {
return new PurchaseItem($form->get('product')->getData());
},
]);
您可以进一步了解此选项 here。