Если видео недоступно для просмотра, попробуйте выключить блокировщик рекламы.

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

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

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

Структура

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

<?php

Capsule::schema()->create('posts', function ($table) {
    $table->bigIncrements('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;

use \Illuminate\Database\Eloquent\Model;

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

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

<?php

// User.php
namespace App;

use \Illuminate\Database\Eloquent\Model;

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

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

CRUD

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

<?php

$user = App\User::find(1);
$posts = $user->posts(); // вызывается как метод
// SELECT * FROM posts WHERE user_id = 1
foreach($posts as $post) {
    echo $post;
}

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

<?php

$post = new App\Post();
$post->title = 'title';
$post->body = 'body';
// Метод save автоматически делает установку пользователя внутрь поста.
$user->posts()->save($post);
$user->posts(); // [['id' => 1, 'title' => 'title', 'body' => 'body', 'creator_id' => 1, ...]]

$post2 = $user->posts()->find(1);

$post == $post2; // true

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

Немного по другому устроена работа с другой стороны связи. Вместо обращения к методу creator(), нужно работать со свойством creator:

<?php

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

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

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

<?php

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

// Хорошо
$post->user = $user;

Выборки

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

<?php

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

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

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

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

  1. Один ко Многим
Мы учим программированию с нуля до стажировки и работы. Попробуйте наш бесплатный курс «Введение в программирование» или полные программы обучения по Node, PHP, Python и Java.

Хекслет

Подробнее о том, почему наше обучение работает →