问题
I have a class named ChatMessage. There are 2 main columns in DB (from_user, to_user). I want to relate one of these 2 columns to User model id. How can I use a relation query that looks like this
public function getChattedUser()
{
return $this->hasOne(User::className(), '(case when(chat_message.from_user={Yii::$app->user->id}) then chat_message.to_user else chat_message.from_user END)=user.id');
}
回答1:
Short answer: this is not possible.
ActiveRecord relations are more complicated than you may think and supporting such conditions in reliable way is really complicated/impossible. The problem is that relation may be used in different contexts:
- Join:
select * from chat_messages join users on users.id = chat_messages.from_user
. - Lazy loading:
select * from users where users.id = 1
. - Eager loading:
select * from users where users.id IN (1, 2, 3, 4)
.
As you may see it is hard to convert your on
condition to fit each of this three types of queries (see this issue at GitHub).
You may try to override ActiveQuery
to support your condition, but much easier (and probably good enough) is to use two relations and create some helpers to make accessing these relations more convenient:
public function getUserFrom() {
return $this->hasOne(User::className(), ['id' => 'user_from']);
}
public function getUserTo() {
return $this->hasOne(User::className(), ['id' => 'user_to']);
}
public function getUser() {
return Yii::$app->user->id == $this->user_from ? $this->userFrom : $this->userTo;
}
Trigger eager loading:
ChatMessage::find()->with(['userFrom', 'userTo'])->all();
It should be enough for simple cases. For more complex scenarios you will need to write queries manually.
Note that using Yii::$app->user->id
in your model is considered as bad practice:
In summary, models /.../
- should NOT directly access request, session, or any other environmental data. These data should be injected by controllers into models;
https://www.yiiframework.com/doc/guide/2.0/en/structure-models#best-practices
回答2:
You can achieve it simply by adding if statement in your relation function. Just write the case
logic in the if statement as written below:
public function getChattedUser()
{
if ($this->from_user == Yii::$app->user->id) {
return $this->hasOne(User::className(), ['id' => 'to_user']);
}
return $this->hasOne(User::className(), ['id' => 'from_user']);
}
来源:https://stackoverflow.com/questions/50328014/how-to-realize-yii2-relation-through-string-not-array