Encodera

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.

Оглавление

Связи между таблицами

Таблицы редко живут отдельно: у статьи есть автор и комментарии. Связь — описание того, как записи одной таблицы относятся к записям другой. Описав связь один раз, связанные данные получают одним вызовом.

Подготовка

Пусть комментарии хранятся в таблице comments со столбцами id, article_id, body, created. Создайте её миграцией и сгенерируйте модель:

bin/cake bake migration CreateComments article_id:integer body:text created
bin/cake migrations migrate
bin/cake bake model Comments

Столбец article_id — внешний ключ: в нём лежит номер статьи, к которой относится комментарий.

Описание связи

Связи записывают в методе initialize() класса таблицы:

// src/Model/Table/ArticlesTable.php
public function initialize(array $config): void
{
    parent::initialize($config);
 
    $this->addBehavior('Timestamp');
 
    $this->hasMany('Comments');
}

hasMany('Comments') означает: у статьи много комментариев. Имя столбца article_id ORM выводит из соглашений. Если модель создавалась командой bake model уже после появления внешнего ключа, генератор добавит связи сам.

Виды связей

СвязьЧто означаетГде внешний ключ
hasMany('Comments')У статьи много комментариевcomments.article_id
belongsTo('Articles')Комментарий относится к статьеcomments.article_id
hasOne('Profiles')У пользователя один профильprofiles.user_id
belongsToMany('Tags')У статьи много меток, у метки много статейТаблица articles_tags

hasMany и belongsTo — две стороны одной связи: первую пишут в ArticlesTable, вторую — в CommentsTable.

Загрузка связанных записей

// src/Controller/ArticlesController.php
public function view(int $id): void
{
    $article = $this->Articles->get($id, contain: ['Comments']);
 
    $this->set(compact('article'));
}

Параметр contain перечисляет связи, которые нужно загрузить вместе со статьёй.

<h1><?= h($article->title) ?></h1>
 
<?php foreach ($article->comments as $comment): ?>
    <p><?= h($comment->body) ?></p>
<?php endforeach; ?>

Комментарии лежат в свойстве comments — имя связи строчными буквами. Для связи с одной записью свойство в единственном числе: $comment->article.

В запросе find

$articles = $this->Articles->find()
    ->contain(['Comments'])
    ->all();

Сколько бы статей ни нашлось, комментарии загрузятся одним дополнительным запросом.

Отбор по связанной таблице

$articles = $this->Articles->find()
    ->matching('Comments', fn ($q) => $q->where(['Comments.body LIKE' => '%спасибо%']))
    ->all();

matching() оставляет только статьи, у которых есть подходящий комментарий.

Сохранение связанной записи

$comment = $this->Articles->Comments->newEntity([
    'article_id' => $article->id,
    'body' => 'Отличная статья!',
]);
 
$this->Articles->Comments->save($comment);

Связанная таблица доступна через связь: $this->Articles->Comments. Чтобы newEntity() принял article_id, поле должно быть в $_accessible класса Comment.

Частая ошибка

Обращение к $article->comments без contain даёт null, а не комментарии: CakePHP не выполняет запросы «по требованию». Это защищает от сотен скрытых запросов, но связи нужно перечислять явно. Если связанное свойство пустое, первым делом проверьте contain.

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.