Отношения «Многие-ко-Многим» в Laravel Eloquent

Laravel отношение Многие ко Многим

В этой статье мы рассмотрим один из наиболее продвинутых типов отношений, предлагаемых Laravel Eloquent — «Многие-ко-многим».

Предполагается, что вы уже знаете, как работают отношения «Один-к-Одному» и «Один-ко-Многим».

Что такое отношение «Многие-ко-Многим»?

Это отношение связывает запись в таблице с одной или несколькими записями в другой таблице, и наоборот.
В отличие от «Один-ко-Многим» и «Один-к-Одному», здесь ключом является сводная таблица (pivot table), которая связывает две таблицы между собой. Сводная таблица связывает id отношения из одной модели с другими моделями и наоборот.

Например, продукты и категории. В категории может быть много товаров, а товар может относиться ко многим категориям, всё просто. Чтобы лучше разобраться, как можно использовать эти отношения, давайте займемся программированием.

Установка Laravel

Для начала установим Laravel. Запустите команду в вашем терминале

composer create-project laravel/laravel ManyToMany --prefer-dist

После чего настройте учетные данные вашей базы данных в файле .env.

Создаем Модели и Миграции

Сначала создадим две модели под названием Product и Category, вместе с миграциями. Запустите команду для генерации моделей и миграций.

php artisan make:model Category -m
php artisan make:model Product -m

Как только появятся модели и миграции, обновим их:

Миграция Category

use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

class CreateCategoriesTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('categories', function (Blueprint $table) {
            $table->bigIncrements('id');
            $table->string('name');
            $table->text('description')->nullable();
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('categories');
    }
}

Модель Category

// Category.php
namespace App;

use Illuminate\Database\Eloquent\Model;

class Category extends Model
{
    protected $table = 'categories';

    protected $fillable = ['name', 'description'];
}

Миграция Product

use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

class CreateProductsTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('products', function (Blueprint $table) {
            $table->bigIncrements('id');
            $table->string('name');
            $table->float('price');
            $table->text('description')->nullable();
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('products');
    }
}

Модель Product

namespace App;

use Illuminate\Database\Eloquent\Model;

class Product extends Model
{
    protected $table = 'products';

    protected $fillable = ['name', 'price', 'description'];
}

Теперь, для переноса таблиц в базу данных, выполните следующую команду в терминале.

php artisan migrate

Фиктивные данные.
Для этого урока добавьте несколько фиктивных категорий и продуктов в свою базу данных.

Создаем сводную таблицу

Нам нужно соединить эти полученные таблицы, используя таблицу ссылок, которая называется Pivot Table (Сводная Таблица). Сводная таблица — это таблица отношений, в которой содержатся идентификаторы category_id и product_id.

Вы можете хранить гораздо больше информации в ней, но сейчас нам просто нужно создать отношение «Многие-ко-Многим». Я расскажу об использовании сводной таблицы для хранения дополнительных данных в отдельной статье.

Запустите команду для генерации миграции для сводной таблицы.

php artisan make:migration create_category_product_table

Скопируйте в полученную миграцию этот код:

use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

class CreateCategoryProductTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('category_product', function (Blueprint $table) {
            $table->id();
            $table->unsignedBigInteger('category_id');
            $table->unsignedBigInteger('product_id');

            $table->foreign('category_id')->references('id')->on('categories');
            $table->foreign('product_id')->references('id')->on('products');
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('category_product');
    }
}

В приведенной выше миграции мы добавляем в таблицу три столбца: id, category_id и product_id. Обратите внимание, что мы также устанавливаем внешние ключи на соответствующие таблицы.

Теперь выполните команду, чтобы перенести эту таблицу в базу данных.

php artisan migrate

Определение отношения «Многие-ко-Многим» в Laravel Eloquent

Настало время определить отношение «Многие-ко-Многим» между моделями Category и Product используя Laravel Eloquent.

Во-первых, мы знаем, что несколько категорий принадлежат нескольким продуктам, поэтому мы можем добавить отношение belongsToMany в модель Product. Откройте класс модели Product и добавьте туда новый метод с именем categories() показано ниже:

/**
 * @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
 */
public function categories()
{
    return $this->belongsToMany(Category::class);
}

Точно так же мы знаем, что несколько продуктов принадлежат нескольким категориям, поэтому мы можем определить отношение belongsToMany в модели Category, как показано ниже. Добавьте новый метод products() в модель Category.

/**
 * @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
 */
public function products()
{
    return $this->belongsToMany(Product::class);
}

Создание отношения «Многие-ко-Многим»

Теперь давайте создадим несколько категорий и продуктов, и отношения «Многие-ко-Многим» между ними. Во-первых, мы создадим несколько категорий, как показано ниже (способ добавления продуктов и категорий полностью зависит от вас).

use App\Category;

$categories = ['Office Chairs', 'Modern Chairs', 'Home Chairs'];

foreach($categories as $category)
{
    Category::create([
        'name'  =>  $category,
    ]);
}

У вас появилось несколько категорий, как это видно по вашей таблице categories.

Отношение «Многие-ко-Многим»

Теперь мы создадим товар и прикрепим его к нескольким категориям.

$product = Product::create([
    'name'  =>  'Home Brixton Faux Leather Armchair',
    'price' =>  199.99,
]);

$categories = Category::find([2,3]); // Modren Chairs, Home Chairs
$product->categories()->attach($categories);

Этот код создаст продукт, а затем присоединит его к нескольким категориям.

Отношение «Многие-ко-Многим»

В таблице category_product вы можете увидеть, что product_id 1 имеет несколько записей с category_id 2 и 3.

Отношение «Многие-ко-Многим»

Таким образом создается отношение «Многие-ко-Многим» в Laravel Eloquent. Очевидно, что вы можете сделать также и обратное отноешение и прикрепить категорию к нескольким продуктам.

Получение данных «Многие-ко-Многим»

После того как установлено отношение и создано несколько записей, можно использовать связанные записи для конкретной модели. В нашем примере мы можем использовать метод categories, чтобы получить связанные с продуктом категории, и, аналогичным образом, мы можем использовать products для получения всех продуктов в данной категории.

$category = Category::find(1);
$category->products; // вернет все продукты для категории 1

$product = Product::find(1);
$product->categories; // вернет все категории для продукта 1

Поскольку отношение«Многие-ко-Многим» всегда возвращает коллекцию (collection), вы можете использовать цикл для ее показа.

Удаление отношения «Многие-ко-Многим»

Как мы используем метод attach(), чтобы соединить модели, так же мы можем использовать detach(), чтобы удалить отношение между моделями.

$category = Category::find(2);
$product = Product::find(1);

$product->categories()->detach($category);

Посмотрите в таблицу category_product и вы увидите, что запись category_id 2 была удалена.

Отношение «Многие-ко-Многим»

Заключение

В этом уроке мы рассмотрели, как можно использовать отношение «Многие-ко-Многим» в Laravel Eloquent. Я рекомендую вам потратить время на ознакомление с официальной документации Laravel.

Автор: Larashout
Перевод: Алексей Широков

Наш Телеграм-канал — следите за новостями о Laravel.