Intereting Posts
Примечание: Неизвестно: Пропуск цифровой клавиши 1 в Unknown в строке 0 Как получить идентификатор выбранного элемента таблицы в php Сопоставление php integer и float Есть ли способ получить имя переменной? PHP – отражение Как просматривать изображения локальной сети с удаленного интерфейса при использовании <img src = "" />? Проблема с записью json_decode PHP При написании XML лучше писать его или использовать генератор, такой как simpleXML в PHP? назвали необязательные аргументы PHP? Настройка переменной окружения PHP при запуске сценария командной строки WordPress использует UTF-8, а остальная часть моего сайта использует ISO-8859-1 – кодировку символов переменная $ _session WooCommerce получить продукт Изображение из категории ID значения доступа к кодовому значению из внешних файлов не удалось отправить html в почтовую функцию через (php) Google in-app payments: как обращаться с обратной почтой JWT Google

Извлечение данных из таблицы соединений в Yii2

Я пытаюсь получить данные из таблицы соединений в Yii2 без дополнительного запроса. У меня есть две модели (User, Group), связанные через таблицу соединений (user_group). В таблице user_group я хочу сохранить дополнительные данные (флаг администратора, …) для этого отношения.

FYI, вот как я определил отношение в моделях:

Group.php

public function getUsers() { return $this->hasMany(User::className(), ['id' => 'user_id']) ->viaTable('user_group', ['group_id' => 'id']); } 

User.php

 public function getGroups() { return $this->hasMany(Group::className(), ['id' => 'group_id']) ->viaTable('user_group', ['user_id' => 'id']); } 

Короче : использование ActiveRecord для таблицы переходов, как вы предложили, является IMHO правильным способом, поскольку вы можете настроить via() для использования существующего ActiveRecord . Это позволяет использовать метод link() Yii для создания элементов в таблице соединений при одновременном добавлении данных (например, вашего флага администратора).


В официальном руководстве Yii Guide 2.0 указывается два способа использования таблицы соединений: использование viaTable() и использование via() (см. Здесь ). В то время как первое ожидает, что имя таблицы переходов как параметр, последний ожидает имя отношения как параметра.

Если вам нужен доступ к данным внутри таблицы переходов, я бы использовал ActiveRecord для таблицы соединений, как вы предложили, и используйте via() :

 class User extends ActiveRecord { public function getUserGroups() { // one-to-many return $this->hasMany(UserGroup::className(), ['user_id' => 'id']); } } class Group extends ActiveRecord { public function getUserGroups() { // one-to-many return $this->hasMany(UserGroup::className(), ['group_id' => 'id']); } public function getUsers() { // many-to-many: uses userGroups relation above which uses an ActiveRecord class return $this->hasMany(User::className(), ['id' => 'user_id']) ->via('userGroups'); } } class UserGroup extends ActiveRecord { public function getUser() { // one-to-one return $this->hasOne(User::className(), ['id' => 'user_id']); } public function getGroup() { // one-to-one return $this->hasOne(Group::className(), ['id' => 'userh_id']); } } 

Таким образом, вы можете получить данные таблицы соединений без дополнительных запросов, используя отношение userGroups (как и любое другое отношение «один ко многим»):

 $group = Group::find()->where(['id' => $id])->with('userGroups.user')->one(); // --> 3 queries: find group, find user_group, find user // $group->userGroups contains data of the junction table, for example: $isAdmin = $group->userGroups[0]->adminFlag // and the user is also fetched: $userName = $group->userGroups[0]->user->name 

Все это можно сделать, используя отношение hasMany . Поэтому вы можете спросить, почему вы должны объявлять отношение «многие ко многим», используя via() : поскольку вы можете использовать метод link() Yii для создания элементов в таблице соединений:

 $userGroup = new UserGroup(); // load data from form into $userGroup and validate if ($userGroup->load(Yii::$app->request->post()) && $userGroup->validate()) { // all data in $userGroup is valid // --> create item in junction table incl. additional data $group->link('users', $user, $userGroup->getDirtyAttributes()) } 

Поскольку я не получил ответа почти 14 дней, я опубликую, как я решил эту проблему. Это не совсем то, что я имел в виду, но это работает, этого достаточно. Итак … это то, что я сделал:

  1. Добавлена ​​модель UserGroup для таблицы соединений
  2. Добавлено отношение к группе

     public function getUserGroups() { return $this->hasMany(UserGroup::className(), ['user_id' => 'id']); } 
  3. Присоединился к UserGroup в моей функции поисковой модели

     $query = Group::find()->where('id =' . $id)->with('users')->with('userGroups'); 

Это получило меня, что я хотел, группа со всеми пользователями и, представленная моей новой моделью UserGroup , данными из таблицы соединений.

Я подумал о том, чтобы сначала расширить функцию построения Yii2 – это может быть лучшим способом решить эту проблему. Но так как я еще не знаю Yii2, я решил не делать этого.

Пожалуйста, дайте мне знать, если у вас есть лучшее решение.

Я не знаю наверняка, это лучшее решение. Но для моего проекта это будет хорошо на данный момент 🙂

1) Левое соединение

Добавить новый атрибут класса в User шаблон public $flag; , Добавьте две строки в основное соотношение, но не удаляйте viaTable это может (и должно) остаться.

 public function getUsers() { return $this->hasMany(User::className(), ['id' => 'user_id']) ->viaTable('user_group', ['group_id' => 'id']) ->leftJoin('user_group', '{{user}}.id=user_id') ->select('{{user}}.*, flag') //or all ->select('*'); } 

leftJoin позволяет выбрать данные из таблицы соединений и select leftJoin для настройки возвращаемых столбцов. Помните, что viaTable должен оставаться, потому что ссылка () полагается на него.

2) запрос подвыбора

Добавить новый атрибут класса в User шаблон public $flag;

А в модели Group изменено отношение getUsers() :

 public function getUsers() { return $this->hasMany(User::className(), ['id' => 'user_id']) ->viaTable('user_group', ['group_id' => 'id']) ->select('*, (SELECT flag FROM user_group WHERE group_id='.$this->id.' AND user_id=user.id LIMIT 1) as flag'); } 

Как вы можете видеть, я добавил дополнительный выбор для списка выбора по умолчанию. Этот выбор предназначен для негрупповой модели пользователей. Да, я согласен, что это немного уродливо, но делает работу.

3) Соотношения условий

Другим вариантом является создание только одного отношения для администраторов:

 // Select all users public function getUsers() { .. } // Select only admins (users with specific flag ) public function getAdmins() { return $this->hasMany(User::className(), ['id' => 'user_id']) ->viaTable('user_group', ['group_id' => 'id'], function($q){ return $q->andWhere([ 'flag' => 'ADMIN' ]); }); } 

$Group->admins – получить пользователей с определенным флагом администратора. Но это решение не добавляет атрибут $flag . Вам нужно знать, когда вы выбираете только администраторов и всех пользователей. Недостаток: вам нужно создать отдельное отношение для каждого значения флага.


Ваше решение с использованием отдельной модели UserGroup прежнему является более гибким и универсальным для всех случаев. Как вы можете добавить валидацию и базовые материалы ActiveRecord. Эти решения – более одностороннее направление – чтобы получить материал.

Для этого я создал простое расширение, которое позволяет привязывать столбцы в таблице подключений к дочерней модели в качестве свойств. Поэтому после настройки этого расширения вы сможете получить доступ к атрибутам таблицы соединений, например

 foreach ($parentModel->relatedModels as $childModel) { $childModel->junction_table_column1; $childModel->junction_table_column2; .... } 

Для получения дополнительной информации ознакомьтесь с расширением атрибутов таблицы Yii2

Благодарю.