Я работаю над формой, которая обрабатывает отношения N: M с дополнительным параметром (дополнительное поле / столбец). Это то, что я сделал до сих пор:
В OrdersType.php
форма:
class OrdersType extends AbstractType {
public function buildForm(FormBuilderInterface $builder, array $options)
{
// $builder fields
// $builder fields only need on edit form not in create
if ($options['curr_action'] !== NULL)
{
$builder
// other $builder fields
->add("orderProducts", "collection", array(
'type' => new OrdersHasProductType(),
'allow_add' => true,
'allow_delete' => true,
'by_reference' => false
));
}
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Tanane\FrontendBundle\Entity\Orders',
'render_fieldset' => FALSE,
'show_legend' => FALSE,
'intention' => 'orders_form',
'curr_action' => NULL
));
}
public function getName()
{
return 'orders';
}
}
В OrderHasProductType.php
:
class OrdersHasProductType extends AbstractType {
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('product', 'text', array(
'required' => FALSE,
'label' => FALSE
))
->add('amount', 'text', array(
'required' => TRUE,
'label' => FALSE
));
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Tanane\FrontendBundle\Entity\OrderHasProduct',
'intention' => 'order_has_product'
));
}
public function getName()
{
return 'order_has_product';
}
}
И, наконец, это Orders.php
а также OrdersHasProduct.php
лица:
class Orders {
use IdentifiedAutogeneratedEntityTrait;
// rest of fields for the entity
/**
* @ORM\OneToMany(targetEntity="OrderHasProduct", mappedBy="order", cascade={"all"})
*/
protected $orderProducts;
protected $products;
/**
* @ORM\Column(name="deletedAt", type="datetime", nullable=true)
*/
protected $deletedAt;
public function __construct()
{
$this->orderProducts = new ArrayCollection();
$this->products = new ArrayCollection();
}
public function getOrderProducts()
{
return $this->orderProducts;
}
public function getDeletedAt()
{
return $this->deletedAt;
}
public function setDeletedAt($deletedAt)
{
$this->deletedAt = $deletedAt;
}
public function getProduct()
{
$products = new ArrayCollection();
foreach ($this->orderProducts as $op)
{
$products[] = $op->getProduct();
}
return $products;
}
public function setProduct($products)
{
foreach ($products as $p)
{
$ohp = new OrderHasProduct();
$ohp->setOrder($this);
$ohp->setProduct($p);
$this->addPo($ohp);
}
}
public function getOrder()
{
return $this;
}
public function addPo($ProductOrder)
{
$this->orderProducts[] = $ProductOrder;
}
public function removePo($ProductOrder)
{
return $this->orderProducts->removeElement($ProductOrder);
}
}
/**
* @ORM\Entity
* @ORM\Table(name="order_has_product")
* @Gedmo\SoftDeleteable(fieldName="deletedAt")
* @UniqueEntity(fields={"order", "product"})
*/
class OrderHasProduct {
use IdentifiedAutogeneratedEntityTrait;
/**
* Hook timestampable behavior
* updates createdAt, updatedAt fields
*/
use TimestampableEntity;
/**
* @ORM\ManyToOne(targetEntity="\Tanane\FrontendBundle\Entity\Orders", inversedBy="orderProducts")
* @ORM\JoinColumn(name="general_orders_id", referencedColumnName="id")
*/
protected $order;
/**
* @ORM\ManyToOne(targetEntity="\Tanane\ProductBundle\Entity\Product", inversedBy="orderProducts")
* @ORM\JoinColumn(name="product_id", referencedColumnName="id")
*/
protected $product;
/**
* @ORM\Column(type="integer", nullable=false)
*/
protected $amount;
/**
* @ORM\Column(name="deletedAt", type="datetime", nullable=true)
*/
protected $deletedAt;
public function setOrder(\Tanane\FrontendBundle\Entity\Orders $order)
{
$this->order = $order;
}
public function getOrder()
{
return $this->order;
}
public function setProduct(\Tanane\ProductBundle\Entity\Product $product)
{
$this->product = $product;
}
public function getProduct()
{
return $this->product;
}
public function setAmount($amount)
{
$this->amount = $amount;
}
public function getAmount()
{
return $this->amount;
}
public function getDeletedAt()
{
return $this->deletedAt;
}
public function setDeletedAt($deletedAt)
{
$this->deletedAt = $deletedAt;
}
}
Но когда я пытаюсь редактировать заказ с этим кодом в моем контроллере:
public function editAction(Request $request, $id)
{
$em = $this->getDoctrine()->getManager();
$order = $em->getRepository('FrontendBundle:Orders')->find($id);
$type = $order->getPerson()->getPersonType() === 1 ? "natural" : "legal";
$params = explode('::', $request->attributes->get('_controller'));
$actionName = substr($params[1], 0, -6);
$orderForm = $this->createForm(new OrdersType(), $order, array('action' => '#', 'method' => 'POST', 'register_type' => $type, 'curr_action' => $actionName));
return array(
"form" => $orderForm->createView(),
'id' => $id,
'entity' => $order
);
}
Я получаю эту ошибку:
Ожидается, что данные вида формы будут скалярными, массивными или
экземпляр \ ArrayAccess, но является экземпляром класса
Proxies__CG __ \ Tanane \ ProductBundle \ Entity \ продукта. Вы можете избежать этого
ошибка при установке опции «data_class» в
«Proxies__CG __ \ Tanane \ ProductBundle \ Entity \ Product» или добавив
преобразователь представления, который преобразует экземпляр класса
Прокси __CG __ \ Tanane \ ProductBundle \ Entity \ Продукт в скаляр, массив или
экземпляр \ ArrayAccess.
И я не нахожу или не знаю, как это исправить, кто-нибудь может мне помочь?
Я думаю, что это происходит потому, что этот код
class OrdersHasProductType extends AbstractType {
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('product', 'text', array(
'required' => FALSE,
'label' => FALSE
))
));
}
//...
}
означает, что Symfony ожидает, что «product» будет полем типа «text», но когда он вызывает getProduct()
в OrderHasProduct он получает объект Product (или Doctrine Proxy для Product, поскольку в тот момент он не был загружен). Поля Symfony наследуются от Form / AbstractType, поэтому они по сути являются формами сами по себе, всего с одним полем, отсюда и сообщение об ошибке.
Решение состоит в том, чтобы либо создать это поле типа «сущность», либо создать другой метод, который дает только название продукта, например, getProductName()
на OrderHasProduct, а затем использовать его в качестве данных за полем.
Других решений пока нет …