我正在制作一个嵌入式表格的发票系统。一个表格,您可以在其中添加日期,从下拉列表中选择客户公司( - >客户是实体)并向发票项目添加详细信息( - >详细信息也是一个实体,具有价格,金额等属性, ...) - 用javascript。这工作得很好,但在保存表单时我收到错误。 我有3个实体:InvoicingCustomer,InvoiceItem,InvoiceItemDetail。 (对不起,这将是一个很长的帖子)
InvoicingCustomer.php (包含街道,地址等属性)=
/**
* @ORM\Table(name="invoicing_customer")
* @ORM\Entity
*/
class InvoicingCustomer
{
/**
* @ORM\OneToMany(targetEntity="Invoicing\InvoicingBundle\Entity\InvoiceItem", mappedBy="customer")
*/
private $invoice;
public function __construct()
{ $this->invoice = new ArrayCollection();}
public function getInvoice()
{ return $this->invoice; }
public function getAllInvoices()
{
$invoices = $this->getInvoice()->toArray();
return $invoices;
}
/**
* @var integer
* @ORM\Column(name="id", type="integer")
* @ORM\Id
* @ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id;
/**
* @var string
* @ORM\Column(name="company_name", type="string", length=50, nullable=false)
* @Assert\NotBlank()
*/
private $companyName;
//idem for next properties:
private $firstName;
private $lastName;
private $street;
private $number;
private $postalCode;
private $city;
}
当然还有吸气剂和制定者。
InvoiceItem.php =
/**
* @ORM\Table(name="invoicing_invoice_item")
* @ORM\Entity
*/
class InvoiceItem
{
/**
* @ORM\OneToMany(targetEntity="Invoicing\InvoicingBundle\Entity\InvoiceItemDetail", mappedBy="item_nr", cascade={"ALL"}, fetch="EAGER", orphanRemoval=true)
*/
private $item_detail;
public function __construct()
{ $this->item_detail = new ArrayCollection(); }
/**
* @return mixed
*/
public function getItemDetail()
{ return $this->item_detail; }
/**
* @param mixed $item_detail
*/
public function setItemDetail(Collection $item_detail)
{
foreach ($item_detail as $v)
{
if (is_null($v->getId()))
{
$v->getId($this);
}
}
$this->item_detail = $item_detail;
}
public function addDetail(InvoiceItemDetail $detail){
$detail->$this->setItemDetail($this);
$this->detail[] = $detail;
return $this;
}
public function removeDetail(InvoiceItemDetail $detail){
//
}
/**
* @var integer
* @ORM\Column(name="id", type="integer")
* @ORM\Id
* @ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id;
/**
* @var \DateTime
* @ORM\Column(name="date", type="date", nullable=false)
*/
private $date;
/**
* @ORM\ManyToOne(targetEntity="Invoicing\CustomerBundle\Entity\InvoicingCustomer", inversedBy="invoice")
* @ORM\JoinColumn(onDelete="CASCADE", nullable=false)
* @Assert\Type(type="Invoicing\CustomerBundle\Entity\InvoicingCustomer")
* @Assert\Valid()
*
*/
private $customer;
// here also getters and setters
}
InvoiceItemDetail.php =
/**
* @ORM\Table(name="invoicing_invoice_itemdetail")
* @ORM\Entity
*/
class InvoiceItemDetail
{
/**
* @var integer
* @ORM\Column(name="id", type="integer")
* @ORM\Id
* @ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id;
/**
* @ORM\Column(name="description", type="text", length=200, nullable=false)
*/
private $description;
/**
* @var string
* @ORM\Column(name="price", type="decimal", precision=10, scale=0, nullable=false)
*/
private $price;
/**
* @var integer
* @ORM\Column(name="amount", type="decimal", precision=10, scale=0, nullable=false)
*/
private $amount;
/**
* @ORM\ManyToOne(targetEntity="Invoicing\InvoicingBundle\Entity\InvoiceItem", inversedBy="item_detail" )
* @ORM\JoinColumn(onDelete="CASCADE", nullable=false, name="item_nr_id", referencedColumnName="id")
* @Assert\Type(type="Invoicing\InvoicingBundle\Entity\InvoiceItem")
* @Assert\Valid()
*/
private $item_nr;
// + getters and setters
}
然后,我得到了类型。 InvoiceItemType.php =
class InvoiceItemType extends AbstractType
{
/**
* @param FormBuilderInterface $builder
* @param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('date', 'date', array(
'format' => 'dd-MM-yyyy',
'empty_value' => array('year' => 'Year', 'month' => 'Month', 'day' => 'Day'),
'years' => range(date('Y') -1, date('Y')),
))
->add('customer', null, array(
'empty_value' => 'Choose a company',
'label' => 'Company',
'required' => true,
))
->add('item_detail', 'collection', array(
'type' => new InvoiceItemDetailType(),
'allow_add' => true,
'constraints' => new NotBlank(),
'by_reference' => false,
));
}
public function getName()
{ return 'invoiceitem'; }
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Invoicing\InvoicingBundle\Entity\InvoiceItem',
));
}
}
InvoicingCustomerType.php =
class InvoicingCustomerType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('companyName', 'text');
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Invoicing\CustomerBundle\Entity\InvoicingCustomer',
));
}
public function getName()
{ return 'customer'; }
}
InvoiceItemDetailType.php =
class InvoiceItemDetailType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('description', 'text')
->add('price', 'number', array(
'label' => 'Price - €',
))
->add('amount', 'number');
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Invoicing\InvoicingBundle\Entity\InvoiceItemDetail',
));
}
public function getName()
{ return 'detail'; }
}
在我的控制器中我有这个( InvoiceItemController.php ):
/** InvoiceItem controller */
class InvoiceItemController extends Controller
{
/**
* Creates a new invoiceitem entity.
*/
public function createAction(Request $request)
{
$entity = new InvoiceItem();
$form = $this->createCreateForm($entity);
$form->handleRequest($request);
if ($form->isValid()) {
$em = $this->getDoctrine()->getManager();
// hack to work around handleRequest not using class methods to populate data
foreach($entity->getItemDetail() as $detail){
foreach($detail as $i){
// if i didn't made a second loop, I get an error: "object could not be converted to string..."
$i->this->setItemNr($entity);
$em->persist($i);
}
}
$em->persist($entity);
$em->flush();
return $this->redirect($this->generateUrl('invoiceitem_show', array('id' => $entity->getId())));
}
return $this->render('InvoicingBundle:InvoiceItem:new.html.twig', array(
'entity' => $entity,
'form' => $form->createView(),
));
}
}
在我的树枝上它就像:
{% block body -%}
<h1>Invoice item creation</h1>
{{ form(form) }}
{% endblock %}
表单中的所有内容都显示良好(使用javascript我可以将一些详细信息添加到一个发票项目)。但是当我提交表单时,symfony会抛出一个错误:
An exception occurred while executing 'INSERT INTO invoicing_invoice_itemdetail (description, price, amount, item_nr_id) VALUES (?, ?, ?, ?)' with params ["test", 300, 1, null]:
SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'item_nr_id' cannot be null
我搜索了symfony(http://symfony.com/doc/current/cookbook/form/form_collections.html)和stackoverflow(例如:Saving embedded collections)的文档,但这些都没有给我正确的解决方案。
我知道这是一篇很长的帖子:对不起。但是我不知道如何解决这个问题(+我是学习symfony2的新手,也是新问题)。
答案 0 :(得分:1)
我相信您的问题出在InvoiceItem实体中。尝试创建方法addItemDetail(或者addInvoiceItemDetail)而不是addDetail。你也可以删除方法setItemDetail,也许你会看到很好的解释Symfony寻找的方法。
public function addItemDetail(InvoiceItemDetail $detail){
$detail->setItemNr($this);
$this->item_detail[] = $detail;
return $this;
}
从控制器中删除黑客。
// hack to work around handleRequest not using class methods to populate data
foreach($entity->getItemDetail() as $detail){
foreach($detail as $i){
// if i didn't made a second loop, I get an error: "object could not be converted to string..."
$i->this->setItemNr($entity);
$em->persist($i);
}
}
我希望它有所帮助,但如果没有实时代码,就很难回答这个问题。
答案 1 :(得分:0)
Symfony表单的概念是,对于关系,您还可以在表单类型中指定相关实体。 在您的情况下,您没有将InvoiceItemType添加到Type ItemInvoiceDetail: 我希望您的InvoiceDetailType中包含以下代码:
class InvoiceItemDetailType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('description', 'text')
->add('invoice_item', 'invoice_item', array('error_bubbling' => true, 'mapped' => true))
->add('price', 'number', array(
'label' => 'Price - €',
))
->add('amount', 'number');
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Invoicing\InvoicingBundle\Entity\InvoiceItemDetail',
));
}
public function getName()
{ return 'detail'; }
注意我将表单元素的类型设置为invoice_item。 您可以通过将其定义为服务来实现这一目标:
service_name:
class: invoiceitemfullclasspath
arguments: []
tags:
- { name: form.type, alias: invoice_item }