Я долго мучался с этим вопросом, как получить все отношения другой модели если они к тому же полиморфные, у меня стояла задача получить все комментарии заведений в категории заведений. нарыл эту статью, она решила мой вопрос. Сохранил на память 🙂
Eloquent Relationships — одна из самых полезных и мощных функций Laravel Framework. Это одна из причин, почему мне больше всего нравится Laravel. В основном это помогает нам извлекать или вставлять данные очень простым и эффективным способом.
Как мы знаем, в Laravel есть несколько типов отношений. Мы (разработчики) чаще всего используем первые четыре. Это: One To One, One To Many, One To Many (Inverse) / Belongs To, Many To Many.
Больше всего мне нравятся Many To Many и Has Many Through. Честно говоря, мне больше всего нравятся эти два отношения. Сегодня я объясню Has Many Through на простом примере. Я надеюсь, что вы будете очень ясны после прочтения этой статьи.
Итак, давайте перейдем к нашему основному разделу .
Пусть у нас есть сценарий, в котором мы создаем элементы/меню ресторана, а элементы относятся к типу, а типы относятся к категории.
Проще говоря, у категории много типов, а у типа много элементов. Теперь, если нам нужны все элементы, принадлежащие категории, нам нужно сохранить таблицу category_id
элементов. Но наши товары в основном относятся к типу. В основном Items напрямую связаны с Types.
Так что это тот случай, когда мы должны использовать Has Many Through. С помощью такого отношения мы можем получать данные через другую модель. Как и в нашем сценарии, мы можем напрямую получать элементы из категории с помощью типов.
Я пытаюсь привести очень простой пример, чтобы вы могли легко понять. Итак, начнем.
Как я упоминал ранее, мы работаем с Category, Type и Item.
Поэтому я делаю свою миграцию очень простой.
Вот моя миграция для Category. Давайте посмотрим.
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateCategoriesTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('categories', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('categories');
}
}
Как видите, я оставил здесь только название категории.
Давайте посмотрим на мою модель категорий.
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Category extends Model
{
use HasFactory;
/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = [
'name',
];
}
Вот моя миграция типов. Давайте посмотрим.
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateTypesTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('types', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->unsignedInteger('category_id');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('types');
}
}
Я просто сохранил category_id
как внешний ключ. Таким образом, Категория связана с Типом.
Вот модель типа.
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Type extends Model
{
use HasFactory;
/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = [
'name', 'category_id'
];
}
Вам может показаться, что это очень просто.
Давайте перейдем к моей миграции предметов.
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateItemsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('items', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->longText('description');
$table->unsignedInteger('type_id');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('items');
}
}
Итак, здесь вы можете видеть, что я просто сохранил type_id
внешний ключ. Итак, Type связан с Item . Я ничего не сохранял category_id
в Items Migration . Вот главный факт, который вы должны понимать, что Item не связан напрямую с Category. Элемент связан с категорией через тип. Вот почему это называется « Имеет много через отношения».
Теперь вот модель для Item.
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Item extends Model
{
use HasFactory;
/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = [
'name', 'description', 'type_id'
];
}
Теперь мы построим отношение Has Many Through от категории к элементу.
Давайте создадим это отношение к модели категорий.
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Category extends Model
{
use HasFactory;
/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = [
'name',
];
/**
* Get all of the items for the user.
*/
public function items()
{
return $this->hasManyThrough(Item::class, Type::class);
}
}
Вот items
отношение, которое будет получать все элементы, принадлежащие категории, по типу.
Вы также можете использовать такое отношение — ЭТО ТО ЧТО НУЖНО ПРИ ПОЛИМОРФЕ:
return $this->hasManyThrough(
Item::class,
Type::class,
'category_id', // Foreign key on the types table...
'type_id', // Foreign key on the items table...(commentable_id)
'id', // Local key on the users table...
'id' // Local key on the categories table...
);
Наши отношения Has Many Through успешно состыковались.
Я хочу предложить вам еще один сценарий, в котором вы можете использовать эти отношения для лучшего понимания.
Сценарий
Предположим, у нас есть три модели.
Команда (id, user_id, имя)
Пользователь (id, имя)
Цель (id, user_id, кол-во целей)
Итак, отношения такие.
Команда hasMany User ( team_id
внутри модели пользователя )
Пользователь имеет много целей ( user_id
внутри модели целей )
Видите ли, из отношений мы видим, что здесь модель пользователя является промежуточной моделью.
Мы не можем хранить goal_id
непосредственно в таблице Team, потому что мы уже храним goal_id
в таблице User.
Итак, теперь модель пользователя находится в отношениях с моделью команды. Итак, внутри модели User есть team_id
.
Наконец, если нам нужно получить доступ к тому, сколько целей создала команда, мы можем использовать модель пользователя.
Если этот тип сценария создается для вашего варианта использования, тогда и тогда вам необходимо определить отношение Has Many Through.
Вот мое решение, надеюсь теперь все стало понятно.
public function comments(): HasManyThrough
{
return $this->hasManyThrough('App\Models\Commentary', 'App\Models\Place', 'company_id', 'commentable_id', 'id', 'id'
)->where('commentable_type', 'App\Models\Place');
}