Я долго мучался с этим вопросом, как получить все отношения другой модели если они к тому же полиморфные, у меня стояла задача получить все комментарии заведений в категории заведений. нарыл эту статью, она решила мой вопрос.

Я долго мучался с этим вопросом, как получить все отношения другой модели если они к тому же полиморфные, у меня стояла задача получить все комментарии заведений в категории заведений. нарыл эту статью, она решила мой вопрос. Сохранил на память 🙂

Eloquent Relationships — одна из самых полезных и мощных функций Laravel Framework. Это одна из причин, почему мне больше всего нравится Laravel. В основном это помогает нам извлекать или вставлять данные очень простым и эффективным способом.

Как мы знаем, в Laravel есть несколько типов отношений. Мы (разработчики) чаще всего используем первые четыре. Это: One To OneOne To ManyOne To Many (Inverse) / Belongs ToMany To Many.

Больше всего мне нравятся Many To Many и Has Many Through. Честно говоря, мне больше всего нравятся эти два отношения. Сегодня я объясню Has Many Through на простом примере. Я надеюсь, что вы будете очень ясны после прочтения этой статьи.

Итак, давайте перейдем к нашему основному разделу .

Пусть у нас есть сценарий, в котором мы создаем элементы/меню ресторана, а элементы относятся к типу, а типы относятся к категории.

Проще говоря, у категории много типов, а у типа много элементовТеперь, если нам нужны все элементы, принадлежащие категории, нам нужно сохранить таблицу category_id элементов.  Но наши товары в основном относятся к типу. В основном Items напрямую связаны с Types.

Так что это тот случай, когда мы должны использовать Has Many Through. С помощью такого отношения мы можем получать данные через другую модель. Как и в нашем сценарии, мы можем напрямую получать элементы из категории с помощью типов.

Я пытаюсь привести очень простой пример, чтобы вы могли легко понять. Итак, начнем.

Как я упоминал ранее, мы работаем с CategoryType и 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');
 }