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?
CodePudding user response:
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