Зарегистрируйтесь для доступа к 15+ бесплатным курсам по программированию с тренажером

Связи 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.

<?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. Один ко Многим

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

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

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

Ошибки, сложный материал, вопросы >
Нашли опечатку или неточность?

Выделите текст, нажмите ctrl + enter и отправьте его нам. В течение нескольких дней мы исправим ошибку или улучшим формулировку.

Что-то не получается или материал кажется сложным?

Загляните в раздел «Обсуждение»:

  • задайте вопрос. Вы быстрее справитесь с трудностями и прокачаете навык постановки правильных вопросов, что пригодится и в учёбе, и в работе программистом;
  • расскажите о своих впечатлениях. Если курс слишком сложный, подробный отзыв поможет нам сделать его лучше;
  • изучите вопросы других учеников и ответы на них. Это база знаний, которой можно и нужно пользоваться.

Об обучении на Хекслете

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

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

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

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

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

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

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

Логотип компании Альфа Банк
Логотип компании Aviasales
Логотип компании Yandex
Логотип компании Tinkoff
Рекомендуемые программы

С нуля до разработчика. Возвращаем деньги, если не удалось найти работу.

Иконка программы PHP-разработчик
Профессия
Разработка веб-приложений на Laravel
25 мая 10 месяцев

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

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

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

Даю согласие на обработку персональных данных, соглашаюсь с «Политикой конфиденциальности» и «Условиями оказания услуг»