В этой статье мы рассмотрим один из наиболее продвинутых типов отношений, предлагаемых 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.