Как реализовать отношение Yii2 через строку, а не массив?
У меня есть класс с именем ChatMessage. В БД есть 2 основных столбца (from_user, to_user). Я хочу связать один из этих 2 столбцов с идентификатором модели пользователя. Как я могу использовать запрос отношения, который выглядит следующим образом
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');
}
2 ответа
Краткий ответ: это невозможно.
Отношения ActiveRecord сложнее, чем вы думаете, и поддержка таких условий надежным способом действительно сложна / невозможна. Проблема в том, что отношение может использоваться в разных контекстах:
- Присоединиться:
select * from chat_messages join users on users.id = chat_messages.from_user
, - Ленивая загрузка:
select * from users where users.id = 1
, - Стремительная загрузка:
select * from users where users.id IN (1, 2, 3, 4)
,
Как вы видите, трудно конвертировать ваши on
условие, чтобы соответствовать каждому из этих трех типов запросов ( см. эту проблему на GitHub).
Вы можете попытаться переопределить ActiveQuery
поддерживать ваше состояние, но гораздо проще (и, вероятно, достаточно хорошо) использовать два отношения и создать несколько помощников, чтобы сделать доступ к этим отношениям более удобным:
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;
}
Триггер нетерпеливо загружается:
ChatMessage::find()->with(['userFrom', 'userTo'])->all();
Этого должно быть достаточно для простых случаев. Для более сложных сценариев вам нужно будет писать запросы вручную.
Обратите внимание, что с помощью Yii::$app->user->id
в вашей модели это считается плохой практикой
В итоге, модели /.../
- НЕ должен напрямую обращаться к запросу, сеансу или любым другим данным об окружающей среде. Эти данные должны быть введены контроллерами в модели;
https://www.yiiframework.com/doc/guide/2.0/en/structure-models
Вы можете достичь этого, просто добавив оператор if в свою функцию отношения. Просто напишите case
логика в выражении if, как написано ниже:
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']);
}