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

在Symfony中使用DTO处理含多个ID的JSON请求体

如何通过DTO关联实体验证ID有效性

问题描述

我需要处理如下JSON请求体:

{
    "idBook": "34567890",
    "idReaders": ["3456789","3456789"]
}

其中idReaders是已阅读该书的用户ID列表。我用DTO处理这个JSON,但不知道怎么把这些ID关联到对应的书籍、用户实体来验证ID的有效性。当前DTO代码如下:

<?php

class addBookReaderDTO
{
    #[Assert\NotBlank]
    #[Assert\NotNull]
    public string $idBook;

    #[Assert\NotBlank]
    #[Assert\NotNull]
    #[Assert\E] // 此处为无效注解
    public array $idReaders;
}

解决方案

步骤1:修正DTO的验证注解

首先修正DTO中无效的#[Assert\E]注解,针对数组元素的验证使用#[Assert\All]确保每个用户ID符合格式要求,同时调整类名符合PHP大驼峰规范:

<?php

use Symfony\Component\Validator\Constraints as Assert;

class AddBookReaderDTO
{
    #[Assert\NotBlank(message: "书籍ID不能为空")]
    #[Assert\NotNull(message: "书籍ID不能为null")]
    public string $idBook;

    #[Assert\NotBlank(message: "用户ID列表不能为空")]
    #[Assert\NotNull(message: "用户ID列表不能为null")]
    #[Assert\All([
        #[Assert\NotBlank(message: "单个用户ID不能为空")],
        #[Assert\Type(type: "string", message: "用户ID必须为字符串类型")]
    ])]
    public array $idReaders;
}

步骤2:在业务服务层关联实体并验证有效性

DTO仅负责请求参数的格式校验,实体关联和存在性验证需放到业务服务层处理(以Doctrine ORM为例):

<?php

namespace App\Service;

use App\DTO\AddBookReaderDTO;
use App\Repository\BookRepository;
use App\Repository\UserRepository;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;

class BookReaderService
{
    public function __construct(
        private BookRepository $bookRepository,
        private UserRepository $userRepository
    ) {}

    public function addReaders(AddBookReaderDTO $dto): void
    {
        // 验证书籍存在
        $book = $this->bookRepository->find($dto->idBook);
        if (!$book) {
            throw new NotFoundHttpException("书籍ID {$dto->idBook} 不存在");
        }

        // 验证所有用户ID对应的用户存在
        $uniqueReaderIds = array_unique($dto->idReaders);
        $users = $this->userRepository->findBy(['id' => $uniqueReaderIds]);
        
        if (count($users) !== count($uniqueReaderIds)) {
            $existingUserIds = array_column($users, 'id');
            $invalidIds = array_diff($uniqueReaderIds, $existingUserIds);
            throw new NotFoundHttpException("以下用户ID不存在:" . implode(', ', $invalidIds));
        }

        // 执行关联逻辑(示例:将用户添加到书籍的阅读列表)
        foreach ($users as $user) {
            $book->addReader($user);
        }

        // 持久化数据
        $this->bookRepository->save($book, true);
    }
}

可选:自定义断言实现DTO层面的实体验证

如果希望在DTO校验阶段直接完成实体存在性检查,可以自定义Symfony验证断言:

1. 自定义书籍存在断言

<?php

namespace App\Validator\Constraints;

use Symfony\Component\Validator\Constraint;

#[\Attribute]
class BookExists extends Constraint
{
    public string $message = "书籍ID {{ value }} 不存在";

    public function getTargets(): string
    {
        return self::PROPERTY_CONSTRAINT;
    }
}

2. 对应的验证器

<?php

namespace App\Validator;

use App\Repository\BookRepository;
use App\Validator\Constraints\BookExists;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;

class BookExistsValidator extends ConstraintValidator
{
    public function __construct(private BookRepository $bookRepository) {}

    public function validate(mixed $value, Constraint $constraint): void
    {
        if (!$constraint instanceof BookExists) return;
        if (empty($value)) return;

        if (!$this->bookRepository->find($value)) {
            $this->context->buildViolation($constraint->message)
                ->setParameter('{{ value }}', $value)
                ->addViolation();
        }
    }
}

3. 同理实现UserExists断言后,修改DTO

<?php

use Symfony\Component\Validator\Constraints as Assert;
use App\Validator\Constraints as CustomAssert;

class AddBookReaderDTO
{
    #[Assert\NotBlank]
    #[Assert\NotNull]
    #[CustomAssert\BookExists] // 直接验证书籍存在
    public string $idBook;

    #[Assert\NotBlank]
    #[Assert\NotNull]
    #[Assert\All([
        #[Assert\NotBlank],
        #[Assert\Type("string")],
        #[CustomAssert\UserExists] // 验证每个用户ID存在
    ])]
    public array $idReaders;
}

这样在控制器执行$this->validate($dto)时,会自动完成实体存在性校验。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.09 12:58:12