I am making an invoicing system with an embedded form. A form where you can add a date, choose a customer company from a dropdown list (-> customer is an entity) and add details to the invoice item (-> details is also an entity, with properties like price, amount,...) - with javascript. This works just fine, but at saving the form I get an error. I have 3 entities: InvoicingCustomer, InvoiceItem, InvoiceItemDetail. (Sorry; this is going to be a long post)
InvoicingCustomer.php (with properties like street, address,...) =
`/**
* @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;
}`
And off course the getters and setters.
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
}'
Then, I got the types. 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'; }
} '
In my controller I have this (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(),
));
}
}'
In my twig it's just like:
'{% block body -%}
<h1>Invoice item creation</h1>
{{ form(form) }}
{% endblock %}'
Everything in the form is displayed good (and with javascript I can add several details to one invoice item). But when I submit the form, symfony throws an error:
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
I searched around on the docs of symfony (http://ift.tt/YnVJYl ) and on stackoverflow (for example: Saving embedded collections ), but none of these give me the right solution.
I know this is a long post: I am sorry. But I don't know how to sort this problem out (+ I am new in learning symfony2 & new in asking questions here).
Thank you in advance!
Aucun commentaire:
Enregistrer un commentaire