Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

keroherox30's avatar

How to throw a 404 if ID or Slug in URL doesnt match

Hello

Check ScreenShots

1- Normal - > ( https://i.ibb.co/mHvQYzd/1-img.jpg )

2- Extra letter with slug, get same result Not Normal -> ( https://i.ibb.co/g4LGtLk/2-img.jpg )

3- After Change id from 1 to 2 got this error - > ( https://i.ibb.co/hFrKTtj/3-img.jpg ) URL -> [ category/products/2/laravel]

Route

Route::get('category/products/{id}/{slug}', 'categoryProducts')->name('category.products');

Controller

 public function categoryProducts(Request $request, $id)
    {

        $products      = Product::available();
        $categoryId    = 0;
        $subcategoryId = 0;

        if ($request->route()->getName() == 'category.products') {
            $categoryId    = $id;
            $category      = Category::active()->with('subcategories')->find($id);
            $subcategories = $category->subcategories;
            $pageTitle     = $category->name . '- Products';

            $products = $products->where('category_id', $categoryId);
        }

        $cloneProducts = clone $products;

        $minPrice     = $cloneProducts->min('regular_price') ?? 0;
        $maxPrice     = $cloneProducts->max('regular_price') ?? 0;
        $products     = $products->with('category', 'user')->latest()->paginate(getPaginate());
        $totalProduct = $cloneProducts->count();
        $tags         = $this->getTags($cloneProducts->pluck('tag'));
        return view($this->activeTemplate . 'product.all', compact('pageTitle', 'products', 'subcategories', 'minPrice', 'maxPrice', 'tags', 'categoryId', 'subcategoryId', 'totalProduct'));
    }
0 likes
18 replies
tykus's avatar

Extra letter with slug, get same result Not Normal

If you want the slug to be significant, then have the controller action accept it, and scope you query with it.

After Change id from 1 to 2 got this error

Because the find($id) returns null whenever $id is 2.

You can solve both of these issues by modifying the query like this

public function categoryProducts(Request $request, $id, $slug)
{
    // ...
    $category = Category::active()->with('subcategories')->where('slug', $slug)->findOrFail($id);
    // ...
1 like
keroherox30's avatar

@tykus

can help me for Showing slug instead of id ?

because i try to remove it and after remove and using Slug only

the items not display without id

tykus's avatar

@keroherox30 the Route needs to be modified:

Route::get('category/products/{slug}', 'categoryProducts')->name('category.products');

The controller action should expect only the $slug, and the query modified to scope only by the given $slug

public function categoryProducts(Request $request, $slug)
{
    // ...
    $category = Category::active()->with('subcategories')->where('slug', $slug)->firstOrFail();
    // ...
keroherox30's avatar

@tykus

HI

the problem from this 2 codes (Undefined variable $id)

$categoryId    = $id;
$products = $products->where('category_id', $categoryId);

this for display the products by item , Shows the products for each section separately

 if ($request->route()->getName() == 'category.products') {
            $category = Category::active()->with('subcategories')->where('slug', $slug)->firstOrFail();
            $subcategories = $category->subcategories;
            $pageTitle     = $category->name . '- Products';

    		$categoryId    = $id;
            $products = $products->where('category_id', $categoryId);

        }

what can i do or change ?

tykus's avatar
tykus
Best Answer
Level 104

@keroherox30 if you find a Category by slug, then you have its ID as well.

        if ($request->route()->getName() == 'category.products') {
            $category      = Category::active()->with('subcategories')->where('slug', $slug)->firstOrFail();
            $subcategories = $category->subcategories;
            $pageTitle     = $category->name . '- Products';

            $products = $products->where('category_id', $category->id);
        }
tykus's avatar

@keroherox30 is this coming from the same Controller action? How are subcategories related to categories?

keroherox30's avatar

@tykus

This Full Code Controller i do with Edit you told me about Slug

    public function categoryProducts(Request $request, $slug)
    {

        $products      = Product::available();
        $categoryId    = 0;
        $subcategoryId = 0;

        if ($request->route()->getName() == 'category.products' || $request->route()->getName() == 'category.featured.products' || $request->route()->getName() == 'category.weekly.best.products' || $request->route()->getName() == 'category.best.selling.products') {


            $category      = Category::active()->with('subcategories')->where('slug', $slug)->firstOrFail();
            $subcategories = $category->subcategories;
            $pageTitle     = $category->name . '- Products';

            $products = $products->where('category_id', $category->id);



            if ($request->route()->getName() == 'category.featured.products') {
                $products = $products->featured();
            } else if ($request->route()->getName() == 'category.weekly.best.products') {
                $date     = weeklyDates();
                $products = $products
                    ->whereBetween('created_at', [$date[0], $date[1]])
                    ->where('total_sell', '>', 0)
                    ->orderBy('total_sell', 'desc');
            } else if ($request->route()->getName() == 'category.best.selling.products') {
                $products = $products->orderBy('total_sell', 'desc');
            }
        }

        if ($request->route()->getName() == 'subcategory.products') {
            $subcategoryId = $id;
            $subcategory   = Subcategory::active()->find($id);
            $pageTitle     = $subcategory->name . '- Products';

            $products      = $products->where('subcategory_id', $subcategoryId);
            $subcategories = null;
        }

        $cloneProducts = clone $products;

        $minPrice     = $cloneProducts->min('regular_price') ?? 0;
        $maxPrice     = $cloneProducts->max('regular_price') ?? 0;
        $products     = $products->with('category', 'user')->latest()->paginate(getPaginate());
        $totalProduct = $cloneProducts->count();
        $tags         = $this->getTags($cloneProducts->pluck('tag'));
        return view($this->activeTemplate . 'product.all', compact('pageTitle', 'products', 'subcategories', 'minPrice', 'maxPrice', 'tags', 'categoryId', 'subcategoryId', 'totalProduct'));
    }

This Blade For Filter

@extends($activeTemplate . 'layouts.frontend')
@section('content')
    <section class="pt-100 pb-100">
        <div class="container">
            <div class="row">
                <div class="col-lg-3 mb-lg-0 mb-3">
                    <button class="action-sidebar-open"><i class="las la-sliders-h"></i> @lang('Filter')</button>
                    <div class="action-sidebar">
                        <button class="action-sidebar-close" type="button"><i class="las la-times"></i></button>
                        <div class="action-widget action-widget-responsive">
                            <div class="input-group">
                                <input class="form--control mySearch" name="search" type="text" value="{{ request()->search }}" placeholder="@lang('Search here')...">
                                <button class="input-group-text searchBtn" type="button"><i class="las la-search"></i></button>
                            </div>
                        </div>
                        @if (@$allCategory)
                            <div class="action-widget mt-3">
                                <h6 class="action-widget__title">@lang('Categories')</h6>
                                <div class="action-widget__body">
                                    <div class="d-flex justify-content-between flex-wrap">
                                        <div class="form-check custom--checkbox mb-0">
                                            <input class="form-check-input sortCategory" id="category0" name="category" type="checkbox" value="" checked>
                                            <label class="form-check-label" for="category0">@lang('All Categories')</label>
                                        </div>
                                    </div>
                                    @foreach ($allCategory as $category)
                                        <div class="d-flex justify-content-between flex-wrap">
                                            <div class="form-check custom--checkbox mb-0">
                                                <input class="form-check-input sortCategory" id="category{{ @$category->id }}" name="category" type="checkbox" value="{{ @$category->id }}">
                                                <label class="form-check-label" for="category{{ @$category->id }}">{{ __(@$category->name) }}</label>
                                            </div>
                                        </div>
                                    @endforeach
                                </div>
                            </div>
                        @endif

                        @if (@$subcategories)
                            <div class="action-widget mt-3">
                                <h6 class="action-widget__title">@lang('Filter by categories')</h6>
                                <div class="action-widget__body">
                                    <div class="form-check custom--checkbox mb-0">
                                        <input class="form-check-input sortSubcategory" id="subcategory0" name="subcategory" type="checkbox" value="" checked>
                                        <label class="form-check-label" for="subcategory0">@lang('All Categories')</label>
                                    </div>
                                    @foreach (@$subcategories as $subcategory)
                                        <div class="form-check custom--checkbox mb-0">
                                            <input class="form-check-input sortSubcategory" id="subcategory{{ @$subcategory->id }}" name="subcategory" type="checkbox" value="{{ @$subcategory->id }}">
                                            <label class="form-check-label" for="subcategory{{ @$subcategory->id }}">{{ __(@$subcategory->name) }}</label>
                                        </div>
                                    @endforeach
                                </div>
                            </div>
                        @endif

                        <div class="action-widget mt-3">
                            <h6 class="action-widget__title">@lang('Tags')</h6>
                            <div class="action-widget__body scroll--active __tag_wrapper">
                                <div class="form-check custom--checkbox">
                                    <input class="form-check-input sortTag" id="tag" name="tag" type="checkbox" value="" checked>
                                    <label class="form-check-label" for="tag">
                                        @lang('All')
                                    </label>
                                </div>
                                @foreach ($tags as $tag)
                                    <div class="form-check custom--checkbox">
                                        <input class="form-check-input sortTag" id="tag{{ $loop->index }}" name="tag" type="checkbox" value="{{ $tag }}">
                                        <label class="form-check-label" for="tag{{ $loop->index }}">
                                            {{ __($tag) }}
                                        </label>
                                    </div>
                                @endforeach
                            </div>
                        </div>

                        <div class="action-widget mt-3">
                            <h6 class="action-widget__title">@lang('Order By')</h6>
                            <div class="action-widget__body">
                                <div class="form-check custom--checkbox mb-0">
                                    <input class="form-check-input sortProduct" id="latest" name="sort" type="radio" value="id_desc">
                                    <label class="form-check-label" for="latest">
                                        @lang('Latest')
                                    </label>
                                </div>
                                <div class="form-check custom--checkbox mb-0">
                                    <input class="form-check-input sortProduct" id="low_to_high" name="sort" type="radio" value="price_asc">
                                    <label class="form-check-label" for="low_to_high">
                                        @lang('Low to High')
                                    </label>
                                </div>
                                <div class="form-check custom--checkbox mb-0">
                                    <input class="form-check-input sortProduct" id="high_to_low" name="sort" type="radio" value="price_desc">
                                    <label class="form-check-label" for="high_to_low">
                                        @lang('High to Low')
                                    </label>
                                </div>
                                <div class="form-check custom--checkbox mb-0">
                                    <input class="form-check-input sortProduct" id="best_selling" name="sort" type="radio" value="totalSell_desc">
                                    <label class="form-check-label" for="best_selling">
                                        @lang('Best Selling')
                                    </label>
                                </div>
                                <div class="form-check custom--checkbox mb-0">
                                    <input class="form-check-input sortProduct" id="best_rating" name="sort" type="radio" value="totalReview_desc">
                                    <label class="form-check-label" for="best_rating">
                                        @lang('Best Rating')
                                    </label>
                                </div>
                            </div>
                        </div>
                        <div class="action-widget mt-3">
                            <h6 class="action-widget__title">@lang('Rating')</h6>
                            <div class="action-widget__body" style="">

                                <div class="form-check custom--radio d-flex justify-content-between align-items-center">
                                    <div class="left">
                                        <input class="form-check-input sortRating" id="ratings-0" name="star" type="radio" value="">
                                        <label class="form-check-label" for="ratings-0">@lang('All') </label>
                                    </div>
                                </div>

                                <div class="form-check custom--radio d-flex justify-content-between align-items-center">
                                    <div class="left">
                                        <input class="form-check-input sortRating" id="ratings-4" name="star" type="radio" value="4">
                                        <label class="form-check-label" for="ratings-4">
                                            <span class="text--warning">
                                                @php
                                                    echo displayRating(4);
                                                @endphp
                                            </span> & @lang('up')
                                        </label>
                                    </div>
                                </div>
                                <div class="form-check custom--radio d-flex justify-content-between align-items-center">
                                    <div class="left">
                                        <input class="form-check-input sortRating" id="ratings-3" name="star" type="radio" value="3">
                                        <label class="form-check-label" for="ratings-3">
                                            <span class="text--warning">
                                                @php
                                                    echo displayRating(3);
                                                @endphp
                                            </span> & @lang('up')

                                        </label>
                                    </div>
                                </div>
                                <div class="form-check custom--radio d-flex justify-content-between align-items-center">
                                    <div class="left">
                                        <input class="form-check-input sortRating" id="ratings-2" name="star" type="radio" value="2">
                                        <label class="form-check-label" for="ratings-2">
                                            <span class="text--warning">
                                                @php
                                                    echo displayRating(2);
                                                @endphp
                                            </span> & @lang('up')
                                        </label>
                                    </div>
                                </div>
                                <div class="form-check custom--radio d-flex justify-content-between align-items-center">
                                    <div class="left">
                                        <input class="form-check-input sortRating" id="ratings-1" name="star" type="radio" value="1">
                                        <label class="form-check-label" for="ratings-1">
                                            <span class="text--warning">
                                                @php
                                                    echo displayRating(1);
                                                @endphp
                                            </span> & @lang('up')
                                        </label>
                                    </div>
                                </div>
                            </div>
                        </div>

                        <div class="action-widget mt-3">
                            <h6 class="action-widget__title">@lang('Filter by price')</h6>
                            <div class="action-widget__body">
                                <div class="filter-price-widget pt-2">
                                    <div id="slider-range"></div>
                                    <div class="price-range">
                                        <label class="form-check-label" for="amount">@lang('Price :')</label>
                                        <input id="amount" type="text" readonly>
                                        <input name="min_price" type="hidden" value="{{ getAmount($minPrice) }}">
                                        <input name="max_price" type="hidden" value="{{ getAmount($maxPrice) }}">
                                    </div>
                                </div>
                            </div>
                        </div>
                    </div>
                </div>
                <div class="col-lg-9">
                    <div class="d-flex justify-content-between align-items-center mb-3 flex-wrap">
                        <p>
                            @if (request()->search)
                                @lang('Search Result For')
                                <span class="text--base">{{ __(request()->search) }}</span> :
                            @endif
                            @lang('Total') <span class="text--base total-product-count">({{ $totalProduct }}) </span> @lang('Products Found')
                        </p>
                        <ul class="top__bar-left">
                            <li class="list-view-btn active" id="list-item">
                                <i class="fas fa-th-list"></i>
                            </li>
                            <li class="grid-view-btn" id="box-item">
                                <i class="fas fa-th-large"></i>
                            </li>
                        </ul>
                    </div>
                    <div class="main-content position-relative">
                        <div class="loader-wrapper">
                            <div class="loader-pre"></div>
                        </div>
                        <div class="row gy-4 card-view-area list-view" id="products">
                            @include($activeTemplate . 'product.card.list', ['products' => $products])
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </section>
@endsection

@push('script')
    <script>
        (function($) {
            "use strict";
            let page = null;
            $('.loader-wrapper').addClass('d-none');
            let totalProduct = $("#products").find('.card-view').length;

            $('.sortProduct, .sortCategory,.sortSubcategory, .sortRating, .sortTag').on('click', function() {
                $('#category0').removeAttr('checked', 'checked');
                if ($('#category0').is(':checked')) {
                    $("input[type='checkbox'][name='category']").not(this).prop('checked', false);
                }
                if ($("input[type='checkbox'][name='category']:checked").length == 0) {
                    $('#category0').attr('checked', 'checked');
                }

                $('#subcategory0').removeAttr('checked', 'checked');
                if ($('#subcategory0').is(':checked')) {
                    $("input[type='checkbox'][name='subcategory']").not(this).prop('checked', false);
                }
                if ($("input[type='checkbox'][name='subcategory']:checked").length == 0) {
                    $('#subcategory0').attr('checked', 'checked');
                }


                if ($('#tag').is(':checked')) {
                    $("input[type='checkbox'][name='tag']").not(this).prop('checked', false);
                }
                if ($("input[type='checkbox'][name='tag']:checked").length == 0) {
                    $('#tag').attr('checked', 'checked');
                }

                if ($('#ratings-0').is(':checked')) {
                    $("input[type='radio'][name='star']").not(this).prop('checked', false);
                }
                page = null;
                fetchProduct();
            });

            $('.productPaginate').on('change', function() {
                page = null;
                fetchProduct();
            });

            $('.searchBtn').on('click', function() {
                $(this).attr('disabled', 'disabled');
                page = null;
                fetchProduct();
            });

            $("#slider-range").slider({
                range: true,
                min: {{ $minPrice }},
                max: {{ $maxPrice }},
                values: [{{ $minPrice }}, {{ $maxPrice }}],
                slide: function(event, ui) {
                    $("#amount").val("{{ $general->cur_sym }}" + ui.values[0] + " - {{ $general->cur_sym }}" + ui.values[1]);
                    $('input[name=min_price]').val(ui.values[0]);
                    $('input[name=max_price]').val(ui.values[1]);
                },
                change: function() {
                    $('.loader-wrapper').removeClass('d-none')
                    page = null;
                    fetchProduct();
                }
            });
            $("#amount").val("{{ $general->cur_sym }}" + $("#slider-range").slider("values", 0) + " - {{ $general->cur_sym }}" + $("#slider-range").slider("values", 1));

            function fetchProduct() {
                $('.loader-wrapper').removeClass('d-none');
                let data = {};

                data.categories = [];
                $.each($("[name=category]:checked"), function() {
                    if ($(this).val()) {
                        data.categories.push($(this).val());
                    }
                });

                data.tags = [];
                $.each($("[name=tag]:checked"), function() {
                    if ($(this).val()) {
                        data.tags.push($(this).val());
                    }
                });

                data.search = $('.mySearch').val();
                data.sort = $('.sortProduct:checked').val();
                data.rating = $('.sortRating:checked').val();
                data.min = $('input[name="min_price"]').val();
                data.max = $('input[name="max_price"]').val();
                data.paginate = $('.productPaginate').find(":selected").val();
                data.categoryId = "{{ @$categoryId }}";
                data.subcategoryId = "{{ @$subcategoryId }}";
                data.route = "{{ request()->route()->getname() }}";

                let url = `{{ route('products.filter') }}`;
                if (page) {
                    url = `{{ route('products.filter') }}?page=${page}`;
                }
                $.ajax({
                    method: "GET",
                    url: url,
                    data: data,
                    success: function(response) {
                        $('.searchBtn').removeAttr('disabled', 'disabled');
                        $('#products').html(response.view);
                        totalProduct = response.totalProduct;
                        $('.total-product-count').text(totalProduct)
                    }
                }).done(function() {
                    $('.loader-wrapper').addClass('d-none')
                });
            }
            $(document).on('click', '.pagination a', function(event) {
                event.preventDefault();
                page = $(this).attr('href').split('page=')[1];
                fetchProduct();
            });
        })(jQuery)
    </script>
@endpush

tykus's avatar

@keroherox30 this is not the same URL as the previous one discussed?

                let url = `{{ route('products.filter') }}`; // ???
                if (page) {
                    url = `{{ route('products.filter') }}?page=${page}`; // ???
                }
                $.ajax({
                    method: "GET",
                    url: url,
                    data: data,
                    success: function(response) {
                        $('.searchBtn').removeAttr('disabled', 'disabled');
                        $('#products').html(response.view);
                        totalProduct = response.totalProduct;
                        $('.total-product-count').text(totalProduct)
                    }
                }).done(function() {
                    $('.loader-wrapper').addClass('d-none')
                });
keroherox30's avatar

@tykus

There the let url = {{ route('products.filter') }}; controller


    public function productsFilter(Request $request)
    {
        $products     = Product::available();
        $routeProduct = $this->routeByProduct($products);
        $this->searchAndTagProduct($products);
        $this->getByCategory($products);
        $this->productsQuery($products, $request);

        $totalProduct = $products->count();

        $products = $products->with('category', 'user')->latest()->paginate(getPaginate());

        $data = [
            'view' => view($this->activeTemplate . 'product.card.list', compact('products'))->render(),
            'totalProduct' => $totalProduct,
        ];

        return response()->json($data);
    }
keroherox30's avatar

@tykus

NO

we talking About URL For Category

Route::get('category/products/{slug}', 'categoryProducts')->name('category.products');

tykus's avatar

@keroherox30 so how is the selected filter represented in the Controller action already? Also, can you share the Blade partial that displays the $products

keroherox30's avatar

@tykus

List Products

@forelse($products as $product)
    <div class="col-md-6 card-view">
        <div class="product-card">
            <div class="product-card__thumb">
                <a href="{{ route('detail', [slug($product->name), $product->id]) }}">
                </a>
            </div>
            <div class="product-card__content">
                <div class="product-card__inner w-100">
                    <h6 class="product-card__title">
                        <a href="{{ route('detail', [slug($product->name), $product->id]) }}" class="link border-effect">
                            {{ strLimit(__($product->name), 50) }}
                        </a>
                    </h6>
                    <div class="product-card__rating flex-between">
                        <li class="ratings">
                            @php echo displayRating($product->avg_rating) @endphp
                        </li>

                        <a href="{{ route('detail', [slug($product->name), $product->id]) }}" target="_blank" class="btn btn--primary btn--sm">
                            <i class="fas fa-shopping-cart"></i> @lang('Live Preview')
                        </a>
                    </div>
                    <p class="mb-1">@lang('Last Updated') - {{ showDateTime($product->updated_at, 'j F, Y') }}</p>
                </div>
                <div class="product-card__meta flex-between w-100">
                    <div class="product-card__actions">
                        <a href="{{ route('category.products', [$product->category->id, slug($product->category->name)]) }}" class="flex-align gap-2">
                            <span class="category-icon flex-center" data-color="#F9322C" style="background-color: rgba(248, 49, 42, 0.2); color: rgb(248, 49, 42);">
                                <i class="fab fa-laravel"></i>
                            </span>
                            <span class="item-category-name" style="color: rgb(248, 49, 42);">
                                {{ __($product->category->name) }}
                            </span>
                        </a>
                    </div>
                    <h5 class="product-card__price">{{ __($general->cur_sym) }}{{ getAmount($product->regular_price) }}</h5>
                </div>

            </div>
        </div>
    </div>
@empty
    <div class="col-md-6 col-lg-8 ms-auto">
      Not Found Items
    </div>
@endforelse
@if ($products->hasPages())
    {{ paginateLinks($products) }}
@endif

Please or to participate in this conversation.