获取相关数据

时间:2019-05-09 17:40:08

标签: laravel eloquent

我正在学习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属性等于null。所有children属性都可以正常加载。

我将源放入GitHub存储库中。除其他外,还有种子文件。

更新: 感谢@ 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 个答案:

答案 0 :(得分:1)

仅返回所有类别树(根目录和子目录树)的最后一个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 /雄辩的开发人员通常不知道的事情。

基本上,您正在创建一个无限的渴望加载。让我向您解释:

这是您的类别模型及其关系:

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

这是您的CategorySlug模型及其关系:

CategorySlug => Category
  1. 您使用返回类别模型的方法制作了CategorySlug模型。

  2. 使用相同的方法,使您的Category关系与关联的子弹一起返回。

实际上,由于在CategorySlug模型上使用了WITH定义,因此您只是在检索相同的CategorySlug模型。

这没有意义,而且还会产生无限的渴望加载。示例说明:

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

这里会发生什么:

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

这是一个无限的渴望加载。这将导致数据库超时异常。

为避免这种情况,只需删除CategorySlug模型上的with

按照我的观点,这是没有必要的。

让我知道您的需求是否发生变化,并且您确实需要这种反向关系。

技巧和额外知识

  • 最早和最新的方法

有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();
  • 总是渴望加载您的人际关系

这是一个技巧,也许您可​​以在项目中使用。

基本上,您可以雄辩地加载关系,而无需显式调用它。

关于类别模型-

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();

如您所见,您不必再显式调用该关系。

希望这会有所帮助。

祝你有美好的一天。