Я новичок в 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 %}
Почему я до сих пор получаю эту ошибку? Спасибо!