Связь «Многие ко многим» (Many to many)

Создание таблиц

Сначала надо создать таблицы post, tag и post_tag.

yii migrate/create create_posts_table --fields="title:string:notNull,content:text"
yii migrate/create create_tag_table --fields="title:strinf:notNull"
yii migrate/create_junction_table_for_post_and_tag_tables
yii migrate

Также надо создать модели Post и Tag.

# models/Post.php
namespace app\models;
use Yii;

class Post extends \yii\db\ActiveRecord
{
}


# models/Tag.php
namespace app\models;
use Yii;

class Tag extends \yii\db\ActiveRecord
{
}

Вывод тегов

В файле models/Post.php надо добавить следующий код:

# models/Post.php

class Post extends \yii\db\ActiveRecord
{
    public function getTags()
    {
        return $this->hasMany(Tag::classname(), ['id' => 'tag_id'])
            ->viaTable('post_tag', ['post_id' => 'id']);
    }
}

Теперь можно выбирать комментарии:

# controllers/PostController.php

class PostController extends Controller
{
    public function actionView()
    {
        // выборка тегов поста
        $post = Post::findOne(1);
        $tags = $post->tags;
    }
}

Когда идёт обращение к свойству tags, происходит дополнительный SQL-запрос для выборка комментариев. Такое поведение называется «Lazy load», т.е. данные загружается тогда, когда они нужны.

Но при выборке нескольких страниц, для каждой страницы будет происходить дополнительный запрос в базу данных. Чтобы такого не происходило, рекомендуется использовать метод with(), который сразу выберет нужные теги при выборки страниц.

# controllers/PostController.php

class PostController extends Controller
{
    public function actionIndex()
    {
        $posts = Post::find()->with('tags')->all();
        foreach ($posts as $post) {
            // здесь SQL-запросов не будет, т.к. данные загрузились при выборке постов
            $post->tags;
        }
    }
}

Добавление тега

Тег добавляется через метод link().

$post = Post::findOne(4);
$tag = Tag::findOne(1);

// добавить тег к посту
$post->link('tags', $tag);

Если повторно выполнить метод link(), то для поста добавится тотже тег.