динамический уровень категории в Laravel - PullRequest
0 голосов
/ 01 ноября 2018

В моем приложении Laravel есть 3 уровня глубины, например

Parent
 - Child One
  -- Child Two

Когда я использую эти вложенные категории в разных частях, таких как меню, детали сообщений и т. Д., Все просто отлично, но недавно я столкнулся с проблемой, и мне нужно руководство для ее решения.

выпуск

Если какой-либо из моих постов включает в себя категорию уровня child one или 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

Создание такого большого количества 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

Есть идеи?

UPDATE

Хорошо, я решил это :-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 для категорий и получил это:

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 голосов
/ 01 ноября 2018

Это возможно, но вы должны позаботиться о производительности скрипта, потому что он может закончить выполнением большого количества запросов к БД для определения маршрута каждой категории.

Я бы порекомендовал создать класс 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

Обратите внимание, что могут быть и другие решения лучше этого. Я только столкнулся с этим, не думая слишком много. Кроме того, приведенный выше код не был протестирован, поэтому имейте в виду, что для корректной работы могут потребоваться некоторые незначительные изменения.

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