GridView и связанные таблицы в Yii 2

Отображение данных из связанных таблиц в GridView одна из самых распространённых задач при работе с Yii/Yii2.

Рассмотрим, её решения для Yii 2.

Допустим у нас имеются две связанные таблицы. Одна (Users) хранит сведения о пользователях, а вторая () перечень их ролей (Администратор, Пользователь и т. д.).

Как отобразить соответствующие сведения в GridView с поддержкой стандартных сортировки и поиска?

Подготовка
В данной статье предполагается, что у вас уже реализован каркас CRUD интерфейса при помощи Gii или вручную. К сожалению, создание CRUD интерфейса выходит за рамки данной статьи и потому в ней не рассматривается.

Вначале мы должны явно прописать связи между моделями таблиц.

В нашем случае за связь отвечает метод getRole модели Users.

public function getRole()
{
    return $this->hasOne(User_roles::className(), ['id' => 'role_id']);
}

Если вы используете, для автоматизации выполнения рутинных задач Gii и связь между таблицами зафиксирована на уровне базы данных, то вам, скорее всего не о чем будет беспокоиться. Gii создаст связь автоматически.

Далее создаём в модели поиска (в нашем случае класс SearchUsers) поле по которому мы будем осуществлять поиск.

public $roleName;

Так как в модели Users свойство role_id имеет тип integer, а свойство role доступно только для чтения, нам необходимо дополнительное поле, чтобы мы могли осуществлять поиск и сортировку по названию роли, а не по её id.

Реализация

В начала продолжим дорабатывать модель поиска.

Пропишем наше новое поле в rules, чтобы оно стало доступным для стандартного поиска.

public function rules()
{
    return [
        [['id', 'role_id'], 'integer'],
        [['user_name', 'fio', 'pass','roleName'], 'safe'],
    ];
}

Далее нам нужно доработать метод search.

Получим сведения о ролях пользователей при помощи метода joinWith объекта ActiveQuery.

$query = Users::find();
$query->joinWith(['role']);

Обратите внимание, в методе joinWidth мы указываем свойство role.

Теперь добавим для dataProvider критерии для сортировки по нашему полю.

$dataProvider->sort->attributes['roleName'] = [
    'asc' => [User_roles::tableName().'.user_role' => SORT_ASC],
    'desc' => [User_roles::tableName().'.user_role' => SORT_DESC],
];

Для параметров asc и desc мы указываем уже имя таблицы, в которой хранятся роли (получаем его при помощи метода tableName соответствующей модели) и название поля, по которому нужно выполнить сортировку.

Названия таблицы и поля указываются в виде строки в формате «Таблица.Поле». Поэтому, не точка перед именем поля обязательна.

Для полей созданных самостоятельно (как в нашем примере) критерии для сортировки следует обязательно указывать до вызова метода load объекта ActiveQuery.

Зададим критерии для поиска.

$query->andFilterWhere(['like', 'user_name', $this->user_name])
    ->andFilterWhere(['like', 'fio', $this->fio])
    ->andFilterWhere(['like', User_roles::tableName().'.user_role', $this->roleName]);

Первые два вызова метода andFilterWhere в приведённой цепочке задают критерии для поиска по полям исходной модели user_name и fio. Третий задаёт эти критерии уже для нашего поля.

Критерии поиска задаются в виде простого массива.

Первый элемент – метод поиска (строка). Здесь задан метод like используемый в Yii2 по умолчанию (поиск по частичному совпадению).

Второй элемент – поле в таблице, по которому осуществляется поиск (в нашем случае с указанием названия таблицы, как при сортировке, т. к. поле находится в другой таблице, а не в той, которой сопоставлена данная модель).

Третий элемент — поле модели, которому сопоставляется предыдущий элемент.

Всё вместе

Ниже приведён пример метода search, в котором все вышеописанные действия уже выполнены.

public function search($params)
{
    $query = Users::find();
    $query→joinWith(['role']);
    $dataProvider = new ActiveDataProvider([
        'query' => $query,
    ]);
    $dataProvider->sort->attributes['roleName'] = [
        'asc' => [User_roles::tableName().'.user_role' => SORT_ASC],
        'desc' => [User_roles::tableName().'.user_role' => SORT_DESC],
    ];
    $this→load($params);
    if (!$this->validate()) {
        return $dataProvider;
    }
    $query->andFilterWhere(['like', 'user_name', $this→user_name])
        ->andFilterWhere(['like', 'fio', $this→fio])
        ->andFilterWhere(['like', User_roles::tableName().'.user_role', $this→roleName]);
    return $dataProvider;
}

Настройка GridView

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

Если вы использовали Gii, то по умолчанию в GridView будут отображаться только собственные поля модели.

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

<?=GridView::widget(['dataProvider' => $dataProvider,'filterModel' => $searchModel,
    'columns' => [['class' => 'yii\grid\SerialColumn'],
        ['attribute' => 'user_name','label' => 'Логин'],
        ['attribute' => 'fio','label' => 'ФИО'],
        ['attribute' => 'role_id','label' => 'Роль'],
        ['class' => 'yii\grid\ActionColumn']]]);
?>

В столбце «Роль» при этом будут находиться целые числа — идентификаторы ролей в соответствующей таблице базы данных.

По этим «цифрам» можно выполнить сортировку и поиск. Но, как уже сказано выше, это не то, что нам нужно.

Заменим role_id на созданное нами поле roleName и установим в качестве отображаемого значения название роли. Последнее доступно посредством ранее упомянутого свойства role.

<?=GridView::widget(['dataProvider' => $dataProvider,'filterModel' => $searchModel,
    'columns' => [['class' => 'yii\grid\SerialColumn'],
        ['attribute' => 'user_name','label' => 'Логин'],
        ['attribute' => 'fio','label' => 'ФИО'],
        ['attribute' => 'roleName','label' => 'Роль', 'value'=>'role.user_role'],
        ['class' => 'yii\grid\ActionColumn']]]);
?>

Наша таблица преобразилась.

Также изменилась работа сортировки и поиска. И в том и в другом механизме теперь используется название роли вместо её идентификатора.

Комментарии
  1. Спасибо что разжевал мне все, на остальных ресурсах понять не мог.

  2. Спасибо большое за статью! для новичка очень полезно!

  3. Спасибо тебе, добрый человек! Реально очень полезно оказалось. Базовый функционал, пригодится еще много раз.

  4. Спасибо за материал! Подскажите, пожалуйста, у вас связанные таблицы находятся в одной БД, а если связанные таблицы находятся в разных БД как сделать?

  5. Можно попытаться использовать распределённые запросы (если они поддерживаются вашей СУБД) или настроить Yii 2, так, чтобы он работал с двумя БД одновременно (делается это через настройку компонентов для каждой БД и переопределение метода getDb в моделях).

  6. Спасибо большое! На других ресурсах такой инструкции не находил. ????

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *