Что такое отношение «Один-ко-Многим»?
Оно также известное как 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->bigIncrements('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->bigIncrements('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->bigIncrements('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->bigIncrements('id');
$table->integer('brand_id')->unsigned();
$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
Перевод: Demiurge Ash