Связи между моделями
Таблицы связаны между собой: у статьи есть автор, у статьи много комментариев. Связь в Yii — метод модели, который объясняет, как найти связанные строки. После этого к ним обращаются как к обычному свойству.
Подготовка
Нужна вторая таблица. Создайте миграцию php yii migrate/create create_comment_table со столбцами id, post_id (целое число) и text (текст), выполните её и создайте модель:
// models/Comment.php
namespace app\models;
use yii\db\ActiveRecord;
class Comment extends ActiveRecord
{
}Метод tableName() здесь не нужен: имя таблицы comment совпадает с именем класса.
Один ко многим: hasMany
Связь описывают в классе модели методом, имя которого начинается с get. Добавьте в models/Post.php:
// models/Post.php
public function getComments()
{
return $this->hasMany(Comment::class, ['post_id' => 'id']);
}В массиве слева — столбец связанной таблицы (comment.post_id), справа — столбец текущей (post.id). К связи обращаются как к свойству, без get и без скобок:
$post = Post::findOne(1);
foreach ($post->comments as $comment) {
echo $comment->text;
}Код выведет тексты всех комментариев первой статьи. Запрос к таблице comment выполняется при первом обращении к свойству.
Обратная связь: hasOne
У комментария одна статья. Добавьте в models/Comment.php:
// models/Comment.php
public function getPost()
{
return $this->hasOne(Post::class, ['id' => 'post_id']);
}Теперь $comment->post->title вернёт заголовок статьи. Точно так же в Post описывают автора: hasOne(User::class, ['id' => 'user_id']).
Загрузка заранее
Список из ста статей, где для каждой выводится число комментариев, выполнит сто один запрос: один за статьями и по одному на каждую. Метод with() загружает связь одним дополнительным запросом:
// controllers/PostController.php
$posts = Post::find()->with('comments')->all();Запросов станет два при любом числе статей. Несколько связей перечисляют массивом: with(['comments', 'author']).
Условие по связанной таблице
$posts = Post::find()
->joinWith('comments')
->where(['like', 'comment.text', 'спасибо'])
->all();joinWith() соединяет таблицы в одном запросе, поэтому в условии можно использовать столбцы комментариев. Результат — статьи, где есть комментарий со словом «спасибо».
Сохранение связанной записи
$comment = new Comment();
$comment->text = 'Отличная статья';
$comment->link('post', $post);Метод link() записывает в post_id номер статьи и сохраняет комментарий. Первый аргумент — имя связи.
Частая ошибка
$post->comments и $post->getComments() — не одно и то же. Свойство возвращает готовый массив объектов, а метод — запрос, который можно уточнить: $post->getComments()->orderBy(['id' => SORT_DESC])->limit(5)->all(). Попытка перебрать в цикле результат метода без all() не даст комментариев.