Yii2 - 与多列有很多关系

Yii2 - hasMany relation with multiple columns

我有一个 table message_thread:

id
sender_id
recipient_id

我想在我的 User 模型中声明一个关系,它将按如下方式获取所有消息线程:

SELECT *
FROM message_thread
WHERE sender_id = {user.id}
    OR recipent_id = {user.id}

我试过以下方法:

public function getMessageThreads()
{
    return $this->hasMany(MessageThread::className(), ['sender_id' => 'id'])
        ->orWhere(['recipient_id' => 'id']);
}

但它会生成一个 AND 查询。有人知道怎么做吗?

对于此查询

SELECT *
FROM message_thread
WHERE sender_id = {user.id}
    OR recipent_id = {user.id}

你可以使用这些

$query = (new \yii\db\Query)->from("message_thread")
$query->orFilterWhere(['sender_id'=>$user_id])->orFilterWhere(['recipent_id '=>$user_id]);

您不能以这种方式创建常规关系 - Yii 将无法为预先加载映射相关记录,因此它不支持这种方式。你可以找到一些解释 int this answer and related issue on GitHub.

根据用例,您可以尝试两种方法来获得类似的东西:

1。两个正则关系和getter简化访问

public function getSenderThreads() {
    return $this->hasMany(MessageThread::className(), ['sender_id' => 'id']);
}

public function getRecipientThreads() {
    return $this->hasMany(MessageThread::className(), ['recipient_id' => 'id']);
}

public function getMessageThreads() {
    return array_merge($this->senderThreads, $this->recipientThreads);
}

通过这种方式,发送者线程和接收者线程有两个独立的关系,因此您可以直接将它们与连接或预先加载一起使用。但是你也有 getter 这将 return 两个关系的结果,所以你可以通过 $model->messageThreads.

访问所有线程

2。假关系

public function getMessageThreads()
{
    $query = MessageThread::find()
        ->andWhere([
            'or',
            ['sender_id' => $this->id],
            ['recipient_id' => $this->id],
        ]);
    $query->multiple = true;

    return $query;
}

这不是真正的关系。您将无法将它用于预先加载或连接,但它会在一个查询中获取所有用户线程,您仍然可以将它用作常规活动记录关系 - $model->getMessageThreads() 将 return ActiveQuery$model->messageThreads 模型数组。


为什么 orOnCondition() 不起作用

orOnCondition() and andOnCondition() 用于附加 ON 条件,这些条件将始终使用 AND 附加到基本关系条件。所以如果你有这样定义的关系:

$this->hasMany(MessageThread::className(), ['sender_id' => 'id'])
    ->orOnCondition(['recipient_id' => new Expression('id')])
    ->orOnCondition(['shared' => 1]);

它将生成如下条件:

sender_id = id AND (recipent_id = id OR shared = 1)

如您所见,orOnCondition() 定义的条件与 hasMany() 中定义的条件和关系分开,并且它们始终使用 AND.

连接