В этой статье мы рассмотрим один из наиболее продвинутых типов отношений, предлагаемых Laravel Eloquent — «Многие-ко-многим».
Предполагается, что вы уже знаете, как работают отношения «Один-к-Одному» и «Один-ко-Многим».
Это отношение связывает запись в таблице с одной или несколькими записями в другой таблице, и наоборот.
В отличие от «Один-ко-Многим» и «Один-к-Одному», здесь ключом является сводная таблица (pivot table), которая связывает две таблицы между собой. Сводная таблица связывает id отношения из одной модели с другими моделями и наоборот.
Например, продукты и категории. В категории может быть много товаров, а товар может относиться ко многим категориям, всё просто. Чтобы лучше разобраться, как можно использовать эти отношения, давайте займемся программированием.
Для начала установим 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->engine = 'MyISAM';
$table->bigIncrements('id');
$table->integer('category_id')->unsigned();
$table->integer('product_id')->unsigned();
$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
Настало время определить отношение «Многие-ко-Многим» между моделями 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,
]);
Этот код создаст продукт, а затем присоединит его к нескольким категориям.
В таблице 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
Перевод: Demiurge Ash