Страницы

Поиск по вопросам

понедельник, 3 июня 2019 г.

Как правильно использовать реляции в Yii2?

Интересует как правильно использовать реляции, создавать индексы в таблицах, внешние ключи и как все это потом использовать через hasOne() и hasMany(). Буду предельно конкретен и распишу вопрос максимально широко (мало ли кому потом пригодится). Попрошу ответить как можно конкретнее используя вот такой типичный пример. Допустим у нас есть две таблицы:
Таблица users с полями id (pk int 11), name (varchar 255) Таблица posts с полями id (pk), title (varchar 255), user_id (int 11)
Мы хотим иметь возможность используя модель пользователей получить все записи пользователя, а используя модель постов получить автора поста. Соответственно делаем индекс для поля user_id, добавляем внешний ключ от этого поля к полю id таблицы users. Таким образом мы сможем получить автора поста через hasOne(), но можем ли мы не добавляя еще один внешний ключ получить через hasMany() все посты определенного автора?
Будет здорово, если кто-нибудь объяснит зачем нужно индексировать связующее поле перед добавлением к нему внешнего ключа.
Привожу пример кода как я все это пытаюсь делать.
$this->createIndex('user_id_index', 'posts', 'user_id');
тут я добавляю индекс для поля users_id и называю его user_id_index (кстати очень интересно зачем назвать индексы, как это делать правильно и где потом это имя использовать)
$this->addForeignKey('FK_posts_user_id', 'posts', 'user_id', 'users', 'id');
Тут я добавляю внешний ключ для таблицы posts для поля user_id, которое будет ссылаться на поле id таблицы users и называться FK_posts_user_id. Кстати буду очень признателен если меня поправят, если я что-то делается не так, как я описываю.
По итогу мы получаем внешний ключ и можем получить пользователя из модели с постами через вот такой геттер
public function getUsers() { $this->hasOne(Users::className(), ['id'=> 'user_id']); }
Соответственно чтобы получить все посты пользователя я модели пользователей делаю такого плана геттер:
public function getPosts() { $this->hasMany(Posts::className(), ['user_id', 'id']); }
Теперь подытожим вопросы:
Правильно ли я делаю? Правильный ли ход мысли? Нужно ли еще добавлять внешний ключ для таблицы пользователей (id,который ссылается на поле user_id в таблице posts)? Для чего нужно называть индексы и где потом эти названия используются? Где используются названия внешних ключей и для чего они нужны?
Буду очень благодарен за разъяснения и советы.


Ответ

Спустя некоторое время копания кода, доков и различных эксперементов с моделями и внешними ключами могу сделать заключение и ответить на свой собственный вопрос. Возможно это кому-нибудь пригодится.
В представленном мною коде есть ошибка. haseOne() и hasMany() должны возвращать результат выполнения их же. Так что проверяйте есть ли return в вашем методе. В базе данных однозначно достаточно только одного внешнего ключа (в моем примере таблица posts) и одного индекса (для связующего поля, в моем примере поле user_id таблицы posts). Все это можно реализовать через миграции, что вполне удобно. В коде модели, как я уже писал, достаточно написать метод haseOne() для модели с постами (в контроллере например можно будет юзать такую конструкцию чтобы получить данные об авторе поста $model->user; Соответственно ваш метод в модели должен назваться getUser() ) Для получения всех постов автора делаем тоже самое, но с моделью юзеров и описываем метод getPost() (назвать можете как хотите, только не забудьте про get в начале), внутри него возвращаем hasMany(). Точно так же можно получить все посты пользователя если заюзать $model->post;
p.s. Если у кого-то не выходит создать внешний ключ и валится ошибка вида Cannot add foreign key constraint - проверьте чтобы данные первичного ключа (id) и связующего поля (user_id) были идентичны. Если у юзера в поле id стоит NOTNULL - то и у связующего поля тоже он должен стоять, так же должен быть выбран одинаковый тип данный у полей (например и там и там INT(11).
Для моего примера приведу рабочий код для миграции, который вам создаст индекс и внешний ключ, необходимый для работы. Чуть поменяете под себя (измените названия полей и таблицы исходя из моего примера в вопросе и вуаля!)
$this->createIndex('user_id_index', 'posts', 'user_id'); $this->addForeignKey('FK_posts_user_id', 'posts', 'user_id', 'users', 'id', CASCADE', 'CASCADE');

Комментариев нет:

Отправить комментарий