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

Связь «Многие ко многим» позволяет реализовать такой функционал, как теги.

Для таблиц «posts» и «tags» надо создать таблицу posts_tags, с полями «post_id» и «tag_id». Затем в модели надо добавить метод $this->belongsToMany(), внутри которого указать таблицу с тегами.

# src/Model/Table/PostsTable.php

class PostsTable extends Table
{
    public function initialize(array $config): void
    {
        // …
        $this->belongsToMany('Tags');
    }
}

Теперь когда надо выбрать статью вместе с тегами, надо добавить параметр contain.

$post = $this->Posts->get($id, [
    'contain' => 'Tags'
]);

// пример вывода тегов
foreach ($post->tags as $tag) {
    echo $this->Html->link($tag->name, ['controller' => 'Tags', 'action' => 'view', $tag->id]);
}

Форма для выборки тегов

По умолчанию CakePHP определяет поле «tags._ids» как список для выбора тегов. Добавить теги в данный список можно через свойство options.

# контроллер
$tags = $this->Posts->Tags->find('list');
$this->set(compact('tags'));

# шаблон
echo $this->Form->control('tags._ids', ['options' => $tags]);

Добавить или изменить тег

Добавить теги можно через следующий код:

$post = $this->Posts->get($id);
$data = [
    'title' => 'Имя страницы',
    'body' => 'Контент',
    // добавление тегов
    'tags' => [
        ['name' => 'php'],
        ['name' => 'cakephp'],
    ],
];

$post = $this->Posts->patchEntity($post, $data);
$this->Posts->save($post);

Код выше всегда добавляет тег (даже если данный тег уже есть).

Указать ИД тегов можно через следующий код:

$post = $this->Posts->get($id);
$data = [
    'title' => 'Имя страницы',
    'body' => 'Контент',
    // указать ИД тегов
    'tags' => [
        '_ids' => [2,5],
    ],
];

$post = $this->Posts->patchEntity($post, $data);
$this->Posts->save($post);

Вывести страницы по указанному тегу

Сначала в модели тегов надо добавить метод belongsToMany(), внутри которого указать таблицу со страницами.

# src/Model/Table/TagsTable.php

class TagsTable extends Table
{
    public function initialize(array $config): void
    {
        // …
        $this->belongsToMany('Posts');
    }
}

Теперь когда надо выбрать тег вместе с его страницами, надо добавить параметр contain.

$tag = $this->Tags->get($id, [
    'contain' => 'Posts'
]);

// пример вывода постов
foreach ($tag->posts as $post) {
    echo $this->Html->link($post->title, ['controller' => 'Posts', 'action' => 'view', $post->id]);
}