【问题标题】:How to create a virtual field based on another hasMany table如何基于另一个 hasMany 表创建虚拟字段
【发布时间】:2026-02-19 00:55:01
【问题描述】:

这是一个与this one 非常相似的问题,但表之间的关系不同。

我有 3 个表:usersnotificationsuser_notificationsuser_notifications 是用户和通知之间的连接表,表示已发送给用户的通知实例。

关系如下:

User => hasMany   => UserNotification
UserNotification => belong to => User, Notification  
Notification => hasMany => UserNotification

各栏目如下: 用户 => id、姓名
UserNotification => id, user_id, notification_id 通知 => id,消息

我想创建一个名为 Notification.users 的虚拟字段,它只包含一个字符串列表,其中包含已发送该特定通知的所有用户,例如:

User => id = 1, name = Bob
User => id = 2, name = Bill

UserNotification => id = 1, user_id = 1, notification_id = 1
UserNotification => id = 2, user_id = 2, notification_id = 1

Notification => id = 1, message = "Hello World!"

所以通知 1,“Hello World!”已发送给用户 1 和 2、Bob 和 Bill。因此,虚拟字段Notification.users 包含这两个名称的逗号分隔列表,我看到了:

Notification => id = 1, message = "Hello World!", users = "Bob,Bill"

【问题讨论】:

    标签: php postgresql cakephp virtual


    【解决方案1】:

    最终为我工作的代码是这样的:

    class Notification extends AppModel
    {
        public $virtualFields = array(
            "users" => "SELECT string_agg(users.name, ',') FROM users where users.id IN (SELECT user_notifications.user_id FROM user_notifications where user_notifications.notification_id = Notification.id)"
    );
    

    string_agg() 使用提供的可选分隔符将选定的列聚合成一个字符串。我一开始遇到的一个问题是其他表的 CakePHP 别名(“User”、“UserNotification”)的用户;这会导致“未提供表”错误,因此我使用了实际的数据库表名。默认情况下,值不按特定顺序馈送到string_agg(),这可能会导致字符串有些混乱。 string_agg() 支持 order by 子句来解决这个问题,如下(注意分隔符 ',' 和 order by 子句之间缺少逗号):

    class Notification extends AppModel
    {
        public $virtualFields = array(
            "users" => "SELECT string_agg(users.name, ',' ORDER BY users.name) FROM users where users.id IN (SELECT user_notifications.user_id FROM user_notifications where user_notifications.notification_id = Notification.id)"
    );
    

    这可行,对我来说似乎是最简单的解决方案。还有更优雅或更正确的吗?

    【讨论】: