You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Laravel实现短URL模式获取分类/子分类/子子分类商品数据

实现Laravel分类短URL访问方案

问题背景

当前项目仅支持/{category}/{subcategory}/{childcategory}层级URL获取对应分类商品,需要实现/{category}、/{subcategory}、/{childcategory}短URL模式,直接访问对应层级分类的商品数据。现有控制器和路由代码如下:

现有控制器方法

public function category(Request $request, $slug = null, $slug1 = null, $slug2 = null)
{
    if (Session::has('currency')) {
        $curr = Currency::find(Session::get('currency'));
    } else {
        $curr = Currency::where('is_default', '=', 1)->first();
    }

    $cat = null;
    $subcat = null;
    $childcat = null;
    $minprice = (isset($request->min)) ? $request->min : 0;
    $maxprice = (isset($request->max)) ? $request->max : 0;
    $sort = $request->sort;
    $minprice = round(($minprice / $curr->value), 2);
    $maxprice = round(($maxprice / $curr->value), 2);

    if (!empty($slug)) {
        $cat = Category::where('slug', $slug)->firstOrFail();
        $data['cat'] = $cat;
        $prodcs = Product::select(
            "*",
            DB::raw('(CASE WHEN size IS NULL THEN price ELSE size_base_price END) AS price_filter')
        );

        $prodcss = Product::select(
            "*",
            DB::raw('(CASE WHEN size IS NULL THEN price ELSE size_base_price END) AS price_filter')
        );

        $minamount = $prodcs->where('category_id', $cat->id)->orderBy('price_filter', 'ASC')->first()->price_filter;
        $maxamount = $prodcss->where('category_id', $cat->id)->orderBy('price_filter', 'DESC')->first()->price_filter;
        // $maxamount = $maxamount + 1;
    }

    if (!empty($slug1)) {
        $subcat = Subcategory::where('slug', $slug1)->firstOrFail();
        $data['subcat'] = $subcat;

        $minamount = @Product::where('subcategory_id', $subcat->id)->orderBy('price', 'ASC')->first()->price;
        $maxamount = @Product::where('subcategory_id', $subcat->id)->orderBy('price', 'DESC')->first()->price;
    }

    if (!empty($slug2)) {
        $childcat = Childcategory::where('slug', $slug2)->firstOrFail();
        $data['childcat'] = $childcat;

        $minamount = @Product::where('childcategory_id', $childcat->id)->orderBy('price', 'ASC')->first()->price;
        $maxamount = @Product::where('childcategory_id', $childcat->id)->orderBy('price', 'DESC')->first()->price;
    }

    $firstPrice = (isset($request->price[0])) ? $request->price[0] : "";
    $secondPrice =  (isset($request->price[1])) ? explode('-', $request->price[1]) : "";
    $thirdPrice = (isset($request->price[2])) ? explode('-', $request->price[2]) : "";
    $fourthPrice =  (isset($request->price[3])) ? $request->price[3] : "";

    $prods = Product::select(
        "*",
        DB::raw('(CASE WHEN size IS NULL THEN price ELSE size_base_price END) AS price_filter')
    );

    $prods->when($cat, function ($query, $cat) {
        return $query->where('category_id', $cat->id);
    })->when($subcat, function ($query, $subcat) {
        return $query->where('subcategory_id', $subcat->id);
    })->when($childcat, function ($query, $childcat) {
        return $query->where('childcategory_id', $childcat->id);
    });

    if (isset($request->price[0]) || isset($request->price[1]) || isset($request->price[2]) || isset($request->price[3])) {
        $prods->where(function ($query) use ($firstPrice, $secondPrice, $thirdPrice, $fourthPrice) {
            if ($firstPrice != "") {
                $query->having('price_filter', '<', $firstPrice);
            }

            if (!empty($secondPrice)) {
                $query->having('price_filter', [$secondPrice[0], $secondPrice[1]]);
            }

            if (!empty($thirdPrice)) {
                $query->having('price_filter', [$thirdPrice[0], $thirdPrice[1]]);
            }

            if ($fourthPrice != "") {
                $query->having('price_filter', '>', $fourthPrice);
            }
        });
    }

    if (!isset($request->price[0]) && !isset($request->price[1]) && !isset($request->price[2]) && !isset($request->price[3])) {
        $prods->when($minprice, function ($query, $minprice) {
            return $query->having('price_filter', '>=', $minprice);
        });

        $prods->when($maxprice, function ($query, $maxprice) {
            return $query->having('price_filter', '<=', $maxprice);
        });
    }

    $prods->when($sort, function ($query, $sort) {
        if ($sort == 'date_desc') {
            return $query->orderBy('id', 'DESC');
        } elseif ($sort == 'date_asc') {
            return $query->orderBy('id', 'ASC');
        } elseif ($sort == 'price_desc') {
            return $query->orderBy('price_filter', 'DESC');
        } elseif ($sort == 'price_asc') {
            return $query->orderBy('price_filter', 'ASC');
        }
    })->when(empty($sort), function ($query, $sort) {
        return $query->orderBy('id', 'DESC');
    });

    $prods = $prods->where(function ($query) use ($cat, $subcat, $childcat, $request) {
        $flag = 0;

        if (!empty($cat)) {
            foreach ($cat->attributes as $key => $attribute) {
                $inname = $attribute->input_name;
                $chFilters = $request["$inname"];

                if (!empty($chFilters)) {
                    $flag = 1;

                    foreach ($chFilters as $key => $chFilter) {
                        if ($key == 0) {
                            $query->where('attributes', 'like', '%' . '"' . $chFilter . '"' . '%');
                        } else {
                            $query->orWhere('attributes', 'like', '%' . '"' . $chFilter . '"' . '%');
                        }
                    }
                }
            }
        }

        if (!empty($subcat)) {
            foreach ($subcat->attributes as $attribute) {
                $inname = $attribute->input_name;
                $chFilters = $request["$inname"];

                if (!empty($chFilters)) {
                    $flag = 1;

                    foreach ($chFilters as $key => $chFilter) {
                        if ($key == 0 && $flag == 0) {
                            $query->where('attributes', 'like', '%' . '"' . $chFilter . '"' . '%');
                        } else {
                            $query->orWhere('attributes', 'like', '%' . '"' . $chFilter . '"' . '%');
                        }
                    }
                }
            }
        }

        if (!empty($childcat)) {
            foreach ($childcat->attributes as $attribute) {
                $inname = $attribute->input_name;
                $chFilters = $request["$inname"];

                if (!empty($chFilters)) {
                    $flag = 1;

                    foreach ($chFilters as $key => $chFilter) {
                        if ($key == 0 && $flag == 0) {
                            $query->where('attributes', 'like', '%' . '"' . $chFilter . '"' . '%');
                        } else {
                            $query->orWhere('attributes', 'like', '%' . '"' . $chFilter . '"' . '%');
                        }
                    }
                }
            }
        }
    });

    $prods = $prods->where('status', 1)->get();
    $prods = (new Collection(Product::filterProducts($prods)))->paginate(24);

    $data['prods'] = $prods;
    $data['minamount'] = $minamount;
    $data['maxamount'] = $maxamount;

    if ($request->ajax()) {
        $data['ajax_check'] = 1;
        return view('includes.product.filtered-products', $data);
    }

    return view('front.category', $data);
}

现有路由代码

// CATEGORY SECTION
Route::get('/{category?}/{subcategory?}/{childcategory?}','Front\CatalogController@category')->name('front.category');
Route::get('/{slug1}/{slug2}','Front\CatalogController@subcategory')->name('front.subcat');
Route::get('/{slug1}/{slug2}/{slug3}','Front\CatalogController@childcategory')->name('front.childcat');
Route::get('/categories/','Front\CatalogController@categories')->name('front.categories');
Route::get('/childcategories/{slug}', 'Front\CatalogController@childcategories')->name('front.childcategories');

// CATEGORY SECTION ENDS

解决方案

1. 路由调整

短URL路由需要优先匹配,同时移除原有冲突的子分类、子子分类路由:

// CATEGORY SECTION - 短URL路由放在最前面,优先匹配
Route::get('/{slug}', 'Front\CatalogController@singleCategory')->name('front.single.category');

// 保留原有多层级分类路由
Route::get('/{category}/{subcategory}/{childcategory}','Front\CatalogController@category')->name('front.category');
Route::get('/categories/','Front\CatalogController@categories')->name('front.categories');
Route::get('/childcategories/{slug}', 'Front\CatalogController@childcategories')->name('front.childcategories');

// 移除冲突路由
// Route::get('/{slug1}/{slug2}','Front\CatalogController@subcategory')->name('front.subcat');
// Route::get('/{slug1}/{slug2}/{slug3}','Front\CatalogController@childcategory')->name('front.childcat');

2. 新增短URL处理控制器方法

在Front\CatalogController中新增方法,匹配单段slug并路由到原有分类逻辑:

public function singleCategory(Request $request, $slug)
{
    // 依次匹配顶级分类、子分类、子子分类
    $cat = Category::where('slug', $slug)->first();
    if ($cat) {
        return $this->category($request, $slug, null, null);
    }

    $subcat = Subcategory::where('slug', $slug)->first();
    if ($subcat) {
        return $this->category($request, null, $slug, null);
    }

    $childcat = Childcategory::where('slug', $slug)->first();
    if ($childcat) {
        return $this->category($request, null, null, $slug);
    }

    // 无匹配时返回404
    abort(404);
}

3. 优化原有控制器逻辑(可选)

原有代码中minamount/maxamount使用@抑制错误,且重复查询,可优化为:

// 替换原有分类下价格区间查询逻辑
$minamount = 0;
$maxamount = 0;

if ($cat) {
    $priceRange = Product::select(
        DB::raw('MIN(CASE WHEN size IS NULL THEN price ELSE size_base_price END) as min_price'),
        DB::raw('MAX(CASE WHEN size IS NULL THEN price ELSE size_base_price END) as max_price')
    )->where('category_id', $cat->id)->first();
    $minamount = $priceRange->min_price ?? 0;
    $maxamount = $priceRange->max_price ?? 0;
} elseif ($subcat) {
    $priceRange = Product::select(
        DB::raw('MIN(CASE WHEN size IS NULL THEN price ELSE size_base_price END) as min_price'),
        DB::raw('MAX(CASE WHEN size IS NULL THEN price ELSE size_base_price END) as max_price')
    )->where('subcategory_id', $subcat->id)->first();
    $minamount = $priceRange->min_price ?? 0;
    $maxamount = $priceRange->max_price ?? 0;
} elseif ($childcat) {
    $priceRange = Product::select(
        DB::raw('MIN(CASE WHEN size IS NULL THEN price ELSE size_base_price END) as min_price'),
        DB::raw('MAX(CASE WHEN size IS NULL THEN price ELSE size_base_price END) as max_price')
    )->where('childcategory_id', $childcat->id)->first();
    $minamount = $priceRange->min_price ?? 0;
    $maxamount = $priceRange->max_price ?? 0;
}

注意事项

  • 路由优先级:短URL路由必须放在所有其他单段路由(如/about、/contact)之前,避免被拦截。
  • Slug唯一性:确保所有分类的slug全局唯一,否则会出现匹配错误;若无法保证,可在数据库增加层级标识字段辅助判断。
  • 错误处理:无匹配分类时返回404,避免逻辑混乱。

内容的提问来源于stack exchange,提问作者ritesh malviya

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 11:48:14