如何通过序列化/反序列化器在存入DB前修改值?自定义Denormalizer未生效
解决Symfony自定义Denormalizer不生效的问题
你的自定义PasswordNormalizer未触发,核心问题集中在三个方面:
getSupportedTypes方法配置错误,导致Symfony序列化组件无法识别它支持User::class类型- 未处理
OBJECT_TO_POPULATE传入已有对象的场景 - 链式Denormalizer的注入方式不符合组件要求
具体修复步骤
1. 修正getSupportedTypes方法
这个方法是Symfony Serializer判断归一化器支持类型的核心依据,你当前返回['*' => false]相当于告知组件该归一化器不支持任何类型,自然不会被调用。修改为:
public function getSupportedTypes(?string $format): array { return [ User::class => true, ]; }
2. 适配OBJECT_TO_POPULATE场景
当反序列化时传入已有对象(通过OBJECT_TO_POPULATE上下文参数),需要直接复用该对象而非重新创建,调整denormalize方法逻辑:
public function denormalize(mixed $data, string $type, ?string $format = null, array $context = []): mixed { if ($type === User::class && isset($data['password'])) { $plainPassword = $data['password']; unset($data['password']); // 优先使用传入的已有对象,无则创建新对象 $user = $context[AbstractNormalizer::OBJECT_TO_POPULATE] ?? $this->decorated->denormalize($data, $type, $format, $context); $hashedPassword = $this->passwordHasher->hashPassword($user, $plainPassword); $user->setPassword($hashedPassword); return $user; } return $this->decorated->denormalize($data, $type, $format, $context); }
3. 调整链式Denormalizer注入逻辑
无需手动指定serializer.denormalizer.array,直接注入DenormalizerInterface即可,Symfony会自动处理归一化器链的委托逻辑。修改构造函数:
public function __construct( private readonly DenormalizerInterface $decorated, private UserPasswordHasherInterface $passwordHasher ) { }
如果手动配置services.yaml,确保给服务加上serializer.normalizer标签:
services: App\Serializer\PasswordNormalizer: tags: [serializer.normalizer]
4. 验证反序列化调用代码
确保正确引入AbstractNormalizer常量:
use Symfony\Component\Serializer\Normalizer\AbstractNormalizer; $serializer->deserialize( $request->getContent(), User::class, 'json', [ AbstractNormalizer::OBJECT_TO_POPULATE => $user] );
完整修正后的PasswordNormalizer代码
<?php namespace App\Serializer; use App\Entity\User; use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface; use Symfony\Component\Serializer\Normalizer\AbstractNormalizer; use Symfony\Component\Serializer\Normalizer\DenormalizerInterface; class PasswordNormalizer implements DenormalizerInterface { public function __construct( private readonly DenormalizerInterface $decorated, private UserPasswordHasherInterface $passwordHasher ) { } public function denormalize(mixed $data, string $type, ?string $format = null, array $context = []): mixed { if ($type === User::class && isset($data['password'])) { $plainPassword = $data['password']; unset($data['password']); // 优先使用传入的已有对象 $user = $context[AbstractNormalizer::OBJECT_TO_POPULATE] ?? $this->decorated->denormalize($data, $type, $format, $context); $hashedPassword = $this->passwordHasher->hashPassword($user, $plainPassword); $user->setPassword($hashedPassword); return $user; } return $this->decorated->denormalize($data, $type, $format, $context); } public function supportsDenormalization($data, string $type, ?string $format = null, array $context = []): bool { return $type === User::class; } public function getSupportedTypes(?string $format): array { return [ User::class => true, ]; } }
内容的提问来源于stack exchange,提问作者DevMoutarde
相关产品推荐
相关产品推荐

