Symfony中make:entity生成实体取消ID自动生成及反序列化方法
Symfony反序列化ID字段问题解决办法
问题场景
我用以下代码反序列化JSON请求:
$user = $serializer->deserialize($request->getContent(), Book::class, 'json');
但通过symfony console make:entity生成的Book实体中,ID字段因默认自动生成策略没有setter方法,导致反序列化时无法为ID赋值。生成的实体代码如下:
class Book { /** * @ORM\Id * @ORM\GeneratedValue * @ORM\Column(type="integer") */ private $id; // 其他字段... public function getId(): ?int { return $this->id; }
手动修改实体将ID生成策略改为NONE并添加setter方法可以解决,但想知道有没有更高效的方式:
class Book { /** * @ORM\Id * @ORM\GeneratedValue(strategy="NONE") * @ORM\Column(type="integer") */ private $id; // 其他字段... public function getId(): ?int { return $this->id; } public function setId(int $id): self { $this->id = $id; return $this; } }
解决方案
一、用make:entity直接生成无自动ID的实体
可以通过交互式命令的选项直接生成带NONE策略ID的实体,无需手动修改:
- 运行命令:
symfony console make:entity Book - 当提示添加字段时,先创建
id字段,类型选择integer - 询问
Is this field the primary key?时输入yes - 询问
What is the generation strategy for the primary key?时选择NONE(而非默认的AUTO) - 继续完成其他字段的创建
这样生成的Book实体自动包含setId方法,ID字段的生成策略也会设为NONE,满足反序列化赋值的需求。
二、不修改实体生成策略,实现ID反序列化赋值
如果需要保留ID的自动生成策略(比如数据库仍自动生成ID,但部分场景需要外部传入ID反序列化),可以用以下几种方式:
方法1:反射直接赋值
反序列化后通过PHP反射机制直接为私有ID属性赋值:
$book = $serializer->deserialize($request->getContent(), Book::class, 'json'); $jsonData = json_decode($request->getContent(), true); // 反射赋值ID $reflection = new \ReflectionClass($book); $idProperty = $reflection->getProperty('id'); $idProperty->setAccessible(true); $idProperty->setValue($book, $jsonData['id']);
方法2:配置序列化上下文
在反序列化时传入上下文参数,允许访问私有属性或指定字段:
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer; $book = $serializer->deserialize( $request->getContent(), Book::class, 'json', [ ObjectNormalizer::ALLOW_UNINITIALIZED_OBJECTS => true, 'attributes' => ['id', 'title', 'author'] // 明确指定要反序列化的字段 ] );
方法3:自定义Denormalizer
编写自定义的反序列化器,专门处理Book实体的ID赋值,无需修改实体类:
namespace App\Serializer; use App\Entity\Book; use Symfony\Component\Serializer\Normalizer\ContextAwareNormalizerInterface; use Symfony\Component\Serializer\Normalizer\DenormalizerInterface; class BookDenormalizer implements ContextAwareNormalizerInterface, DenormalizerInterface { private $decorated; public function __construct(DenormalizerInterface $decorated) { $this->decorated = $decorated; } public function denormalize($data, string $type, string $format = null, array $context = []) { $book = $this->decorated->denormalize($data, $type, $format, $context); if ($book instanceof Book && isset($data['id'])) { $reflection = new \ReflectionClass($book); $idProp = $reflection->getProperty('id'); $idProp->setAccessible(true); $idProp->setValue($book, $data['id']); } return $book; } public function supportsDenormalization($data, string $type, string $format = null, array $context = []): bool { return $type === Book::class && $this->decorated->supportsDenormalization($data, $type, $format, $context); } public function supportsNormalization($data, string $format = null, array $context = []): bool { return false; } public function normalize($object, string $format = null, array $context = []) { throw new \LogicException('该归一化器仅处理反序列化逻辑'); } }
然后在config/services.yaml中注册这个服务:
App\Serializer\BookDenormalizer: arguments: ['@serializer.normalizer.object'] tags: - { name: serializer.normalizer, priority: 100 }
方法4:DTO中转
先创建包含所有字段(包括ID)的DTO类,反序列化到DTO后再映射到Book实体:
// BookDTO.php class BookDTO { public int $id; public string $title; // 其他字段... } // 控制器中处理 $bookDTO = $serializer->deserialize($request->getContent(), BookDTO::class, 'json'); $book = new Book(); // 反射赋值ID $reflection = new \ReflectionClass($book); $idProp = $reflection->getProperty('id'); $idProp->setAccessible(true); $idProp->setValue($book, $bookDTO->id); // 其他字段用setter赋值 $book->setTitle($bookDTO->title); // ...
内容的提问来源于stack exchange,提问作者Laurent
相关产品推荐
相关产品推荐

