Использование parent child отношения в рамках одной таблицы

30 Октября 2022 21:11

Метод хорош в случаях, когда в приложении предусматриваются иерархии данных, например жанр->субжанр->субсубжанр и т.д или отдел->секция.

Связь будет организовываться посредством связи полей id -> parent_id. Естественно, эти поля должны быть созданыв базе.

Модель 1

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\BelongsTo;

class Tag extends Model
{
    /**
     * Получение родительского тега
     *
     * @return BelongsTo
     */
    public function parent(): BelongsTo
    {
        return $this->belongsTo(__CLASS__, 'parent_id');
    }

    /**
     * Получение дочерних тегов
     *
     * @return HasMany
     */
    public function children(): HasMany
    {
        return $this->hasMany(__CLASS__, 'parent_id', 'id') ;
    }

    /**
     * Получение неограниченной вложенности дочерних тегов
     *
     * @return HasMany
     */
    public function childrenAll(): HasMany
    {
        return $this->hasMany(__CLASS__, 'parent_id', 'id')->with('children');
    }
}

Теперь можно получать либо всех непосредственных потомков либо все дерево.

<?php

$resource = Tag::find(3);

$children = $resource->children();
$childrenAll = $resource->childrenAll();

Далее, можно сделать метод, который будет формировать древовидный массив.

public function tree(Tag $tag): array
{
    if ($tag->childrenAll) {
        $item = [
            'id' => $tag->id,
            'name' => $tag->name,
            'slug' => $tag->slug,
            'content' => $tag->content,
            'parent_id' => $tag->parent_id
        ];

        foreach ($tag->childrenAll as $value) {
            $item['children'][] = $this->tree($value);
        }

        return $item;
    }

    return [
        'id' => $tag->id,
        'name' => $tag->name,
        'slug' => $tag->slug,
        'content' => $tag->content,
        'parent_id' => $tag->parent_id
    ];
}

Использовать этот метод можно в слое ресурсов или в контроллере. Или еще где-то.

Есть еще альтернативный вариант подобной схемы.

Модель 2

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasMany;

class Tag extends Model
{
    /**
     * Получение дочерних тегов
     *
     * @return HasMany
     */
    public function children(): HasMany
    {
        return $this->hasMany(__CLASS__, 'parent_id', 'id') ;
    }

    /**
     * Получение неограниченной вложенности дочерних тегов
     *
     * @return HasMany
     */
    public function childrenAll(): HasMany
    {
        return $this->children()->with('childrenAll');
    }
}