如何序列化ManyToOne关联字段仅展示单个ID属性
问题描述
我有User和Message两个实体,二者为OneToMany关联关系。当前API返回的消息数据中,toUser和fromUser字段包含__initializer__等额外初始化属性,希望序列化后仅展示它们的ID值,格式如下:
"toUser": "1ed01e01-1133-690e-8ac8-6b6e91ccf39b", "fromUser": "1ed01e01-1133-671a-8d31-6b6e91ccf39b"
现有实体与控制器代码
User.php
#[ORM\Entity(repositoryClass: UserRepository::class)] #[ORM\Table(name: '`user`')] class User { #[ORM\Id] #[ORM\Column(type: 'uuid', unique: true)] #[ORM\GeneratedValue(strategy: "CUSTOM")] #[ORM\CustomIdGenerator(class: 'doctrine.uuid_generator')] private $id; #[ORM\Column(type: 'string', length: 255, unique: true)] private $username; #[ORM\OneToMany(mappedBy: 'toUser', targetEntity: Message::class, orphanRemoval: true)] private $messages; // ... 其他代码 }
Message.php
#[ORM\Entity(repositoryClass: MessageRepository::class)] class Message { #[ORM\Id] #[ORM\Column(type: 'uuid', unique: true)] #[ORM\GeneratedValue(strategy: "CUSTOM")] #[ORM\CustomIdGenerator(class: 'doctrine.uuid_generator')] private $id; #[ORM\ManyToOne(targetEntity: User::class, inversedBy: 'messages')] #[ORM\JoinColumn(nullable: false)] private $toUser; #[ORM\ManyToOne(targetEntity: User::class, inversedBy: 'messages')] #[ORM\JoinColumn(nullable: false)] private $fromUser; #[ORM\Column(type: 'datetime_immutable')] private $createdAt; #[ORM\Column(type: 'datetime', nullable: true)] private $updatedAt; #[ORM\Column(type: 'text')] private $content; public function getId(): ?UuidV6 { return $this->id; } // ... 其他代码 }
MessageController.php
public function getAllMessages() { $serialized = $serializer->serialize($messages, 'json', [ ObjectNormalizer::SKIP_NULL_VALUES => true, ObjectNormalizer::IGNORED_ATTRIBUTES => ['username', 'messages', 'created_at'], ObjectNormalizer::CIRCULAR_REFERENCE_HANDLER => function ($object) { return $object->getId(); }, ]); $dataMessage = json_decode($serialized); $json = json_encode([ "data" => array("recipients" => $dataMessage, "count" => count($dataMessage)) ]); }
解决方案
出现__initializer__是因为Doctrine关联对象默认是延迟加载的代理类,序列化时会输出代理类的内部属性。以下三种方法可以实现仅输出ID的需求:
方法1:添加虚拟属性(推荐)
在Message实体中添加返回ID的方法,并通过注解指定序列化后的键名:
use Symfony\Component\Serializer\Annotation\SerializedName; class Message { // ... 原有代码 #[SerializedName('toUser')] public function getToUserId(): ?string { return $this->toUser?->getId()?->toString(); } #[SerializedName('fromUser')] public function getFromUserId(): ?string { return $this->fromUser?->getId()?->toString(); } }
然后修改控制器的序列化配置,忽略原有的toUser/fromUser字段,改用新的虚拟属性:
$serialized = $serializer->serialize($messages, 'json', [ ObjectNormalizer::SKIP_NULL_VALUES => true, ObjectNormalizer::IGNORED_ATTRIBUTES => ['username', 'messages', 'created_at', 'toUser', 'fromUser'], ObjectNormalizer::ATTRIBUTES => ['id', 'content', 'createdAt', 'updatedAt', 'toUserId', 'fromUserId'], ObjectNormalizer::CIRCULAR_REFERENCE_HANDLER => function ($object) { return $object->getId(); }, ]);
方法2:使用序列化回调处理字段
无需修改实体,直接在序列化时通过回调函数处理toUser/fromUser字段:
$serialized = $serializer->serialize($messages, 'json', [ ObjectNormalizer::SKIP_NULL_VALUES => true, ObjectNormalizer::IGNORED_ATTRIBUTES => ['username', 'messages', 'created_at'], ObjectNormalizer::CALLBACKS => [ 'toUser' => function ($user) { return $user?->getId()?->toString(); }, 'fromUser' => function ($user) { return $user?->getId()?->toString(); }, ], ObjectNormalizer::CIRCULAR_REFERENCE_HANDLER => function ($object) { return $object->getId(); }, ]);
方法3:配置属性提取规则
通过ATTRIBUTES指定关联字段仅提取ID,再配合回调转成字符串:
$serialized = $serializer->serialize($messages, 'json', [ ObjectNormalizer::SKIP_NULL_VALUES => true, ObjectNormalizer::IGNORED_ATTRIBUTES => ['username', 'messages', 'created_at'], ObjectNormalizer::ATTRIBUTES => [ 'id', 'content', 'createdAt', 'updatedAt', 'toUser' => ['id'], 'fromUser' => ['id'], ], ObjectNormalizer::CALLBACKS => [ 'toUser' => function ($userData) { return $userData['id']->toString(); }, 'fromUser' => function ($userData) { return $userData['id']->toString(); }, ], ObjectNormalizer::CIRCULAR_REFERENCE_HANDLER => function ($object) { return $object->getId(); }, ]);
内容的提问来源于stack exchange,提问作者Andrew
相关产品推荐
相关产品推荐

