Зарегистрируйтесь, чтобы продолжить обучение

Связи PHP: Eloquent (ORM)

Сущности предметной области существуют не сами по себе. Они часто зависят друг от друга. На уровне базы данных такие связи задаются через внешние ключи или даже промежуточные таблицы, как в случае связи "многие ко многим". ORM, в свою очередь, используют эти ключи, для работы со связями. Добавляет множество полезных методов, которые упрощают работу с зависимыми сущностями: выборкой, добавлением, модификацией и удалением.

Так как в этом курсе мы делаем блог, нам понадобится сущность Post. Пользователи связаны с постами "один ко многим":

  • Один пользователь может быть автором многих постов
  • У одного поста всегда один автор

Структура

Для поддержки такой связи при создании таблицы постов, нужно добавить внешний ключ на таблицу пользователей:

<?php

Capsule::schema()->create('posts', function ($table) {
    $table->id();
    $table->string('state')->nullable();
    $table->string('title');
    $table->text('body');
    // Поле которое будет внешним ключом
    $table->bigInteger('creator_id');
    // Добавление внешнего ключа (ограничения)
    $table->foreign('creator_id')->references('id')->on('users');
    $table->timestamps();
});

По умолчанию Eloquent не воспринимает внешние ключи как что-то особенное. Она требует (как и большинство ORM) явного указания связи на уровне моделей. Для этого в каждой из моделей определяется специальный метод, через который будет происходить всё взаимодействие между связанными сущностями. Имя этого метода произвольно и выбирается так, чтобы лучше отражать суть связи: У поста есть автор, у каждого автора есть посты. Каждый такой метод должен вернуть вызов другого метода, отвечающего за связь. В примере ниже это belongsTo и hasMany.

<?php

// Post.php
namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Post extends Model
{
    public function creator()
    {
        // Принадлежит пользователю
        // belongsTo определяется у модели содержащей внешний ключ
        return $this->belongsTo('App\Models\User');
    }
}

Вторым параметром метод belongsTo ожидает имя внешнего ключа, по которому строится связь: $this->belongsTo('App\Models\User', 'creator_id'). Имя ключа можно не указывать, потому что Eloquent может сформировать его автоматически. В этом случае имя ключа сформируется по шаблону имя метода_id.

Так происходит потому, что в Eloquent принято соглашение в именовании сущностей и методов. Подробнее об этом можно почитать в документации.

<?php

// User.php
namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class User extends Model
{
    // Во множественном числе потому что это коллекция
    public function posts()
    {
        // У каждого пользователя много постов
        // hasMany определяется у модели, имеющей внешние ключи в других таблицах
        return $this->hasMany('App\Models\Post', 'creator_id');
    }
}

Метод hasMany также поддерживает соглашение для определения имени внешнего ключа. Только здесь оно определяется не по имени метода, а по имени модели, в которой описывается связь. Для модели User это будет user_id. В нашем случае такая логика не работает, поэтому имя свойства указано явно.

CRUD

Теперь Eloquent знает о связях и даёт работать с ними напрямую:

<?php

$user = App\Models\User::find(1);
// SELECT * FROM posts WHERE creator_id = 1
foreach($user->posts as $post) {
    echo $post;
}

Обращение к коллекции зависимых сущностей возвращает специальный объект, который может использоваться как массив.

Есть и другой способ взаимодействовать с зависимостями. Вызов постов как метода, позволяет управлять этой коллекцией, например удалить или добавить новый пост:

<?php

// В пост автоматически устанавливается пользователь
$post = $user->posts()->make(); // Параметры поста можно передать в make
$post->title = 'title';
$post->body = 'body';
$post->save();

$user->posts; // [['id' => 1, 'title' => 'title', 'body' => 'body', 'creator_id' => 1, ...]]

// В WHERE добавится creator_id = <идентификатор пользователя>
$post2 = $user->posts()->find(1);

$post->is($post2); // true

// Удаление всех постов одним запросом
$user->posts()->delete();

То же самое происходит и с другой стороны связи:

<?php

$post = App\Models\Post::find(1);
$post->creator->first_name;

// Установка пользователя
$post->creator()->associate($user);
$post->save();

При работе со связями важно переключиться от мышления через таблицы и ключи к сущностям и связям (почему). Технически это значит, что код опирается на сами сущности, а не их идентификаторы:

<?php

// Плохо
$post->user_id = $user->id;

// Хорошо
$post->creator()->associate($user);

Выборки

Все типы связей в Eloquent поддерживают построение запросов на выборку:

<?php

// В запрос будет включено условие по creator_id равным текущему пользователю
// SELECT * FROM posts WHERE creator_id = 1 AND state = 'active'
$user->posts()->where('state', 'active')->get();

Самостоятельная работа

  1. Заполните $fillable у Post.
  2. Изучите связи внутри Post.
  3. Откройте REPL и создайте несколько постов.

Дополнительные материалы

  1. Один ко Многим

Аватары экспертов Хекслета

Остались вопросы? Задайте их в разделе «Обсуждение»

Вам ответят команда поддержки Хекслета или другие студенты

Для полного доступа к курсу нужен базовый план

Базовый план откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, проектам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.

Получить доступ
1000
упражнений
2000+
часов теории
3200
тестов

Открыть доступ

Курсы программирования для новичков и опытных разработчиков. Начните обучение бесплатно

  • 130 курсов, 2000+ часов теории
  • 1000 практических заданий в браузере
  • 360 000 студентов
Отправляя форму, вы принимаете «Соглашение об обработке персональных данных» и условия «Оферты», а также соглашаетесь с «Условиями использования»

Наши выпускники работают в компаниях:

Логотип компании Альфа Банк
Логотип компании Aviasales
Логотип компании Yandex
Логотип компании Tinkoff
Рекомендуемые программы
профессия
от 25 000 ₸ в месяц
Разработка веб-приложений на Laravel
10 месяцев
с нуля
Старт 26 декабря

Используйте Хекслет по-максимуму!

  • Задавайте вопросы по уроку
  • Проверяйте знания в квизах
  • Проходите практику прямо в браузере
  • Отслеживайте свой прогресс

Зарегистрируйтесь или войдите в свой аккаунт

Отправляя форму, вы принимаете «Соглашение об обработке персональных данных» и условия «Оферты», а также соглашаетесь с «Условиями использования»