В этом уроке я помогу вам понять концепцию полиморфных отношений в Laravel. Обещаю, это будет весело, так как будем рассматривать их на примере пиццы.
Основые типы Отношений
Прежде чем рассмотрим Полиморфизм, давайте быстренько пробежимся по трем основным типам отношений:
- Один к одному (One-to-One)
- Один ко многим (One-to-Many)
- Многие-ко-многим (Many-to-Many)
Один к одному
Эти отношения связывают одну вещь с другой. Одна пицца имеет один тип топпинга (начинки) и наоборот — один топпинг может быть у одной пиццы.
Базовая структура таблиц, для отношения Один-к-Одному будет такой:
pizzas id - integer toppings id - integer name - string pizza_id - integer
Таблица toppings
имеет уникальный внешний ключ pizza_id
, который ссылается на id
таблицы pizzas
, создавая отношения Один-к-Одному.
Конечно, пицца с одним топпингом это смешно. В большинстве случаев в пицце их будет много. Это подводит нас к следующим отношениям.
Один ко многим
Эти отношения связывают одну вещь со многими другими. Согласно нашему сценарию, одна пицца может иметь много топпингов, и, наоборот, множество топпингов могут быть в одной пицце.
Таблицы БД для этих отношений:
pizzas id - integer toppings id - integer name - string pizza_id - integer
Похоже на таблицы предыдущих отношений? Так и есть!
Эти отношения имеют такую же структуру, но есть небольшое отличие. В отношениях Один-к-Одному внешний ключ pizza_id
уникален (unique
). В отношениях Один-ко-Многим» внешний ключ не уникален, что позволяет пицце иметь много топпингов.
А что, если бы мы хотим много пицц со множеством топпингов? Лучший способ — это отношения Многие-ко-Многим» .
Многие ко многим
В этих отношениях мы используем сводную (промежуточную) таблицу для того, чтобы определить отношения между пиццей и ее топпингами.
Структуру таблицы отношений:
pizzas id - integer pizza_toppings pizza_id - integer topping_id - integer toppings id - integer name - string
Сводная таблица pizza_toppings
имеет два внешних ключа, которые ссылаются на таблицы pizzas
и toppings
. Эта таблица и создает отношения между многими пиццами и многими топпингами.
Это три наиболее распространенных типа отношений.
Теперь, когда мы с этим разобрались, то перейдем к полиморфным отношениям.
Полиморфизм
Что если нужно добавить в наш сценарий новый продукт? Например, горячие сандвичи! И чтобы они использовали те же топпинги, что и пицца, то как это оформить в нашей базе данных?
Вот тогда мы и призываем на помощь Полиморфизм!
У Полиморфных Отношений есть сводная таблица, похожая на таблицу отношений «Многие-ко-Многим», но, она имеет дополнительное поле, определяющее тип продукта питания.
Структура таблиц для полиморфных отношений:
pizzas id - integer sandwiches id - integer toppings id - integer name - string toppables topping_id - integer toppable_id - integer toppable_type - string
В этих отношениях продукт питания (пицца или сандвич) не указывается напрямую, в то время как в отношениях «Многие-ко-Многим» в таблице pizza_toppings
прямо указана пицца.
В полиморфных отношениях наша еда может быть «морфирована» (преобразована) в разные типы (пицца, сандвичи и т.д.). Теперь любой новый продукт, который мы добавляем в меню, может быть toppable
, то есть иметь много топпингов.
Важно помнить, что таблицы в полиморфных отношениях могут быть «морфированными» или динамическими.
Как работает полиморфизм
Полиморфные отношения работают с использованием сводной таблицы с дополнительным полем, определяющим, на какую таблицу должен ссылаться внешний ключ.
В неполиморфных отношениях внешние ключи ссылаются на primary ID
в конкретной таблице. С другой стороны, внешний ключ в полиморфной сводной таблице может ссылаться на множество таблиц.
Пример полиморфизма
Полиморфные отношения доступны в любом языке, использующем реляционную базу данных. Я покажу вам несколько примеров кода для Laravel с использованием этих таблиц:
pizzas id - integer sandwiches id - integer toppings id - integer name - string toppables topping_id - integer toppable_id - integer toppable_type - string
Во-первых, мы создадим новую Eloquent-модель Pizza
с отношением toppings()
:
namespace App; use Illuminate\Database\Eloquent\Model; class Pizza extends Model { /** * Получаем все топпинги для этой пиццы */ public function toppings() { return $this->morphToMany('App\Topping', 'toppable'); } }
Чтобы получить топпинги для пиццы, напишем следующий код:
$pizza = App\Pizza::find(1); foreach ($pizza->toppings as $topping) { // }
Круто!
Далее, мы также можем получить обратные отношения для модели Topping
:
namespace App; use Illuminate\Database\Eloquent\Model; class Topping extends Model { /** * Получаем все пиццы, имеющие конкретный топпинг */ public function pizzas() { return $this->morphedByMany('App\Pizza', 'toppable'); } /** * Получаем все сандвичи, имеющие конкретный топпинг */ public function sandwiches() { return $this->morphedByMany('App\Sandwich', 'toppable'); } }
И тогда мы могли бы попросить все пиццы и сандвичи с определенным топпингом:
$topping = App\Topping::where('name', 'onions')->first(); // Получить все пиццы c луком, в качестве топпинга foreach ($topping->pizzas as $pizza) { // } // Получить все сендвичи c луком, в качестве топпинга foreach ($topping->sandwiches as $sandwich) { // }
Добавление полиморфных связей может сделать ваше приложение более эффективным, гибким и простым в программировании.
Заглянем глубже
Полиморфные отношения также можно разделить на три категории, которые мы рассмотрели ранее. Они могут быть «Один-к-Одному», «Один-ко-Многим» и «Многие-ко-Многим».
Пример отношения, который мы рассмотрели в этом уроке, это Полиморфные Отношения «Многие-ко-Многим».
Если вы хотите узнать больше о полиморфных отношениях, то обязательно ознакомьтесь с документацией Laravel:
- Полиморфные отношения «Один-к-Одному»
- Полиморфные отношения «Один-ко-Многим»
- Полиморфные отношения «Многие-ко-Многим»
Вывод
Надеюсь, этот урок помог вам разобраться с полиморфными отношениями.
Лучший способ изучить какую-либо концепцию — просто погрузиться в неё и начать применять. После внедрения полиморфизма в несколько проектов вы начнете понимать все больше и больше. В конце концов, концепция использования «Полиморфных отношений» будет похожа на многослойную пиццу ;)
Автор: Tony Lea
Перевод: Алексей Широков
Наш Телеграм-канал — следите за новостями о Laravel.