A circular reference has been detected when serializing the object of class "App\Entity\Orders" (configured limit: 1) - symfony

I am encountering problems regarding the circular references of the entities.
I am creating an API where one route will have the purpose of providing in detail, all entities in one single call. So in the OrdersRepository, i have a function that provide to give all the infos.
I have tried, as read in other questions made on stack overflow (and as suggest in the doc), to use the annotations #[Ignore] and #[Groups(['group_name'])], but I was not able to make it work. Probably I did not understand the concept of this solution well.
Even the "MaxDepth" solution didn't work.
Basically, I have a repository function that provides the result of 4 entities: Order, Address, OrderProducts and Product. And they are structured in this way:
Order
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column]
private ?int $id = null;
#[ORM\ManyToOne]
private ?Carrier $carrier = null;
#[ORM\ManyToOne]
#[ORM\JoinColumn(nullable: false)]
private ?Address $address_delivery = null;
#[ORM\ManyToOne]
private ?Address $address_invoice = null;
#[ORM\Column]
private ?int $invoice = null;
#[ORM\OneToMany(mappedBy: 'order_related', targetEntity: OrderProducts::class)]
private Collection $orderProducts;
#[ORM\OneToMany(mappedBy: 'order_related', targetEntity: OrderDetail::class)]
private Collection $orderDetails;
OrderDetail
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column]
private ?int $id = null;
#[ORM\Column(type: Types::DECIMAL, precision: 20, scale: 6, nullable: true)]
private ?string $weight = null;
#[ORM\Column(type: Types::DECIMAL, precision: 20, scale: 6, nullable: true)]
private ?string $height = null;
#[ORM\Column(type: Types::DECIMAL, precision: 20, scale: 6, nullable: true)]
private ?string $depth = null;
#[ORM\Column(type: Types::DECIMAL, precision: 20, scale: 6, nullable: true)]
private ?string $length = null;
#[ORM\ManyToOne(inversedBy: 'orderDetails')]
private ?Orders $order_related = null;
OrderProducts
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column]
private ?int $id = null;
#[ORM\ManyToOne(inversedBy: 'orderProducts')]
#[ORM\JoinColumn(nullable: false)]
private ?Orders $order_related = null;
#[ORM\Column(type: Types::DECIMAL, precision: 20, scale: 6)]
private ?string $price= null;
#[ORM\ManyToOne]
#[ORM\JoinColumn(nullable: false)]
private ?Product $product = null;
Product
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column]
private ?int $id = null;
#[ORM\Column(length: 255)]
private ?string $name = null;
#[ORM\Column(length: 255)]
private ?string $description_short = null;
#[ORM\Column(length: 255)]
private ?string $description = null;
#[ORM\Column(length: 255)]
private ?string $reference = null;
OrderController
#[Route('v1/api/all', name: 'dashboard', methods: 'GET')]
public function all_orders_info(CustomSerializer $serializer): Response
{
$orders = $this->ordersRepository->findAll();
$responseContent = $serializer->serialize($orders, 'json');
return new JsonResponse(data: $responseContent, status: Response::HTTP_OK, json: true);
}
Service CustomSerializer.php
class CustomSerializer implements SerializerInterface
{
private SerializerInterface $serializer;
public function __construct()
{
$this->serializer = new Serializer(
normalizers : [new ObjectNormalizer()],
encoders : [new JsonEncoder()],
);
}
public function serialize(mixed $data, string $format, array $context = []): string
{
return $this->serializer->serialize($data, $format, $context);
}
public function deserialize(mixed $data, string $type, string $format, array $context = []): mixed
{
return $this->serializer->deserialize($data, $type, $format, $context);
}
}
The problem is that I get the classic error "A circular reference has been detected when serializing the object of class "App\Entity\Orders" (configured limit: 1).". I inserted the Ignore and Groups annotations within the "Orders" entity, above the two properties "orderProducts" and "orderDetails" of Orders entity, but nothing.
Where i'm wrong?

None of these attributes would would work if you initialize the serializer component manually like that (your CustomSerializer), a little more is needed. Luckily there is an alternative;
It is recommended when within the Symfony framework to just inject the SerializerInterface service.
// src/Controller/DefaultController.php
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Serializer\SerializerInterface;
class DefaultController extends AbstractController
{
public function index(SerializerInterface $serializer)
{
// keep reading for usage examples
}
}
As described here https://symfony.com/doc/current/serializer.html.
This comes with a few encoders and a lot of normalizers already configured to work with other aspects of the framework.
Sadly when you search through google the standalone component documentation (https://symfony.com/doc/current/components/serializer.html) comes up first. Which does explain some good core principles in how its internals work.
But within the framework most explained configuration things are already done and you can use it right away.
You could then start playing with your Ignore/Groups and MaxDepth things to get it working.
Another minor note you can also change some settings for the injected version in https://symfony.com/doc/current/reference/configuration/framework.html#serializer
these settings used to be in framework.yaml as a comment not sure if they are still there or if it got its own file now when you install the package though flex.
For those who are using the standalone component and still want attributes you can read up on how to set up that at https://symfony.com/doc/current/components/serializer.html#attributes-groups

Related

How to eliminate this Column not found error?

Originally, the entity Gut had a field reaction that contained a string. The options for reaction were hard-wired in a template. By adding an entity Reaction and changing the Gut form's reaction to an EntityType I'm now plagued with the error message
SQLSTATE[42S22]: Column not found: 1054 Unknown column 't0.reaction' in 'field list'
even though I've rewritten the Gut & Reaction entities. I've probably lost sight of the forest for the trees. What's wrong with the following?
MySQL table gut: reaction column replaced by reaction_id; reaction_id correctly created; foreign key created manually.
Error occurs with this controller method:
#[Route('/', name: 'app_gut_index', methods: ['GET'])]
public function index(GutRepository $gutRepository): Response
{
$guts = $gutRepository->findBy([], ['happened' => 'DESC']); // error thrown here
return $this->render('gut/index.html.twig', [
'guts' => $guts,
]);
}
Gut entity:
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column]
private ?int $id = null;
#[ORM\Column(length: 255)]
#[ORM\ManyToOne(targetEntity: Reaction::class)]
#[ORM\JoinColumn(name: 'reaction_id', referencedColumnName: 'id')]
protected $reaction;
#[ORM\Column(length: 255, nullable: true)]
private ?string $description = null;
#[ORM\Column(name: "datetime")]
private ?\DateTime $happened = null;
public function getId(): ?int
{
return $this->id;
}
public function getReaction(): ?Reaction
{
return $this->reaction;
}
public function setReaction(?Reaction $reaction): self
{
$this->reaction = $reaction;
return $this;
}
...
}
Reaction entity:
use App\Entity\Gut;
use App\Repository\ReactionRepository;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
#[ORM\Entity(repositoryClass: ReactionRepository::class)]
class Reaction
{
public function __construct()
{
$this->guts = new ArrayCollection();
}
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column]
private ?int $id = null;
#[ORM\Column(length: 45)]
private ?string $reaction = null;
public function getId(): ?int
{
return $this->id;
}
public function getReaction(): ?string
{
return $this->reaction;
}
public function setReaction(string $reaction): self
{
$this->reaction = $reaction;
return $this;
}
#[ORM\OneToMany(targetEntity: Gut::class, mappedBy: 'reaction')]
private $guts;
/**
* #return Collection|Product[]
*/
public function getGuts(): Collection
{
return $this->guts;
}
public function addGut($gut): self
{
$this->guts[] = $gut;
return $this;
}
public function __toString()
{
return $this->getReaction();
}
}
Your $reaction property should not have both ORM\Column and ORM\JoinColumn annotations at the same time.
Because of this Doctrine thinks it's a regular column so it's looking for a database field based on the variable name: $reaction -> gut.reaction.
Remove #[ORM\Column(length: 255)] then make sure that you have gut.reaction_id in your database and now it should work.
As a little side note I don't think you need name: 'reaction_id', referencedColumnName: 'id' in ORM\JoinColumn because that's how Doctrine will name them automatically anyway
Just couldn't let go. I eventually found a path to get the Gut and Reaction entities to play nicely together. What I did:
cloned the project
manually deleted reaction property from Gut entity; created & executed a migration
in MySQL, added back in a reaction column
used make:entity Gut to add a reaction property as ManyToOne on Reaction; made a migration
used MySQL to populate the reaction_id column from the database of the cloned project.
(Probably missed a step in here somewhere, but) gut->getReaction(),etc,
now behave as expected - in a ManyToOne relationship.

Can you please simplify (or help me refactor) my logic

I am aggregating data in json to convert it into symfony object.
A category (otology) has a type (swimming pool, restaurant). This category has a description that is translated into several languages.
I would like to be able to simply do getXXX('fr'), to have this description in French, getXXX('en') for English.
With a repository I think I can do it, but maybe I complicate it:
My class Otolongy:
class Ontology
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column]
private ?int $id = null;
#[ORM\Column(length: 255)]
private ?string $idOntology = null;
#[ORM\OneToMany(mappedBy: 'ontology', targetEntity: Label::class, cascade: ['persist', 'remove'], fetch:'EAGER')]
private Collection $descriptions;
my class label (for fields description):
#[ORM\Entity(repositoryClass: LabelRepository::class)]
class Label
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column]
private ?int $id = null;
#[ORM\Column(type: Types::TEXT)]
private ?string $label = null;
#[ORM\ManyToOne(inversedBy: 'descriptions')]
private ?Ontology $ontology = null;
#[ORM\ManyToOne(inversedBy: 'labels', fetch:'EAGER')]
private ?Lang $lang = null;
my class lang:
#[ORM\Entity(repositoryClass: LangRepository::class)]
class Lang
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column]
private ?int $id = null;
#[ORM\Column(length: 10)]
private ?string $labelLang = null;
#[ORM\OneToMany(mappedBy: 'lang', targetEntity: Label::class)]
private Collection $labels;
You can see that for my tests I had to put fetch:'EAGER' I'm not convinced that this is the right way to do it... well after all put 1 eager or have to make a 'complex' request in my controller it should be equivalent...
I have already done this system on java and I had made a map key, so I used this command ontology.description.get(lang) in the template, in order to use the local as an argument and java gave me the description in the good language.
if i can avoid loading the ontology in eager i think the display of twig will go faster? Namely that I must also check that the translation in the visitor's local exists, otherwise I must display by default (English or French)
So my goal is to use the visitor's locale to know which language (language of the ontology in the database) that twig should display. Is there maybe a simpler way to do it? Maybe I'm totally wrong?!

Symfony 6 & VichUploaderBundle : "default" UploadedFile

I'm learning Symfony by working on a personal project. I'm using Symfony 6.1.
I have a page containing two forms :
the "main" form ($bookForm) deals the Book entity and persist it to the database
the second one ($isbnForm) takes a string and search the Google Books API.
When submitting the second form ($isbnForm), the page is reloaded and $bookForm is preloaded with data.
Here is the code of the BookController :
#[Route('/create', name: 'create', methods: ['GET', 'POST'])]
public function new(
Request $request,
BookshelfRepository $bookshelfRepository,
AuthorRepository $authorRepository,
PublisherRepository $publisherRepository,
BookRepository $bookRepository
): Response {
$this->denyAccessUnlessGranted('edit', $this->getUser());
$book = new Book();
// Retrieving the Bookshelf passed along the request
// and associating it to the Book
if ($request->query->get('bksid')) {
$bookshelf = $bookshelfRepository->findOneBy(['ulid' => $request->query->get('bksid')]);
$book->setBookshelf($bookshelf);
}
// Dealing with the ISBN form
$isbnForm = $this->createForm(IsbnType::class);
$isbnForm->handleRequest($request);
if ($isbnForm->isSubmitted() && $isbnForm->isValid()) {
$isbnTools = new IsbnTools();
$isbn = $isbnTools->format($isbnForm->getData()['isbn']);
$book->setIsbn($isbn);
// Getting book's details from the Google Books API using the ISBN
$gbapi = new GoogleBooksApiUtils();
$details = $gbapi->gettingVolumeInfoByIsbn($isbn);
$book->setTitle($details->getTitle());
$book->setSubtitle($details->getSubtitle());
$book->setDescription($details->getDescription());
$book->setPages($details->getPageCount());
$book->setPublicationDate(substr($details->getPublishedDate(), 0, 4));
if ($details->getPublisher()) {
$publisher = $publisherRepository->findOneBy(['name' => $details->getPublisher()]);
if (!$publisher) {
$publisher = new Publisher();
$publisher->setName($details->getPublisher());
$publisherRepository->save($publisher, true);
}
$book->setPublisher($publisher);
}
foreach ($details->getAuthors() ?? [] as $dga) {
$author = $authorRepository->findOneBy(['name' => $dga]);
if (!$author) {
$author = new Author();
$author->setName($dga);
$authorRepository->save($author, true);
}
$book->addAuthor($author);
}
}
// Dealing with the main form, dealing with the Book entity
$bookForm = $this->createForm(BookType::class, $book);
$bookForm->handleRequest($request);
if ($bookForm->isSubmitted() && $bookForm->isValid()) {
$bookRepository->save($book, true);
return $this->redirectToRoute('bks_book_view', [
'ulid' => $book->getUlid(),
], Response::HTTP_SEE_OTHER);
}
return $this->renderForm('book/create.html.twig', [
'isbn_form' => $isbnForm,
'form' => $bookForm,
'book' => $book
]);
}
It works nicely until I tried to submit the thumbnail get from the Google Books API.
I've installed the VichUploaderBundle, set up a mapping, modified my Book entity accordingly to the documentation.
vich_uploader:
db_driver: orm
metadata:
type: attribute
mappings:
books:
uri_prefix: /uploads/books
upload_destination: '%kernel.project_dir%/public/uploads/books'
namer: Vich\UploaderBundle\Naming\SmartUniqueNamer
inject_on_load: false
delete_on_update: true
delete_on_remove: true
<?php
namespace App\Entity;
use App\Repository\BookRepository;
use DateTime;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\HttpFoundation\File\File;
use Symfony\Component\Uid\Ulid;
use Vich\UploaderBundle\Mapping\Annotation\Uploadable;
use Vich\UploaderBundle\Mapping\Annotation\UploadableField;
#[ORM\Entity(repositoryClass: BookRepository::class)]
#[ORM\HasLifecycleCallbacks]
#[Uploadable]
class Book
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column]
private ?int $id = null;
#[ORM\Column(length: 255)]
private ?string $title = null;
#[ORM\Column(length: 255, nullable: true)]
private ?string $subtitle = null;
#[ORM\ManyToMany(targetEntity: Author::class, inversedBy: 'books')]
private Collection $authors;
#[ORM\ManyToOne(inversedBy: 'books')]
#[ORM\JoinColumn(nullable: false)]
private ?Publisher $publisher = null;
#[ORM\Column(length: 4, nullable: true)]
private ?string $publication_date = null;
#[ORM\Column(type: Types::TEXT, nullable: true)]
private ?string $description = null;
#[ORM\Column(length: 13, nullable: true)]
private ?string $isbn = null;
#[ORM\Column(nullable: true)]
private ?int $pages = null;
#[ORM\Column(type: 'ulid')]
private ?Ulid $ulid = null;
#[ORM\ManyToOne(inversedBy: 'books')]
#[ORM\JoinColumn(nullable: false)]
private ?Bookshelf $bookshelf = null;
#[UploadableField(mapping: 'books', fileNameProperty: 'imageName', size: 'imageSize')]
private ?File $imageFile = null;
#[ORM\Column(type: 'string', nullable: true)]
private ?string $imageName = null;
#[ORM\Column(type: 'integer', nullable: true)]
private ?int $imageSize = null;
#[ORM\Column(type: 'datetime')]
private ?\DateTimeInterface $updatedAt = null;
public function __construct()
{
$this->authors = new ArrayCollection();
$this->updatedAt = new DateTime();
}
public function getId(): ?int
{
return $this->id;
}
public function getTitle(): ?string
{
return $this->title;
}
public function setTitle(string $title): self
{
$this->title = $title;
return $this;
}
public function getSubtitle(): ?string
{
return $this->subtitle;
}
public function setSubtitle(?string $subtitle): self
{
$this->subtitle = $subtitle;
return $this;
}
/**
* #return Collection<int, Author>
*/
public function getAuthors(): Collection
{
return $this->authors;
}
public function addAuthor(Author $author): self
{
if (!$this->authors->contains($author)) {
$this->authors->add($author);
}
return $this;
}
public function removeAuthor(Author $author): self
{
$this->authors->removeElement($author);
return $this;
}
public function getPublisher(): ?Publisher
{
return $this->publisher;
}
public function setPublisher(?Publisher $publisher): self
{
$this->publisher = $publisher;
return $this;
}
public function getPublicationDate(): ?string
{
return $this->publication_date;
}
public function setPublicationDate(?string $publication_date): self
{
$this->publication_date = $publication_date;
return $this;
}
public function getDescription(): ?string
{
return $this->description;
}
public function setDescription(?string $description): self
{
$this->description = $description;
return $this;
}
public function getIsbn(): ?string
{
return $this->isbn;
}
public function setIsbn(?string $isbn): self
{
$this->isbn = $isbn;
return $this;
}
public function getPages(): ?int
{
return $this->pages;
}
public function setPages(?int $pages): self
{
$this->pages = $pages;
return $this;
}
public function getUlid(): ?Ulid
{
return $this->ulid;
}
#[ORM\PrePersist]
public function setUlid(): void
{
$this->ulid = new Ulid();
}
public function getBookshelf(): ?Bookshelf
{
return $this->bookshelf;
}
public function setBookshelf(?Bookshelf $bookshelf): self
{
$this->bookshelf = $bookshelf;
return $this;
}
/**
* If manually uploading a file (i.e. not using Symfony Form) ensure an instance
* of 'UploadedFile' is injected into this setter to trigger the update. If this
* bundle's configuration parameter 'inject_on_load' is set to 'true' this setter
* must be able to accept an instance of 'File' as the bundle will inject one here
* during Doctrine hydration.
*
* #param File|\Symfony\Component\HttpFoundation\File\UploadedFile|null $imageFile
*/
public function setImageFile(?File $imageFile = null): void
{
$this->imageFile = $imageFile;
if (null !== $imageFile) {
// It is required that at least one field changes if you are using doctrine
// otherwise the event listeners won't be called and the file is lost
$this->updatedAt = new \DateTimeImmutable();
}
}
public function getImageFile(): ?File
{
return $this->imageFile;
}
public function setImageName(?string $imageName): void
{
$this->imageName = $imageName;
}
public function getImageName(): ?string
{
return $this->imageName;
}
public function setImageSize(?int $imageSize): void
{
$this->imageSize = $imageSize;
}
public function getImageSize(): ?int
{
return $this->imageSize;
}
}
Then I modify my BookController in order to add the imageFile to the Book entity :
#[Route('/create', name: 'create', methods: ['GET', 'POST'])]
public function new(
Request $request,
BookshelfRepository $bookshelfRepository,
AuthorRepository $authorRepository,
PublisherRepository $publisherRepository,
BookRepository $bookRepository
): Response {
$this->denyAccessUnlessGranted('edit', $this->getUser());
$book = new Book();
// Retrieving the Bookshelf passed along the request
// and associating it to the Book
if ($request->query->get('bksid')) {
$bookshelf = $bookshelfRepository->findOneBy(['ulid' => $request->query->get('bksid')]);
$book->setBookshelf($bookshelf);
}
// Dealing with the ISBN form
$isbnForm = $this->createForm(IsbnType::class);
$isbnForm->handleRequest($request);
if ($isbnForm->isSubmitted() && $isbnForm->isValid()) {
$isbnTools = new IsbnTools();
$isbn = $isbnTools->format($isbnForm->getData()['isbn']);
$book->setIsbn($isbn);
// Getting book's details from the Google Books API using the ISBN
$gbapi = new GoogleBooksApiUtils();
$details = $gbapi->gettingVolumeInfoByIsbn($isbn);
$book->setTitle($details->getTitle());
$book->setSubtitle($details->getSubtitle());
$book->setDescription($details->getDescription());
$book->setPages($details->getPageCount());
$book->setPublicationDate(substr($details->getPublishedDate(), 0, 4));
if ($details->getPublisher()) {
$publisher = $publisherRepository->findOneBy(['name' => $details->getPublisher()]);
if (!$publisher) {
$publisher = new Publisher();
$publisher->setName($details->getPublisher());
$publisherRepository->save($publisher, true);
}
$book->setPublisher($publisher);
}
foreach ($details->getAuthors() ?? [] as $dga) {
$author = $authorRepository->findOneBy(['name' => $dga]);
if (!$author) {
$author = new Author();
$author->setName($dga);
$authorRepository->save($author, true);
}
$book->addAuthor($author);
}
$thumb = $details->getImageLinks()->getThumbnail() ?? null;
if ($thumb) {
$path = 'uploads/books';
$filename = 'gbapi_' . $book->getIsbn() . '.jpg';
file_put_contents("$path/$filename", file_get_contents($thumb));
$file = new UploadedFile("$path/$filename", $filename, 'image/jpeg', null, false);
$book->setImageFile($file);
$book->setImageName($file->getFilename());
$book->setImageSize($file->getSize());
}
}
// Dealing with the main form, dealing with the Book entity
$bookForm = $this->createForm(BookType::class, $book);
$bookForm->handleRequest($request);
if ($bookForm->isSubmitted() && $bookForm->isValid()) {
$bookRepository->save($book, true);
return $this->redirectToRoute('bks_book_view', [
'ulid' => $book->getUlid(),
], Response::HTTP_SEE_OTHER);
}
return $this->renderForm('book/create.html.twig', [
'isbn_form' => $isbnForm,
'form' => $bookForm,
'book' => $book
]);
}
When I submit the $isbnForm, the data are well passed to the Book entity, and the image is displayed inside the $bookForm. But when I submit it, the imageFile (imageSize and imageName fields) are not persisted to the database.
I've tried to add the UploadedFile to the $request (something like that : $request->files->set('book', ['imageFile' => ['file' => $file]])) but without success...
I have been trying for two days to solve this issue and I'm not even sure I've identified clearly the issue...
Thanks in advance for any lead :-)

Symfony ArrayCollection dans form symfony

I'm trying somehow to attach a partner's modules to my PartnerModule entity, but I can't because it has to create as many relationships as there are associated modules.
A partner has several modules and a module belongs to several partners.
Hence this intermediate table: PartnerModule. I understand that this is definitely a problem in my entities but I can't manage to fix it. Here are my entities and my form. I specify that when I put "multiple" => false it works well.
enter image description here
My error :
Expected argument of type "?App\Entity\Module", "Doctrine\Common\Collections\ArrayCollection" given at property path "modules".
Entity Module :
#[ORM\Entity(repositoryClass: ModuleRepository::class)]
class Module
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column]
private ?int $id = null;
#[ORM\Column(length: 250)]
private ?string $nom = null;
#[ORM\Column]
private ?bool $statut = null;
#[ORM\OneToMany(mappedBy: 'Modules', targetEntity: PartenaireModule::class)]
private Collection $partenaireModules;
public function __construct()
{
$this->partenaireModules = new ArrayCollection();
}
Entity Partenaire Module
#[ORM\Entity(repositoryClass: PartenaireModuleRepository::class)]
class PartenaireModule
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column]
private ?int $id = null;
#[ORM\Column]
private ?bool $is_active = true;
#[ORM\ManyToOne(inversedBy: 'partenaireModules')]
private ?Partenaire $Partenaires = null;
#[ORM\ManyToOne(inversedBy: 'partenaireModules')]
private ?Module $Modules = null;
This is how my data should be mapped:
-> module_id, partenaire_id, is_active, id
Any help or clue to help me will be welcome :)
Thanks

API Platform: Apply a filter to multiple resources

I'm trying to apply SearchFilter to multiple resources.
Let's imagine I've got two resources: Post and Article (just for the illustration):
#[ApiRsource]
class Post
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column]
private ?int $id = null;
#[ORM\Column(length: 255, nullable: true)]
private ?string $postDescription = null;
...
}
#[ApiRsource]
class Article
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column]
private ?int $id = null;
#[ORM\Column(length: 255, nullable: true)]
private ?string $articleDescription = null;
...
}
I want to add a unique endpoint /search?q=key that will return Post and Article resources with a postDescription/articleDescription containing key.
Is there a way to do this without resorting to inheritance?

Resources