首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 相關知識

PHP 實作麵包屑導航(Breadcrumb)完整解決方案

     編輯:PHP 相關知識

麵包屑導航是提升網站易用性、SEO 結構化資料的關鍵 UI 元件。本文從基礎路徑解析、動態標題映射、Schema.org 結構化資料輸出、快取優化到現代框架整合,提供完整的 PHP 實作方案。

核心邏輯:從 REQUEST_URI 解析路徑層級

<?php
function buildBreadcrumbs(array $labelMap = [], string $separator = ' > '): string
{
    $requestUri = $_SERVER['REQUEST_URI'] ?? '/';
    // 移除查詢字串
    $path = parse_url($requestUri, PHP_URL_PATH) ?? '/';
    $path = rtrim($path, '/');
    
    $crumbs = [];
    $currentPath = '';
    
    // 根目錄
    $crumbs[] = ['label' => '首頁', 'url' => '/', 'active' => false];
    
    if ($path !== '') {
        $segments = explode('/', ltrim($path, '/'));
        foreach ($segments as $segment) {
            $currentPath .= '/' . $segment;
            $label = $labelMap[$segment] ?? ucfirst(str_replace(['-', '_'], ' ', $segment));
            $crumbs[] = [
                'label' => $label,
                'url' => $currentPath,
                'active' => false,
            ];
        }
    }
    
    // 最後一項設為 active
    if (!empty($crumbs)) {
        $crumbs[count($crumbs) - 1]['active'] = true;
    }
    
    return renderBreadcrumbs($crumbs, $separator);
}

function renderBreadcrumbs(array $crumbs, string $separator): string
{
    $html = '';
    return $html;
}

// 使用範例
$labelMap = [
    'service' => '有償服務',
    'teach' => '家教中心',
    'union' => '校園服務聯盟',
    'job' => '學生兼職',
    'free' => '資訊庫',
];
echo buildBreadcrumbs($labelMap);
// 輸出:首頁 > 有償服務 > 子分類 > 當前頁
?>

進階:支援動態路由參數的麵包屑

<?php
class BreadcrumbBuilder
{
    private array $crumbs = [];
    private array $routeMap = []; // 路由模式 => 標題生成器
    
    public function addRoute(string $pattern, callable $labelGenerator): self
    {
        $this->routeMap[$pattern] = $labelGenerator;
        return $this;
    }
    
    public function buildFromRequest(): self
    {
        $this->crumbs = [];
        $path = parse_url($_SERVER['REQUEST_URI'] ?? '/', PHP_URL_PATH) ?? '/';
        $path = rtrim($path, '/');
        
        $this->crumbs[] = ['label' => '首頁', 'url' => '/', 'active' => false];
        
        if ($path !== '') {
            $segments = explode('/', ltrim($path, '/'));
            $currentPath = '';
            foreach ($segments as $i => $segment) {
                $currentPath .= '/' . $segment;
                $isLast = ($i === count($segments) - 1);
                
                // 嘗試匹配路由模式
                $label = $segment;
                $matched = false;
                foreach ($this->routeMap as $pattern => $generator) {
                    // 簡單模式匹配:/user/{id}/post/{pid}
                    $regex = '@^' . preg_replace('/\{[^}]+\}/', '([^/]+)', $pattern) . '$@';
                    if (preg_match($regex, $currentPath, $matches)) {
                        array_shift($matches); // 移除完整匹配
                        $label = $generator(...$matches);
                        $matched = true;
                        break;
                    }
                }
                
                if (!$matched) {
                    $label = ucfirst(str_replace(['-', '_'], ' ', $segment));
                }
                
                $this->crumbs[] = [
                    'label' => $label,
                    'url' => $isLast ? null : $currentPath,
                    'active' => $isLast,
                ];
            }
        }
        return $this;
    }
    
    public function render(string $template = ''): string
    {
        if ($template) {
            // 自定義模板渲染
            ob_start();
            include $template;
            return ob_get_clean();
        }
        return $this->defaultRender();
    }
    
    private function defaultRender(): string
    {
        $html = '';
        return $html;
    }
    
    public function getJsonLd(): string
    {
        $items = [];
        foreach ($this->crumbs as $i => $crumb) {
            $items[] = [
                '@type' => 'ListItem',
                'position' => $i + 1,
                'name' => $crumb['label'],
                'item' => $crumb['url'] ? (isset($_SERVER['HTTPS']) ? 'https' : 'http') . '://' . $_SERVER['HTTP_HOST'] . $crumb['url'] : null,
            ];
        }
        return json_encode([
            '@context' => 'https://schema.org',
            '@type' => 'BreadcrumbList',
            'itemListElement' => $items,
        ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
    }
}

// 使用範例
$builder = new BreadcrumbBuilder();
$builder->addRoute('/user/{id}', fn($id) => "用戶 #$id")
        ->addRoute('/article/{slug}', fn($slug) => ucfirst($slug))
        ->addRoute('/category/{name}/page/{page}', fn($name, $page) => "$name (第 $page 頁)");
        
$builder->buildFromRequest();
echo $builder->render();
// 頁面底部輸出 Schema.org JSON-LD
echo '';
?>

Schema.org BreadcrumbList 結構化資料

Google 搜尋結果會顯示麵包屑,提升點擊率:

<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "BreadcrumbList",
  "itemListElement": [
    {"@type": "ListItem", "position": 1, "name": "首頁", "item": "https://example.com/"},
    {"@type": "ListItem", "position": 2, "name": "有償服務", "item": "https://example.com/service"},
    {"@type": "ListItem", "position": 3, "name": "網站開發", "item": "https://example.com/service/web-dev"}
  ]
}
</script>

快取優化:避免每請求重複解析

<?php
function getCachedBreadcrumbs(string $cacheKey, callable $generator, int $ttl = 3600): string
{
    $cacheFile = sys_get_temp_dir() . '/breadcrumbs_' . md5($cacheKey) . '.cache';
    
    if (file_exists($cacheFile) && (time() - filemtime($cacheFile)) < $ttl) {
        return file_get_contents($cacheFile);
    }
    
    $html = $generator();
    file_put_contents($cacheFile, $html);
    return $html;
}

// 使用
$cacheKey = $_SERVER['REQUEST_URI'];
$breadcrumbsHtml = getCachedBreadcrumbs($cacheKey, fn() => (new BreadcrumbBuilder())->buildFromRequest()->render());
echo $breadcrumbsHtml;
?>

現代框架整合範例

Laravel Blade 元件

<!-- resources/views/components/breadcrumb.blade.php -->
@props(['crumbs' => [], 'separator' => '>'])


<!-- 使用 -->
 

Symfony Twig 擴充

<?php
// src/Twig/BreadcrumbExtension.php
class BreadcrumbExtension extends \Twig\Extension\AbstractExtension
{
    public function getFunctions(): array
    {
        return [
            new \Twig\TwigFunction('breadcrumbs', [$this, 'renderBreadcrumbs']),
        ];
    }
    
    public function renderBreadcrumbs(array $crumbs, string $separator = '>'): string
    {
        // 渲染邏輯...
    }
}
?>
{# templates/_breadcrumb.html.twig #}

無障礙(a11y)最佳實踐

  • <nav aria-label="breadcrumb"> 標識導航區塊
  • <ol> 語意化有序列表
  • aria-current="page" 標識當前頁
  • 分隔符使用 aria-hidden="true" 隱藏螢幕閱讀器
  • 鍵盤可聚焦、色彩對比度符合 WCAG AA

常見場景與解決方案

場景解決方案
單頁應用(SPA)前端路由更新麵包屑,或 SSR 預渲染
多語系網站標題映射表按語系區分,或使用翻譯函數 __()
動態參數過多(如 /search/q/foo/page/2/sort/desc)只顯示主要層級,參數頁面合併為「搜尋結果」
深層巢狀超過 5 層截斷中間層級,顯示「...」或僅保留前 2 + 後 2
非標準 URL(如 rewrite 後)使用路由解析器而非直接解析 REQUEST_URI

總結

優質的麵包屑導航需要:正確的路徑解析、靈活的標題映射、語意化 HTML、Schema.org 結構化資料、快取機制、無障礙支援。建議封裝為可複用的類別或元件,整合至專案的佈局模板中,並在路由/控制器層提供動態標題覆寫機制。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved