简体   繁体   English

laravel中的动态类别级别

[英]dynamic category level in laravel

I have 3 level deep categories in my laravel application like 我的laravel应用程序中有3个级别的深层类别

Parent
 - Child One
  -- Child Two

When I use this nested categories in different parts such as menu, posts details etc. everything is just fine but recently I came cross an issue and I need guide to solve it. 当我在菜单,帖子详细信息等不同部分中使用此嵌套类别时,一切都很好,但是最近我遇到了一个问题,需要指导来解决。

The issue 问题

If any of my posts includes child one or child two level category it's a bit hard to provide correct route path for it, EXAMPLE: 如果我的任何帖子都包含child onechild two级别的child 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

creating this much if statement in blade to make sure we get the right route for the right categories to me doesn't seem right. 在Blade中创建大量if语句,以确保我们为我找到正确类别的正确路线似乎不正确。 I was thinking about model function which returns final route depend on category parenting level in database but I wasn't sure if it's possible or not? 我正在考虑返回最终路线的模型函数取决于数据库中类别育儿级别,但是我不确定是否可行? and if it is, how? 如果是,怎么办?

Question

  1. Is it possible I pass category routes to the categories from model? 是否可以将类别路由传递给模型中的类别?
  2. How to do that? 怎么做?

Code

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');
    }

this is how currently i'm getting my posts categories: 这是我目前获取帖子类别的方式:

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

Any idea? 任何想法?

UPDATE UPDATE

Well I solved it :-D here is the code I've made 好了,我解决了:-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;
    }

This does exactly what I needed it return slugs by orders like parent/child1/child2 这恰好满足了我的需要,它通过像parent/child1/child2这样的命令返回了parent/child1/child2

Issue 问题

the issue here is now routing this dynamic path as the result of this function I can now have any deep level of path and this needs to be dynamic in routes as well. 现在,这里的问题是路由此动态路径,此功能的结果是我现在可以具有任何深层次的路径,并且在路由中也必须是动态的。

Route 路线

my current route is like: 我当前的路线是这样的:

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

which returns this path: 返回以下路径:

site.com/category/parent

but it doesn't return: 但不会返回:

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'));
}

any idea? 任何想法?

Update 2 更新2

based on Matei Mihai answer I've made custom classes in App/Helpers folder with details below: 基于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;
    }
}

and CategoryServiceProvider.php 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();
        });
    }
}

then I registered my provider to composer.json file like: 然后我将提供者注册到composer.json文件,例如:

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

I also dumped autoloads and added this to my Category model 我还转储了自动加载并将其添加到我的Category模型中

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

then I used {{$category->route}} as my categries href attribute and I got this: 然后我使用{{$category->route}}作为categries href属性,得到了以下信息:

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

which is refers to: 这是指:

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

ideas? 想法?

It is possible, but you must take care of the script performance because it could end up having a lot of DB queries done to determine each category route. 可能,但是您必须注意脚本的性能,因为它最终可能需要完成许多数据库查询才能确定每个类别路由。

My recommendation would be to create a CategoryRouteService class which will be registered as singleton to keep the database queries as low as possible. 我的建议是创建一个CategoryRouteService类,该类将注册为单例,以使数据库查询尽可能少。 It could be something like: 可能是这样的:

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;
    }
}

Now as I said before, you need a service provider to register this service. 现在,正如我之前所说,您需要服务提供商来注册此服务。 It should look like this: 它看起来应该像这样:

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

This service provider must be added in the app configuration file. 该服务提供商必须添加到应用程序配置文件中。

The Category model could have a method which defines the route attribute: Category模型可以具有定义route属性的方法:

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

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

    /** Your relationships methods **/
}

Finally, you can use it in your blade views simply by using $category->route . 最后,只需使用$category->route即可在刀片视图中使用它。

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

Please note that there could be other solutions better than this. 请注意,可能还有其他比这更好的解决方案。 I just came across this one without thinking too much. 我只是在没有太多思考的情况下碰到了这个。 Also, the code above was not tested so please be aware that it might need some minor changes to make it work properly. 另外,上面的代码尚未经过测试,因此请注意,可能需要进行一些小的更改才能使其正常运行。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM