Reputation: 3576
I am trying to generate a form which I think just needs two entities. However the only way I've managed to get it working is by using three. The form I have works but it is VERY slow as there are a lot of records in the database for each entity. Is there is a smarter way that I should be handling this?
What I am trying to achieve is a nested InvoiceType
form for every customer entity in the database. The admin user should then be able to tick/untick which customers they want to generate a batch of invoices for. It should look something like this:
| Generate Invoice? | Customer | Amount | Date | Notes |
|-------------------|------------|----------|------------|-----------|
| ☑ | Customer1 | 100.00 | 2016-05-08 |-----------|
| ☐ | Customer2 | 105.55 | 2016-05-09 |-----------|
Currently, I have it working by using three entities. I created an Invoicebatch entity in order to identify which batch (if any) an Invoice belongs to. However, as I need to create a new Invoice object for every Customer in the database, I am loading (I think) each Customer object into the form object in my controller:
public function batchInvoicesAction(Request $request)
{
$em = $this->getDoctrine()->getManager();
$customers = $em->getRepository('AppBundle:Customer')->findAll();
$batch = new InvoiceBatch();
foreach ($customers as $customer) {
$invoice = new Invoice();
$invoice->setCustomerId($customer);
$invoice->setSelected(True);
$invoice->setCreatedate(new \Datetime());
$invoice->setAmount($customer->getDefaultinvoiceamount());
$invoice->setinvoicebatchid($batch);
$batch->addInvoiceId($invoice);
}
$form = $this->createForm(InvoiceBatchType::class, $batch);
$form->handleRequest($request);
if ($form->isSubmitted() && ($form->isValid())) {
$batchform = $form->getData();
foreach ($batchform->getInvoiceids() as $invoiceform) {
if ($invoiceform->getSelected() == False) {
$batchform->removeInvoiceId($invoiceform);
} else {
$em->persist($invoiceform);
}
}
$em->persist($batchform);
$em->flush();
return $this->redirectToRoute('view_monthly_invoices');
}
return $this->render('invoices/new.batch.invoice.html.twig')
}
My InvoiceBatchType is as below:
class InvoiceBatchType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('batchevent', TextType::class, array(
))
->add('invoice_ids', CollectionType::class, array(
'entry_type' => InvoiceForBulkType::class,
))
;
}
My InvoiceForBulkType is:
class InvoiceForBulkType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('selected', CheckboxType::class, array(
'label' => ' ',
'required' => false,
))
->add('customer_id', EntityType::class, array(
'class' => 'AppBundle:Customer',
'choice_label' => 'FullName',
'label'=>'Customer',
'disabled'=>true,
)
)
->add('amount', TextType::class, array(
'label' => 'Amount',
))
->add('invoicedate', DateType::class, array(
'widget' => 'single_text',
'data' => new \DateTime('now'),
'format' => 'dd/MMM/yyyy',
'label' => 'Date of invoice',
'attr'=> array(
'class'=>'datepicker',
)
))
->add('description', TextType::class, array(
'required'=>false,
))
;
}
}
I was thinking it may be possible to just load the Customer names in as an array, as I just want to display a customer name on each row (ie no dropdown or other form element). I've tried a few attempts at this but with no luck so I am thinking the $customer
objects need to be included for background processes in Symfony I'm unaware of (I have a OneToMany
association between Customer and Invoice).
Upvotes: 0
Views: 2067
Reputation: 3576
I managed to get this working in the end - the problem was that I was not aware of the options for passing entities in Symfony forms. I've tried to summarise the theory/logic below in the hope it's of help to anyone else struggling to get their heads around Symfony forms:
Having read through the Symfony Form documentation I mistakenly thought that only EntityType
and CollectionType
could handle receiving an entity as input. Therefore, my InvoiceForBatchType
was causing the problem - it was loading ALL Customers into every single Invoice.
The key concept I had been missing for Symfony forms is:
If you are passing an entity that you want to be able to access, then you do not use
EntityType
(unless you want to populate dropdown/radio/ticks). Instead, you create a new file to define a nested FormType
This was then an easy fix in my case by:
1. Updating InvoiceForBulkType to:class InvoiceForBulkType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
/* ... */
->add('customer_id', CustomerForInvoiceType::class)
/* ... */
;
}
}
2. Creating CustomerForInvoiceType:
class CustomerForInvoice extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('fullname', TextType::class, array(
))
;
}
/* ... */
}
Upvotes: 1