Сохранить коллекцию форм с doctrine и symfony 3.4 - PullRequest
0 голосов
/ 09 января 2020

Я новичок в Symfony и пытаюсь создать форму для сохранения счета-фактуры, а затем коллекцию дополнительных элементов счета-фактуры, связанных с этим счетом (с отношением OneToMany), но когда я отправляю форму, я получаю следующая ошибка:

The class 'Doctrine\Common\Collections\ArrayCollection' was not found in the chain configured namespaces App\Entity

Вот родительская сущность:

    <?php

namespace App\Entity;

use ApiPlatform\Core\Annotation\ApiResource;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;

/**
 * @ApiResource()
 * @ORM\Entity(repositoryClass="App\Repository\InvoiceRepository")
 */
class Invoice
{
    /**
     * @ORM\Id()
     * @ORM\GeneratedValue()
     * @ORM\Column(type="integer")
     */
    private $id;

    /**
     * @ORM\Column(type="date")
     */
    private $invoice_date;

    /**
     * @ORM\Column(type="integer")
     */
    private $invoice_number;

     /**
     * @ORM\Column(type="integer")
     */
    private $client;

    /**
     * @ORM\OneToMany(targetEntity="App\Entity\InvoiceItem",cascade={"persist"}, mappedBy="invoice", orphanRemoval=true)
     */
    private $invoiceItems;

    public function __construct()
    {
        $this->invoiceItems = new ArrayCollection();
    }

    public function getId(): ?int
    {
        return $this->id;
    }

    public function getInvoiceDate(): ?\DateTimeInterface
    {
        return $this->invoice_date;
    }

    public function setInvoiceDate(\DateTimeInterface $invoice_date): self
    {
        $this->invoice_date = $invoice_date;

        return $this;
    }
    public function getClient(): ?int
    {
        return $this->client;
    }

    public function setClient(int $client): self
    {
        $this->client = $client;

        return $this;
    }

    public function getInvoiceNumber(): ?int
    {
        return $this->invoice_number;
    }

    public function setInvoiceNumber(int $invoice_number): self
    {
        $this->invoice_number = $invoice_number;

        return $this;
    }



    /**
     * @return Collection|InvoiceItem[]
     */
    public function getInvoiceItems(): Collection
    {
        return $this->invoiceItems;
    }

    public function addInvoiceItem(InvoiceItem $invoiceItem): self
    {
        if (!$this->invoiceItems->contains($invoiceItem)) {
            $this->invoiceItems[] = $invoiceItem;
            $invoiceItem->setInvoice($this);
        }

        return $this;
    }

    public function removeInvoiceItem(InvoiceItem $invoiceItem): self
    {
        if ($this->invoiceItems->contains($invoiceItem)) {
            $this->invoiceItems->removeElement($invoiceItem);
            // set the owning side to null (unless already changed)
            if ($invoiceItem->getInvoice() === $this) {
                $invoiceItem->setInvoice(null);
            }
        }

        return $this;
    }
}

, а вот InvoiceItemEntity

<?php

namespace App\Entity;

use ApiPlatform\Core\Annotation\ApiResource;
use Doctrine\ORM\Mapping as ORM;

/**
 * @ApiResource()
 * @ORM\Entity(repositoryClass="App\Repository\InvoiceItemRepository")
 */
class InvoiceItem
{
    /**
     * @ORM\Id()
     * @ORM\GeneratedValue()
     * @ORM\Column(type="integer")
     */
    private $id;

    /**
     * @ORM\ManyToOne(targetEntity="App\Entity\Invoice", inversedBy="invoiceItems")
     * @ORM\JoinColumn(nullable=false)
     */
    private $invoice;

    /**
     * @ORM\Column(type="text")
     */
    private $description;

    /**
     * @ORM\Column(type="integer")
     */
    private $quantity;

    /**
     * @ORM\Column(type="decimal", precision=12, scale=2)
     */
    private $net_amount;

    /**
     * @ORM\Column(type="decimal", precision=12, scale=2, nullable=true)
     */
    private $vat_amount;

    /**
     * @ORM\Column(type="decimal", precision=12, scale=2)
     */
    private $gross_amount;

    public function getId(): ?int
    {
        return $this->id;
    }

    public function getInvoice(): ?Invoice
    {
        return $this->invoice;
    }

    public function setInvoice(?Invoice $invoice): self
    {
        $this->invoice = $invoice;

        return $this;
    }

    public function getDescription(): ?string
    {
        return $this->description;
    }

    public function setDescription(string $description): self
    {
        $this->description = $description;

        return $this;
    }

    public function getQuantity(): ?int
    {
        return $this->quantity;
    }

    public function setQuantity(int $quantity): self
    {
        $this->quantity = $quantity;

        return $this;
    }

    public function getNetAmount(): ?string
    {
        return $this->net_amount;
    }

    public function setNetAmount(string $net_amount): self
    {
        $this->net_amount = $net_amount;

        return $this;
    }

    public function getVatAmount(): ?string
    {
        return $this->vat_amount;
    }

    public function setVatAmount(?string $vat_amount): self
    {
        $this->vat_amount = $vat_amount;

        return $this;
    }

    public function getGrossAmount(): ?string
    {
        return $this->gross_amount;
    }

    public function setGrossAmount(string $gross_amount): self
    {
        $this->gross_amount = $gross_amount;

        return $this;
    }

    public function removeInvoiceItem(InvoiceItem $invoiceItem)
    {
        $this->tags->removeElement($invoiceItem);
    }
}

InvoiceType. php:

// src/Form/InvoiceType.php
namespace App\Form;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;

use App\Entity\Invoice;
use App\Entity\InvoiceItem;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\CollectionType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;

class InvoiceType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder->add('invoice_date')
        ->add('invoice_number')
        ->add('client', ChoiceType::class, [
           "choices" => ["Mario Rossi" => 1,
            "Maria Rossi"=> 2,
            "Mario Bianchi"=> 3]
        ])
        ->add('invoice_items', CollectionType::class, [
            'by_reference' => false,
            'entry_type' => InvoiceItemType::class,
            'entry_options' => ['label' => false],
            'allow_add' => true,
            'allow_delete' => true,
            'prototype' => true,

        ]);
    }

    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults([
            'data_class' => Invoice::class,
        ]);
    }
}

и InvoiceItemType. php:

<?php // src/Form/InvoiceItemType.php
namespace App\Form;

use App\Entity\InvoiceItem;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;

class InvoiceItemType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder->add('description')
        ->add('quantity')
        ->add('net_amount')
        ->add('vat_amount')
        ->add('gross_amount');
    }

    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults([
            'data_class' => InvoiceItem::class,
            'entry_options' => [
                'label' => false,


            ],

        ]);
    }
}

и, наконец, InvoiceGeneratorController:

<?php // src/Controller/InvoiceController.php
namespace App\Controller;
use App\Entity\InvoiceItem;
use App\Entity\Invoice;
use App\Form\InvoiceType;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;

class InvoiceController extends AbstractController
{
    public function form(Request $request)
    {
        $invoice = new Invoice();


        $invoiceItem1 = new InvoiceItem();
        $invoice->getInvoiceItems()->add($invoiceItem1);


        $form = $this->createForm(InvoiceType::class, $invoice);
        $entityManager = $this->getDoctrine()->getManager();

        $form->handleRequest($request);
    if ($form->isSubmitted() && $form->isValid()) {

        $invoice = $form->getData();
         $entityManager->persist($invoice);
         $entityManager->persist($invoice->getInvoiceItems());         
         $entityManager->flush();

    }



        return $this->render('invoice.html.twig', [
            'form' => $form->createView(),
        ]);
    }
    public function success(Request $request) {
        $this->render('success_invoice_insertion.html.twig');
    }
}

Вот форма, созданная в виде веточки:

{% extends "base.html.twig" %}

{% block title %}Generate new invoice{% endblock %}
{% block head %}
    {{ parent() }}
    <style type="text/css">
        .important { color: #336699; }
    </style>
{% endblock %}
{% block content %}
{% include 'nav.html.twig' %}



   <div class="flex flex-row"> 
   <div class="container mx-auto">
   <div class="flex-none w-full h-20 bg-teal-600 mt-2 text-left align-middle"> 
   <h2 class="text-2xl text-white align-middle h-full uppercase p-5">invoice </h2>
   </div>
   <div class="flex w-full flex-col bg-gray-200 shadow-lg h-auto">
   {% if form_errors(form) %}
   <div class="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded relative flex-none w-full" role="alert">
  <strong class="font-bold">Error in form validation</strong>
  <span class="block sm:inline">{{ form_errors(form) }}</span>
  <span class="absolute top-0 bottom-0 right-0 px-4 py-3">
    <svg class="fill-current h-6 w-6 text-red-500" role="button" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><title>Close</title><path d="M14.348 14.849a1.2 1.2 0 0 1-1.697 0L10 11.819l-2.651 3.029a1.2 1.2 0 1 1-1.697-1.697l2.758-3.15-2.759-3.152a1.2 1.2 0 1 1 1.697-1.697L10 8.183l2.651-3.031a1.2 1.2 0 1 1 1.697 1.697l-2.758 3.152 2.758 3.15a1.2 1.2 0 0 1 0 1.698z"/></svg>
  </span>
</div>
{% endif %}
{{form_start(form)}}     
     <div class="flex flex-row">   <div class="flex-none w-1/3">
   <label class="block text-gray-700 text-sm font-bold mb-2" >
        {{form_label(form.invoice_date)}}
      </label>
  {{form_widget(form.invoice_date)}}
  </div>
       <div class="flex-none w-1/3">
   <label class="block text-gray-700 text-sm font-bold mb-2" >
        {{form_label(form.invoice_number)}}
      </label>
    {{form_widget(form.invoice_number)}}
    </div>



  <div class="flex-none w-1/3">
    <label class="block text-gray-700 text-sm font-bold mb-2" >
        {{form_label(form.client)}}
      </label>
    {{form_widget(form.client)}}

  </div>



   </div>
   <div class="flex-none w-full"> 
  <h2 class=" block text-gray-700 text-sm font-bold mb-2 mt-5"> Invoice items </div> 
  <div class="flex flex-col invoice_items" data-prototype='  
  <div class="flex-grow"> 
  <label class="block text-gray-700 text-sm font-bold mb-2" > Quantity </label>
  <input type="number name="invoice[invoice_items][__name__][quantity]">  </div>
  <div class="flex-grow"> 
  <label class="block text-gray-700 text-sm font-bold mb-2" > Description </label>
  <textarea name="invoice[invoice_items][__name__][description]"> </textarea>
</div>
<div class="flex-grow"> 
  <label class="block text-gray-700 text-sm font-bold mb-2" > Net Amount </label>
  <input type="text name="invoice[invoice_items][__name__][net_amount]"> 
</div>
<div class="flex-grow"> 
  <label class="block text-gray-700 text-sm font-bold mb-2" > Gross Amount </label>
  <input type="text name="invoice[invoice_items][__name__][vat_amount]"> 
</div>
<div class="flex-grow"> 
  <label class="block text-gray-700 text-sm font-bold mb-2" > Gross Amount </label>
  <input type="text name="invoice[invoice_items][__name__][gross_amount]"> 
</div>
'> 
  {% for invoice_item in form.invoice_items %}
  <div class="flex flex-row bg-gray-400 p-3 adda"> 

  <div class="flex-grow"> 
    <label class="block text-gray-700 text-sm font-bold mb-2" >{{form_label(invoice_item.quantity)}} </label>


          {{form_widget(invoice_item.quantity)}}

  </div>

  <div class="flex-grow"> 
    <label class="block text-gray-700 text-sm font-bold mb-2" >{{form_label(invoice_item.description)}} </label>


          {{form_widget(invoice_item.description)}}

  </div>
  <div class="flex-grow"> 
     <label class="block text-gray-700 text-sm font-bold mb-2" >{{form_label(invoice_item.net_amount)}} </label>

          {{form_widget(invoice_item.net_amount)}}

  </div>
   <div class="flex-grow"> 
     <label class="block text-gray-700 text-sm font-bold mb-2" >{{form_label(invoice_item.vat_amount)}} </label>

          {{form_widget(invoice_item.vat_amount)}}

  </div>
   <div class="flex-grow"> 
    <label class="block text-gray-700 text-sm font-bold mb-2" >{{form_label(invoice_item.gross_amount)}} </label>

          {{form_widget(invoice_item.gross_amount)}}

  </div>
  </div>

  {% endfor %}
  </div>
   <button class="bg-teal-500 hover:bg-teal-700 text-white font-bold py-2 px-4 rounded mt-2" id="newInvoiceItemButton" type="button">
  New Invoice item
</button>
   </div>
   <button class="bg-green-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded mt-2">
  Save invoice
</button>
   </div>

{{form_end(form)}}
    </div>
   </div>

   <script> 
   var $collectionHolder;

// setup an "add a tag" link


jQuery(document).ready(function() {
    $collectionHolder = $('div.invoice_items');
    $addInvoiceItemButton = $("#newInvoiceItemButton");



    $collectionHolder.data('index', $collectionHolder.find(':input').length);

    $addInvoiceItemButton.on('click', function(e) {
        addInvoiceItemForm($collectionHolder, $addInvoiceItemButton);
    });

    function addInvoiceItemForm($collectionHolder, $addInvoiceItemButton) {
    var prototype = $collectionHolder.data('prototype');

    var index = $collectionHolder.data('index');

    var newForm = prototype;

    newForm = newForm.replace(/__name__/g, index);

    $collectionHolder.data('index', index + 1);

    var $newInvoiceItem = $(`<div class="flex flex-row bg-gray-400 p-3 a"> 
 </div>`).append(newForm);
    $addInvoiceItemButton.before($newInvoiceItem);

    // adding the removal button
        addInvoiceItemDeleteLink($newInvoiceItem);

}
function addInvoiceItemDeleteLink($newInvoiceItem) {
    var $removeFormButton = $(`
    <div class="flex-grow"> 
    <button class="bg-red-500 hover:bg-red-700 text-white font-bold  p-4 rounded">Delete invoice item</button>
    </div>
`);
    $newInvoiceItem.append($removeFormButton);

    $removeFormButton.on('click', function(e) {
        // remove the li for the tag form
        $newInvoiceItem.remove();
    });
}
});
   </script>
{% endblock %}

Почему я до сих пор получаю эту ошибку? Спасибо!

...