laravel中的动态类别级别

时间:2018-11-01 09:05:47

标签: php laravel

我的laravel应用程序中有3个级别的深层类别,例如

Parent
 - Child One
  -- Child Two

当我在菜单,帖子详细信息等不同部分中使用此嵌套类别时,一切都很好,但是最近我遇到了一个问题,需要指导来解决。

问题

如果我的任何帖子都包含child onechild two级别类别,则很难为其提供正确的路线,例如:

Parent route :  site.com/p/slug
Child one route: site.com/parent_slug/childOne_slug
Child two route: site.com/parent_slug/childOne_slug/childTwo_slug

在Blade中创建太多if语句来确保我们为正确的类别找到正确的路线似乎不正确。我正在考虑返回最终路线的模型函数取决于数据库中类别育儿级别,但是我不确定是否可行?如果是的话,怎么办?

问题

  1. 是否可以将类别路由传递给模型中的类别?
  2. 该怎么做?

代码

Category model

protected $fillable = [
        'title', 'slug', 'thumbnail', 'publish','mainpage', 'parent_id', 'color', 'template'
    ];

    public function posts(){
        return $this->belongsToMany(Post::class, 'post_categories');
    }

    public function parent() {
        return $this->belongsTo(Category::class, 'parent_id');
    }

    public function children() {
        return $this->hasMany(Category::class, 'parent_id');
    }

这是我目前获取帖子类别的方式:

@foreach($post->categories as $category)
  <a class="post-cat" href="#">{{$category->title}}</a>
@endforeach

有什么主意吗?

更新

我解决了它:-D这是我编写的代码

public function getCatSlug(){

        if($this->parent_id != ''){ //child one

            if($this->parent->parent_id != ''){ //child two

                if($this->parent->parent->parent_id != ''){ //child three
                    return $this->parent->parent->parent->slug. '/'. $this->parent->parent->slug. '/'. $this->parent->slug. '/'. $this->slug;
                }

                return $this->parent->parent->slug. '/'. $this->parent->slug. '/'. $this->slug;
            }

            return $this->parent->slug. '/'. $this->slug;
        }

        return $this->slug;
    }

这正是我所需要的,它按parent/child1/child2

之类的命令返回段塞

问题

由于此功能的结果,这里的问题是现在正在路由此动态路径,我现在可以具有任何深层次的路径,并且在路由中也必须是动态的。

路线

我当前的路线是:

Route::get('/category/{slug}', 'Front\CategoryController@parent')->name('categoryparent');

返回此路径:

site.com/category/parent

但它不会返回:

site.com/category/parent/child_one
site.com/category/parent/child_one/child_two

Controller

public function parent($slug){
  $category = Category::where('slug', $slug)->with('children')->first();
  $category->addView();
  $posts = $category->posts()->paginate(8);
  return view('front.categories.single', compact('category','posts'));
}

有什么主意吗?

更新2

基于Matei Mihai的答案我已经在App/Helpers文件夹中创建了自定义类,详细信息如下:

CategoryRouteService.php

<?php

namespace App\Helpers;

use App\Category;

class CategoryRouteService
{
    private $routes = [];

    public function __construct()
    {
        $this->determineCategoriesRoutes();
    }

    public function getRoute(Category $category)
    {
        return $this->routes[$category->id];
    }

    private function determineCategoriesRoutes()
    {
        $categories = Category::all()->keyBy('id');

        foreach ($categories as $id => $category) {
            $slugs = $this->determineCategorySlugs($category, $categories);

            if (count($slugs) === 1) {
                $this->routes[$id] = url('p/' . $slugs[0]);
            }
            else {
                $this->routes[$id] = url('/' . implode('/', $slugs));
            }
        }
    }

    private function determineCategorySlugs(Category $category, Collection $categories, array $slugs = [])
    {
        array_unshift($slugs, $category->slug);

        if (!is_null($category->parent_id)) {
            $slugs = $this->determineCategorySlugs($categories[$category->parent_id], $categories, $slugs);
        }

        return $slugs;
    }
}

CategoryServiceProvider.php

<?php

namespace App\Helpers;

use App\Helpers\CategoryRouteService;

class CategoryServiceProvider
{
    public function register()
    {
        $this->app->singleton(CategoryRouteService::class, function ($app) {
            // At this point the categories routes will be determined.
            // It happens only one time even if you call the service multiple times through the container.
            return new CategoryRouteService();
        });
    }
}

然后我将提供者注册到composer.json文件中,例如:

"autoload": {
        "classmap": [
            "database/seeds",
            "database/factories"
        ],
        "psr-4": {
            "App\\": "app/"
        },
        "files": [
            "app/helpers/CategoryServiceProvider.php" //added here
        ]
    },

我还转储了自动加载功能并将其添加到我的Category模型中

use App\Helpers\CategoryRouteService;
public function getRouteAttribute()
{
  $categoryRouteService = app(CategoryRouteService::class);
  return $categoryRouteService->getRoute($this);
}

然后我将{{$category->route}}用作我的href href属性,我得到了:

Argument 2 passed to App\Helpers\CategoryRouteService::determineCategorySlugs() must be an instance of App\Helpers\Collection, instance of Illuminate\Database\Eloquent\Collection given

是指:

private function determineCategorySlugs(Category $category, Collection $categories, array $slugs = [])
    {

想法?

1 个答案:

答案 0 :(得分:0)

这是可能的,但是您必须注意脚本的性能,因为它最终可能会完成许多数据库查询以确定每个类别路由。

我的建议是创建一个CategoryRouteService类,该类将注册为单例,以使数据库查询尽可能少。可能是这样的:

class CategoryRouteService
{
    private $routes = [];

    public function __construct()
    {
        $this->determineCategoriesRoutes();
    }

    public function getRoute(Category $category)
    {
        return $this->routes[$category->id];
    }

    private function determineCategoriesRoutes()
    {
        $categories = Category::all()->keyBy('id');

        foreach ($categories as $id => $category) {
            $slugs = $this->determineCategorySlugs($category, $categories);

            if (count($slugs) === 1) {
                $this->routes[$id] = url('/p/' . $slugs[0]);
            }
            else {
                $this->routes[$id] = url('/' . implode('/', $slugs));
            }
        }
    }

    private function determineCategorySlugs(Category $category, Collection $categories, array $slugs = [])
    {
        array_unshift($slugs, $category->slug);

        if (!is_null($category->parent_id)) {
            $slugs = $this->determineCategorySlugs($categories[$category->parent_id], $categories, $slugs);
        }

        return $slugs;
    }
}

现在,正如我之前所说,您需要服务提供商来注册此服务。它应该看起来像这样:

class CategoryServiceProvider
{
    public function register()
    {
        $this->app->singleton(CategoryRouteService::class, function ($app) {
            // At this point the categories routes will be determined.
            // It happens only one time even if you call the service multiple times through the container.
            return new CategoryRouteService();
        });
    }
}

此服务提供商必须添加到应用程序配置文件中。

Category模型可以具有定义route属性的方法:

class Category extends Model
{
    public function getRouteAttribute()
    {
        $categoryRouteService = app(CategoryRouteService::class);

        return $categoryRouteService->getRoute($this);
    }

    /** Your relationships methods **/
}

最后,只需使用$category->route就可以在刀片视图中使用它。

@foreach($post->categories as $category)
  <a class="post-cat" href="{{$category->route}}">{{$category->title}}</a>
@endforeach

请注意,可能还有其他解决方案。我只是在没有太多思考的情况下碰到了这个。另外,上面的代码尚未经过测试,因此请注意,可能需要进行一些小的更改才能使其正常工作。