我正在学习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')
?
答案 0 :(得分:1)
这是我的建议:
关于类别模型:
public function slug() {
return $this->hasMany('App\CategorySlug', 'category_id', 'id')->latest();
}
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
您使用返回类别模型的方法制作了CategorySlug模型。
使用相同的方法,使您的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();
如您所见,您不必再显式调用该关系。
希望这会有所帮助。
祝你有美好的一天。