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

如何序列化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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 09:06:22