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'
问题原因及解决方法
核心问题
- 分组配置未生效:
ObjectNormalizerContextBuilder的withGroups方法是不可变对象,原代码中$context->withGroups('show_truck');未重新赋值给变量,导致序列化分组完全没应用,序列化器会尝试序列化所有属性。 - 关联字段未管控:即使分组生效,Truck的
foodCategories字段未配置序列化规则,序列化器会完整序列化关联的FoodCategory对象,而FoodCategory又关联回Truck,触发循环引用。
解决步骤
修复上下文分组配置
修改Controller中上下文构建代码,将withGroups的返回值重新赋值:$context = $context->withGroups('show_truck');管控关联字段序列化
二选一即可:- 方式一:限制关联深度
为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
相关产品推荐
相关产品推荐

