You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Symfony 6使用序列化分组时出现循环引用错误求助

Symfony 6 API序列化循环引用问题

开发Symfony 6 API时,使用序列化分组show_truck返回Truck实体时触发循环引用错误,提示序列化FoodCategory类时检测到循环引用(限制为1)。


相关代码

Truck实体(使用show_truck分组)

#[ORM\Entity(repositoryClass: TruckRepository::class)]
class Truck
{
    #[ORM\Id]
    #[ORM\GeneratedValue(strategy: 'CUSTOM')]
    #[ORM\Column(type: UuidType::NAME, unique: true)]
    #[ORM\CustomIdGenerator(class: 'doctrine.uuid_generator')]
    #[Groups(['list_trucks', 'show_truck'])]
    private ?Uuid $id = null;

    #[ORM\Column(length: 50)]
    #[Groups(['list_trucks', 'show_truck'])]
    private ?string $name = null;
...
    #[ORM\ManyToMany(targetEntity: FoodCategory::class, inversedBy: 'trucks')]
    private Collection $foodCategories;
...

FoodCategory实体(未使用show_truck分组)

#[ORM\Entity(repositoryClass: FoodCategoryRepository::class)]
class FoodCategory
{
    #[ORM\Id]
    #[ORM\GeneratedValue(strategy: 'CUSTOM')]
    #[ORM\Column(type: UuidType::NAME, unique: true)]
    #[ORM\CustomIdGenerator(class: 'doctrine.uuid_generator')]
    #[Groups(['list_food_categories', 'show_food_category'])]
    private ?Uuid $id = null;

    #[ORM\Column(length: 30)]
    #[Groups(['list_food_categories', 'show_food_category'])]
    private ?string $name = null;

    #[ORM\Column(length: 255, nullable: true)]
    #[Groups(['list_food_categories', 'show_food_category'])]
    private ?string $imageLink = null;

    #[ORM\ManyToMany(targetEntity: Truck::class, mappedBy: 'foodCategories')]
    #[Groups(['show_food_category'])]
    private Collection $trucks;
...

TruckController添加卡车方法

#[Route('/trucks', methods: ['POST'], name: 'add_truck')]
public function add(#[MapRequestPayload] TruckCreationDto $truckDto, EntityManagerInterface $entityManager, ValidatorInterface $validator, FoodCategoryRepository $foodCategoryRepository): JsonResponse
{
    $context = new ObjectNormalizerContextBuilder();

    $responseData = new ResponseData();

    $errors = $validator->validate($truckDto);

    if (count($errors) > 0)
    {
        $responseData->setStatus(Status::ERROR);
        $responseData->addMessage((string) $errors);

        $statusCode = Response::HTTP_BAD_REQUEST;
    }
    else
    {
        $truck = new Truck();
        $truck->setName($truckDto->getName());
        $truck->setSiret($truckDto->getSiret());

        foreach($truckDto->getFoodCategories() as $foodCategory)
        {
            $foodCategory = $foodCategoryRepository->find($foodCategory);

            if($foodCategory)
                $truck->addFoodCategory($foodCategory);
        }

        $entityManager->persist($truck);
        $entityManager->flush();

        $statusCode = Response::HTTP_CREATED;

        $responseData->setStatus(Status::SUCCESS);
        $responseData->addData('truck', $truck);

        $context->withGroups('show_truck');
    }

    return $this->json($responseData, $statusCode, $headers = [], $context->toArray());
}

开发环境配置

Dockerfile

FROM php:8.2.12-apache-bullseye

WORKDIR /app

# Mise à jour des repositories
RUN apt update -y

# Installation de divers packages manquants
RUN apt install -y unzip wget

# Installation d'extensions PHP
RUN docker-php-ext-install mysqli pdo pdo_mysql && docker-php-ext-enable pdo_mysql

# Installation de composer
RUN php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
RUN php composer-setup.php
RUN php -r "unlink('composer-setup.php');"
RUN mv composer.phar /usr/local/bin/composer

# Installation Symfony CLI
RUN wget https://get.symfony.com/cli/installer -O - | bash
RUN mv /root/.symfony5/bin/symfony /usr/local/bin/symfony

# Installation du certificat auto-signé
RUN symfony server:ca:install

# Démarrage du serveur web
ENTRYPOINT ["symfony", "server:start"]

docker-compose.yml

version: '3.2'

services:
  symfony-api:
    build:
      context: .
    ports:
      - '8000:8000'
    volumes:
      - '/home/<user>/wsl_dev/symfony-api/:/app'

问题原因及解决方法

核心问题

  1. 分组配置未生效:ObjectNormalizerContextBuilder的withGroups方法是不可变对象,原代码中$context->withGroups('show_truck');未重新赋值给变量,导致序列化分组完全没应用,序列化器会尝试序列化所有属性。
  2. 关联字段未管控:即使分组生效,Truck的foodCategories字段未配置序列化规则,序列化器会完整序列化关联的FoodCategory对象,而FoodCategory又关联回Truck,触发循环引用。

解决步骤

  1. 修复上下文分组配置
    修改Controller中上下文构建代码,将withGroups的返回值重新赋值:

    $context = $context->withGroups('show_truck');
    
  2. 管控关联字段序列化
    二选一即可:

    • 方式一:限制关联深度
      为Truck的foodCategories添加分组和深度限制注解:
      #[ORM\ManyToMany(targetEntity: FoodCategory::class, inversedBy: 'trucks')]
      #[Groups(['show_truck'])]
      #[MaxDepth(1)]
      private Collection $foodCategories;
      
      同时在config/packages/serializer.yaml启用深度限制:
      framework:
          serializer:
              enable_max_depth: true
      
    • 方式二:指定FoodCategory序列化字段
      为FoodCategory的核心字段添加show_truck分组,仅序列化必要内容:
      #[ORM\Id]
      #[ORM\GeneratedValue(strategy: 'CUSTOM')]
      #[ORM\Column(type: UuidType::NAME, unique: true)]
      #[ORM\CustomIdGenerator(class: 'doctrine.uuid_generator')]
      #[Groups(['list_food_categories', 'show_food_category', 'show_truck'])]
      private ?Uuid $id = null;
      
      #[ORM\Column(length: 30)]
      #[Groups(['list_food_categories', 'show_food_category', 'show_truck'])]
      private ?string $name = null;
      

内容的提问来源于stack exchange,提问作者Conil

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.05 11:35:56