Laravel categories with dynamic deep paths

Laravel categories with dynamic deep paths

2,319

Today I'm telling you how to make deep categories and return data but mostly I will focus on how to get the correct path of each category in complete dynamic way, it will save you lots of time and results cleaner codes.

1. First thing first, we will need categories table so lets meke schema for it. Run command below in your terminal

php artisan make:model Category -m

2- Edit your schema file as:

        Schema::create('categories', function (Blueprint $table) {

          $table->increments('id');

          $table->string('title');

          $table->string('slug');

          $table->integer('parent_id')->unsigned()->nullable();

          $table->timestamps();

        });

        Schema::table('categories', function (Blueprint $table) {

          $table->foreign('parent_id')->references('id')->on('categories')->onUpdate('cascade')->onDelete('cascade');

        });    

 3. Edit your category model

            protected $fillable = [
                  'title', 'slug', 'parent_id'
            ];

            public function parent() {
                  return $this->belongsTo(Category::class, 'parent_id'); //get parent category
            } 
            public function children() {
                  return $this->hasMany(Category::class, 'parent_id'); //get all subs. NOT RECURSIVE
            }

 Now execute command below and create table in database

php artisan migration

 4. Categories are ready to use, for sake of saving time I'm not gonna trough process of making controller and CRUD you can do it later by yourself. For now:

4.1. Open your PhpMyAdmin (or anything you use for DB)

4.2. Add some categories

4.3 Add child category by providing id of previous categories you've made in `parent_id` column

 5. Now we have categories ready in our database it's time to return them in blade (front-end) where we will use slugs as link to each of them (getting close to main course)

5.1. Add this to your view controller

        

            use App\Category;

            public function myView(){

               $categories = Category::where('parent_id', '=', '')->with('children')->get();

               return view('myBlade', compact('categories');

           
        }

        
    

 5.2. Now add this to your view (blade)


           @foreach($categories as $category)

               {{$category->title}}

                    //getting childrens

                  @if(count($category->children) > 0 )

                          @foreach($category->children as $child)

                                 {{$child->title}}

                          @endforeach

                  @endif

           @endforeach

test it and first then back to reading this article :) 

6. Well we come to the final part of this article which is main part and the reason I made this article. "Creating laravel categories with dynamic deep paths"

6.1. First we make new folder under App foler I name it `Helpers` you can name it whatever you like.

6.2. In that folder I make 2 files name them 'CategoryRouteService.php' and 'CategoryServiceProvider.php`

6.3. Let's finish `CategoryRouteService.php` first, add code below and save the file

            namespace App\Helpers;

            use App\Category;
            use Illuminate\Database\Eloquent\Collection;

            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('category/' . $slugs[0]);
                        }
                        else {
                            $this->routes[$id] = url('category/' . 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;
                }
            }

   6.4. Add code below to `CategoryServiceProvider.php' and save the file

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

 7.  It's time to let laravel know that we are using this custom class in our app, open Config/App.php file and add this in there

App/Helpers/CategoryServiceProvider::class,

8. Open your Category model and add this:

                      public function getRouteAttribute()
                      {

                          $categoryRouteService = app(CategoryRouteService::class);

                          return $categoryRouteService->getRoute($this);

                      }

 9. Go back to your blade and add `` tag to your category titles and for `href` attribute provide `{{$categoryt->route}}`

Test it and see if your routes come nested. Works? perfect. Now lets take care of our routes.

Populating view for categories

Note: we will use one blade for all of our categories in any deep. (awesome)

 10. Open web.php add add following route

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

PS: you can change namings to whatever you like and use your own controller path etc. but don't delete `{slug}` and `->where('slug', '.+')` parts. they are important.

11. make function for your route, open your controller and make function below (nams chan be changed)

                   public function parent($slug){

                         $category = Category::where('slug', $slug)->with('children')->first();

                        return view('front.categories.single', compact('category')); //make blade anyware you wish.

                   }

Now go and actually test the link see if it returns the right blade for you.

- Last updated 4 years ago

Be the first to leave a comment.

You must login to leave a comment