В этой статье мы рассмотрим на конкретном примере, как можно создать отношение «Один-ко-Многим» в Laravel.
Что такое отношение «Один-ко-Многим»?
Оно также известное как hasMany и связывает одну запись в таблице со множеством других записей в других таблицах базы данных. Это наиболее используемый тип отношений.
Например, на коммерческом сайте модель Brand принадлежит несколько записей модели Product, а модель Product принадлежит модели Brand. Эта взаимосвязь изображена на диаграмме ниже
Отношение «Один-ко-Многим» очень простое, как и отношение «Один-к-Одному», о которых мы рассказывали в статье «Отношение «Один-к-Одному» в Laravel Eloquent«. Чтобы разобраться с «Один-ко-Многим» и научиться его использовать, давайте рассмотрим очень простой пример.
Создание Моделей и Миграций
Для реализации отношения нам понадобятся две таблицы в нашей базе данных brands и products. Создадим модель и миграцию, а затем определим «Один-ко-Многим». Для генерации моделей и миграций запустите две команды в терминале
php artisan make:model Brand -m php artisan make:model Product -m
Выше приведенная команда сгенерирует в папке app две модели под названиями Brand и Product. Вы также найдете две новые миграции в папке database/migrations. Свежесозданные модели и миграция будут выглядеть следующим образом:
// Brand Model namespace App; use Illuminate\Database\Eloquent\Model; class Brand extends Model { // }
Миграция Brand:
// Brand Migration use Illuminate\Support\Facades\Schema; use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Migrations\Migration; class CreateBrandsTable extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::create('brands', function (Blueprint $table) { $table->id(); $table->timestamps(); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::dropIfExists('brands'); } }
Модель Product:
// Product Model namespace App; use Illuminate\Database\Eloquent\Model; class Product extends Model { // }
Миграция Product:
// Product Migration 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->id(); $table->timestamps(); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::dropIfExists('products'); } }
После того, как вы сгенерировали модели и миграции, обновите метод up() в обоих миграциях.
Добавляем несколько полей для таблицы бренда.
/** * Run the migrations. * * @return void */ public function up() { Schema::create('brands', function (Blueprint $table) { $table->id(); $table->string('name'); $table->text('description'); $table->timestamps(); }); }
И добавляем несколько полей для таблицы продуктов. Обратите внимание на столбец brand_id, являющимся внешним ключом к идентификатору brands.
/** * Run the migrations. * * @return void */ public function up() { Schema::create('products', function (Blueprint $table) { $table->id(); $table->unsignedBigInteger('brand_id'); $table->foreign('brand_id') ->references('id')->on('brands') ->onDelete('cascade'); $table->string('name'); $table->string('slug'); $table->double('price'); $table->integer('qty'); $table->text('description'); $table->timestamps(); }); }
Теперь выполните команду ниже, для того чтобы создать таблицы brands и products в вашей базе данных:
php artisan migrate
Определение отношения «Один-ко-Многим»
Теперь у нас есть таблицы брендов и продуктов, давайте определим отношение «Один-ко-Многим» в моделях.
Откройте файл модели app/Brand.php и добавьте новую функцию с именем products(), которая будет возвращать отношение hasMany, как показано ниже:
// Brand Model namespace App; use Illuminate\Database\Eloquent\Model; class Brand extends Model { public function products() { return $this->hasMany(Product::class); } }
Примечание. Мы возвращаем отношение «Один-ко-Многим» для модели бренда, которая вернёт более одной записи модели продукта, поэтому мы назвали ее во множественном числе: products. Позже, когда мы определим обратное отношение «Один-ко-Многим» в модели продукта, то будем использовать название в единственном числе: brand. У каждого продукта будет только один бренд, поэтому имя в единственном числе.
Как видите, в методе products() мы определили отношение hasMany для модели Product .
Определение обратного отношения «Один-ко-Многим»
Поскольку мы определили отношение hasMany для модели Brand, которое вернет связанные записи модели Product, то мы можем определить обратную зависимость для модели Product.
Откройте файл модели app/Product.php и добавьте в него новый метод brand(), возвращающий бренд, который связан с этим продуктом.
После добавления нового метода brand() обновите его с помощью приведенного ниже фрагмента кода.
// Product Model namespace App; use Illuminate\Database\Eloquent\Model; class Product extends Model { public function brand() { return $this->belongsTo(Brand::class); } }
Как видите, в это методе мы возвращаем отношение belongsTo, которое вернут соответствующий продукту бренд.
Получение данных «Один-ко-Многим»
После того, как определено отношения в моделях, мы можем получать связанные записи. В нашем примере, для конкретного бренда мы можем получить все связанные с ним продукты, используя метод products().
$brand = Brand::find(1); $products = $brand->products;
Когда мы запрашиваем продукты для бренда, то Laravel забирает из таблицы продуктов все записи, которые в поле brand_id содержать id запрошенного бренда.
Точно так же мы можем получить обратную связь.
$product = Product::find(1); $brand = $product->brand; dd($brand->name);
Для обратного отношения «Один-ко-Многим», Laravel забирает из таблицу брендов запись, у которой поле идентификатора равно столбцу brand_id продукта.
Создание отношения «Один-ко-Многим»
Для того чтобы создать отношения между двумя моделями или связать их, мы можем сначала создать дочерний объект, а затем сохранить его, используя родительский объект.
$brand = Brand::find(1); $product = new Product(); $product->name = 'iPhone X'; $product->slug = 'iphone-x'; $product->price = '899.99'; $product->qty = 10; $product->description = 'Some description'; // Сохраняем связанную модель $brand->products()->save($product);
Вы также можете использовать метод saveMany() вместо save() для сохранения нескольких связанных моделей.
Фильтрация отношения «Один-ко-Многим»
Возможно, вы захотите отфильтровать связанные записи и отсортировать их при получении. Вы можете объединить методы в своем поисковом запросе.
$brand = Brand::find(1); $products = $brand->products() ->where('price', '>', '500') ->orderBy('name', 'asc') ->get();
В выше приведенном запросе мы получаем все продукты для бренда, причем цена каждого продукта должна быть больше 500, и сортируем записи по имени в порядке возрастания.
Удаление отношения «Один-ко-Многим»
Процесс удаления отношения такое же, как и их создание. Сначала мы получаем родительский объект — Бренд, а затем используем метод products() для удаления всех продуктов. Чтобы удалить все продукты для конкретного бренда, используйте приведенный ниже пример:
$brand = Brand::find(1); $brand->products()->delete();
Заключение
Я старался как мог, делая эту статью насколько возможно простой, так как отношения в Laravel — самая неверно понимаемая тема. Рекомендую вам потратить немного времени на чтение официальной документации Laravel.
Автор: Larashout
Перевод: Алексей Широков
Наш Телеграм-канал — следите за новостями о Laravel.