0
votes

I have product catalog with 4 subcategory levels

routes/web.php

Route::get('/products/{level2?}/{level3?}/{level4?}',   'ProductController@showIndex');
Route::get('/products/{level2?}/{level2prod?}/{level3?}/{level3prod?}/{level4?}/{level4prod?}',     'ProductController@showProduct');

First route is showing categories with corresponding products, which are children to current category. Between Product and Category models there is ManytoMany relationship $this->belongsToMany. And I have category_product pivot table.

showProduct function in ProductController receives $request and I extract last 2 segments out of $request->path(). Last path segment is equal to current product sef - this is the most important info to load product page. Previous path segment is equal to parent category sef. This is needed to know under which category we are located now. Because one product could belong to many categories and we don't know how current product page was loaded. In this case I use $category to load breadcrumbs like that

Home > Products > Category level 2 > Category level3 > Category level4
    public function showProduct(Request $request) {
        $parent = '';
        $categorysef = '';
        // we need only last part of uri after last slash
        if ( strrpos($request->path(), '/') !== false ) {
            $sef = substr($request->path(), strrpos($request->path(), '/') + 1);
            $parent = substr($request->path(), 0, strrpos($request->path(), '/') );
            if ( strrpos($parent, '/') !== false ) {
                $categorysef = substr($parent, strrpos($parent, '/') + 1);
            }
            else {$categorysef = $parent;}
        } else {
            $sef = $request->path();
        }

        $item     = Product::whereSef($sef)->firstorfail();
        $previous = Product::where('id', '<', $item->id)->orderBy('id', 'desc')->first();
        $next     = Product::where('id', '>', $item->id)->orderBy('id', 'asc')->first();
        $category = Category::whereSef($categorysef)->firstorfail();

        return view('product.item')
                    ->withItem($item)
                    ->withPrevious($previous)
                    ->withNext($next)
                    ->withCategory($category);
    }

One thing I cannot understand is how to filter $previous and $next to contain only values belongsTo current category.

1
You can't chain multiple optional URL parameters... This is invalid: {level2?}/{level2prod?}/{level3?}/{level3prod?}/{level4?}/{level4prod?}. You'd physically have to put null in the URL to achieve this, like /null/null/example/level3, otherwise /example/level3 would, to you, map to level3, but the the program would map to level2.Tim Lewis
This is not the point of my question, but anyway if this invalid then why it works?schel4ok
That's why I posted it as a comment and not an answer. It's invalid in the sense that as soon as you change the order of params/omit a param, you'll get unexpected results. If you try to navigate to a URL that omits level2, and has 2 params, one for level3 and level4, you'll navigate to a route that is handled by level2 and level3.Tim Lewis
I agree, but the only case how you can navigate to route like you suggest is if you manually delete level2 from URL. Because my category logic and routes do not allow to omit parent category. My Category model based on Nestedset. Every node has parent and child relations and it is very comfortable for catalog trees. Finally I tested my catalog. It works well.schel4ok
Actually today I found more elegant way for unlimited routes in product catalog here stackoverflow.com/questions/31407827/…schel4ok

1 Answers

0
votes

Solution found!

@if ( !empty($previous) and in_array( $category->id, $previous->category->pluck('id')->toArray()) )
   <li><a href="{{ $previous->sef }}">← Prev</a></li>
@endif

Same code for next link.