Получить связанные данные - PullRequest
0 голосов
/ 09 мая 2019

Я изучаю Laravel, очень начальный уровень. Итак, идея заключается в реализации slug URL. Есть модель Category. Модель может иметь бесконечное количество слизней. Если я запрашиваю последний, я получаю соответствующую страницу. Если я запрашиваю другой слаг (устаревший), я получаю перенаправление 301 на последний.

Категория - модель иерархии родитель-потомок. Мне нужна связь один-к-одному с CategorySlug моделью, потому что я не забочусь обо всех предыдущих значениях слага.

Миграция:

class CreateCategoriesTable extends Migration
{
    public function up()
    {
        Schema::create('categories', function (Blueprint $table) {
            $table->bigIncrements('id');
            $table->string('title');
        });

        Schema::table('categories', function (Blueprint $table) {
            $table->unsignedBigInteger('parent_id')->nullable()->after('id');;
            $table->foreign('parent_id')->references('id')->on('categories')->onUpdate('cascade')->onDelete('cascade');
        });
    }
}
class CreateCategoriesSlugTable extends Migration
{
    public function up()
    {
        Schema::create('categories_slug', function (Blueprint $table) {
            $table->bigIncrements('id');
            $table->string('value')->unique()->index()->nullable(false);
            $table->timestamp('created_at')->useCurrent();
        });

        Schema::table('categories_slug', function (Blueprint $table) {
            $table->unsignedBigInteger('category_id')->nullable()->after('id');
            $table->foreign('category_id')->references('id')->on('categories')->onUpdate('cascade')->onDelete('cascade');
        });
    }
}

Модель:

class Category extends Model
{
    public function children() {
        return $this->hasMany('App\Category', 'parent_id', 'id')->
            orderBy('order')->
            with('children');
    }

    public function slug() {
        return $this->hasOne('App\CategorySlug', 'category_id', 'id')->
            orderBy('created_at', 'desc')->limit(1);
    }
}
class CategorySlug extends Model
{
    /**
     * @return App\Category
     */
    public function category() {
        return $this->belongsTo('App\Category', 'category_id', 'id')->with('slug');
    }

}

Когда я запускаю следующий код в контроллере, я ожидаю, что каждый Category будет заполнен атрибутом slug.

$categories = Category::where('parent_id', null)->with('children', 'slug')->get();

Но на самом деле, только у первого есть правильный slug. У других атрибут slug равен нулю. Все атрибуты children загружены нормально.

Я поставил источник в GitHub хранилище. Среди прочего, есть файл seed.

Обновление: Благодаря @ marlon-ferreira я внес некоторые изменения:

class Category extends Model
{
    ...

    public function slug() {
        return $this->hasOne('App\CategorySlug', 'category_id', 'id')->latest();
    }
}

Теперь все работает отлично! Я предпочитаю использовать hasOne, потому что я не люблю писать $category->slug[0]->value вместо $category->slug->value. Это нормально?

Почему я не могу заменить latest() на limit(1) + orderBy('created_at')?

1 Ответ

1 голос
/ 09 мая 2019

Возвращает только последнюю категорию SlugCategory для всего дерева категорий (корневая и детская)

Это мое предложение:

В категории Модель:

public function slug() {
    return $this->hasMany('App\CategorySlug', 'category_id', 'id')->latest();
}
  1. Вы устанавливаете отношение слагов как оно есть: один ко многим (категория имеет много слагов);
  2. Вы вызываете метод latest(), чтобы предоставить вам только последний слаг.

При таком подходе при вызове отношения slug() он будет извлекать только самую последнюю запись CategorySlug.

В категории Модель:

public function children() {
    return $this->hasMany('App\Category', 'parent_id', 'id')->
            orderBy('order')->
            with('children', 'slug');
}

Итак, этот вызов контроллера ...

$categories = Category::where('parent_id', null)->with('children', 'slug')->get();

... будет возвращать только последний слаг для основной категории (parent_id = null), а также для всех их детей.

ПРЕДУПРЕЖДЕНИЕ - бесконечная энергичная загрузка

После того, как я предоставил вам свое решение, я хотел бы поговорить о чем-то, что, как правило, многие разработчики Laravel / Eloquent не знают.

По сути, вы создаете бесконечную нетерпеливую загрузку. Позвольте мне объяснить вам:

Это модель вашей категории и их отношения:

Categories => Categories (children)
           => CategorySlug (slug)

А это ваша модель CategorySlug и их отношения:

CategorySlug => Category
  1. Вы создали модель CategorySlug с методом, который возвращает модель Category.

  2. В этом же методе вы вернули отношение категории с соответствующим слагом.

Из-за этого определения WITH в вашей модели CategorySlug фактически вы просто восстанавливаете ту же модель CategorySlug.

Это не имеет смысла, а также создает бесконечную нагрузку. Пример объяснения:

Category::find(1)->slug();

Что здесь произойдет:

category will load slug -> slug will load category -> category will load slug -> ...

Это бесконечная энергичная загрузка. Это приведет к исключению тайм-аута базы данных.

Чтобы избежать этого, просто удалите with на своей модели CategorySlug.

Это не обязательно, с моей точки зрения.

Дайте мне знать, если ваши требования изменятся, и вам действительно нужны эти обратные отношения.

Трюки и дополнительные знания

  • Старейшие и новейшие методы

Существует 2 метода, которые позволяют вам извлечь только самую старую или самую последнюю запись. Пример: * * тысяча семьдесят пять

$categories = Category::where('is_active', true); // we have many categories here

$last_created_category = $categories->latest(); // latest category created

$old_updated_category = $categories->oldest('updated_at'); // oldest category updated

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

По умолчанию created_at.

  • Использование стратегии области действия

Области являются отличным способом создания настраиваемой многократно используемой реализации.

Предполагая реализацию вашего контроллера, мы могли бы применить область действия в следующем случае:

В категории Модель -

class Category extends Model
{
    ...

    public function scopeWithoutParent($query) {
        return $query->whereNull('parent_id');
    }
}

На контроллере -

$categories = Category::withoutParent()->with('children', 'slug')->get();
  • Всегда стремимся загрузить ваши отношения

Это трюк, который, возможно, вы могли бы использовать в своем проекте.

По сути, вы можете заставить eloquent загружать отношения без явного вызова.

В категории Модель -

class Category extends Model
{
    ...

    protected $with = [ 'children', 'slug' ];

    public function children() {
        return $this->hasMany('App\Category', 'parent_id', 'id')->orderBy('order');
    }

    public function slug() {
        return $this->hasMany('App\CategorySlug', 'category_id', 'id')->latest();
    }

На контроллере -

$categories = Category::withoutParent()->get();

Как видите, вам больше не нужно явно называть отношения.

Надеюсь, это будет полезно.

Хорошего дня.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...