phpでパンくずリストを作る方法

Webサイトを作っていると、ページの上部に「ホーム > ブログ > PHP」のような案内を表示したくなることがあります。これがパンくずリストです。利用者が今どこにいるのかを把握しやすくなりますし、上の階層へ戻りやすくなるので、使いやすさの面でも役立ちます。
PHPでパンくずリストを作る方法はいくつかありますが、初心者が理解しやすいのは、URLを分解して階層を取り出し、その名前を別で管理するやり方です。最初はPHPの配列に対応表を書いておき、慣れてきたらJSONファイルに分けて管理する、という流れがわかりやすいと思います。
今回は、PHPでパンくずリストを作る基本的な考え方から、配列で管理する方法、さらにJSONで管理する方法まで、順番に整理してみます。
パンくずリストとは何か
パンくずリストとは、現在表示しているページがサイト内のどこにあるのかを階層で示すナビゲーションのことです。たとえば、PHPに関する記事ページなら、次のような形になります。
ホーム > ブログ > PHP > PHPでパンくずリストを作る方法
このように表示しておくと、利用者は今のページがブログの中のPHPカテゴリにあることをすぐに理解できます。また、途中の「ブログ」や「PHP」をクリックできるようにしておけば、関連する上位ページへ簡単に戻れます。
パンくずリストは見た目としてもよく使われますが、単なる飾りではありません。ページの構造を整理して見せる役割があり、サイトを作る側にとっても、どのページがどのカテゴリに属しているのかを意識するきっかけになります。
PHPでパンくずリストを作るときの基本的な考え方
PHPでパンくずリストを作るときは、まず現在のURLを取得し、そのURLを階層ごとに分解します。たとえば、今のURLが /blog/php/laravel/ なら、次のように分けられます。
- blog
- php
- laravel
ただし、これをそのまま画面に出しても、見た目としてはあまり親切ではありません。blog は「ブログ」、php は「PHP」と表示したいでしょうし、laravel も必要に応じて「Laravel」と見せたいところです。そこで、URLの各要素と表示名を対応づける仕組みが必要になります。
考え方の流れは次のとおりです。
- 現在のURLパスを取得する
- スラッシュで区切って階層を取り出す
- 各ディレクトリ名に対応する表示名を取得する
- 階層ごとのURLを組み立てる
- 最後にHTMLとして出力する
最初はPHPの配列で表示名を管理するとわかりやすいです。そして、ページ数やカテゴリ数が増えてきたら、JSONファイルに切り出していくと整理しやすくなります。
まずは配列でパンくずの名前を管理する方法
URLを分解して階層を取り出す
最初に、現在のURLパスを取得します。パンくずリストでは、実際のPHPファイル名よりも、ブラウザに見えているURLをもとにしたほうが自然です。そのため、$_SERVER['REQUEST_URI'] を使うことが多いです。
たとえば、次のように書きます。
$currentPath = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
このコードで、クエリ文字列を除いたURLパスだけを取得できます。たとえば、/blog/php/laravel/?page=2 というURLなら、取り出されるのは /blog/php/laravel/ です。
その後、前後のスラッシュを取り除いて、explode() で分解します。
$segments = explode('/', trim($currentPath, '/'));
これで、/blog/php/laravel/ は ['blog', 'php', 'laravel'] という配列になります。
ディレクトリ名と表示名を配列で対応づける
次に、URLに含まれるディレクトリ名と、画面に表示したい名前を対応づけます。最初はPHPの配列で十分です。
$nameMap = [
'' => 'ホーム',
'blog' => 'ブログ',
'php' => 'PHP',
'laravel' => 'Laravel',
'javascript' => 'JavaScript',
'about' => 'このサイトについて'
];
この配列の役割は単純で、たとえばURLの中に blog があれば「ブログ」と表示し、php があれば「PHP」と表示する、という対応表です。ホームだけはディレクトリ名がないので、空文字をキーにしておくと扱いやすくなります。
この段階では、表示名をソースコードの中で直接管理していることになります。ページ数が少ないうちは、この方法でも十分実用的です。
配列を使ってパンくずリストを組み立てる
URLを分解できて、表示名の対応表も用意できたら、あとはパンくずリストの配列を順番に組み立てていきます。流れとしては、まずホームを入れ、そのあとURLの各階層をひとつずつ追加していく形です。
<?php
$nameMap = [
'' => 'ホーム',
'blog' => 'ブログ',
'php' => 'PHP',
'laravel' => 'Laravel',
'javascript' => 'JavaScript',
'about' => 'このサイトについて'
];
$currentPath = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
$rawSegments = explode('/', trim($currentPath, '/'));
$segments = array_values(array_filter($rawSegments, function ($segment) {
return $segment !== '' && $segment !== 'index.php';
}));
$breadcrumbs = [
[
'name' => $nameMap[''] ?? 'ホーム',
'url' => '/'
]
];
$accumulatedPath = '';
foreach ($segments as $segment) {
$accumulatedPath .= '/' . $segment . '/';
$breadcrumbs[] = [
'name' => $nameMap[$segment] ?? $segment,
'url' => $accumulatedPath
];
}
$lastIndex = count($breadcrumbs) - 1;
if ($lastIndex >= 0) {
$breadcrumbs[$lastIndex]['url'] = null;
}
echo '<nav aria-label="パンくず">';
echo '<ol>';
foreach ($breadcrumbs as $item) {
echo '<li>';
if (!empty($item['url'])) {
echo '<a href="' . htmlspecialchars($item['url'], ENT_QUOTES, 'UTF-8') . '">';
echo htmlspecialchars($item['name'], ENT_QUOTES, 'UTF-8');
echo '</a>';
} else {
echo htmlspecialchars($item['name'], ENT_QUOTES, 'UTF-8');
}
echo '</li>';
}
echo '</ol>';
echo '</nav>';
このコードでは、URLの各要素をもとにパンくずの配列を作っています。最後の要素だけは現在ページなので、リンクを外しています。これで、利用者には現在地がわかりやすくなります。
配列で管理する方法のメリットと注意点
配列で管理する方法のよいところは、まずわかりやすいことです。PHPファイルの中に対応表がそのまま書かれているので、どのディレクトリがどの名前になるのかがすぐに確認できます。少ないページ数で運用するサイトなら、これで十分なことも多いです。
一方で、ページ数やカテゴリ数が増えてくると、管理が少しずつ大変になります。対応表が大きくなると見通しが悪くなりますし、処理のコードと設定のデータが同じファイルに混ざってくるので、保守もしにくくなります。最初は配列で始めてよいのですが、長く運用するなら、いずれ設定を外に出したくなります。
次にJSONでパンくずの名前を管理する方法
配列管理をJSONに置き換える理由
配列での管理はわかりやすいのですが、対応表が増えてくると、PHPコードの中にデータが大量に書かれている状態になります。そうなると、処理のロジックを見たいのに配列ばかり目に入ったり、逆に設定だけ編集したいのにPHPコードまで気にしなければならなかったりします。
そこで便利なのがJSONです。表示名の対応表だけをJSONファイルとして切り出しておけば、PHP側は「URLを分解してJSONから名前を取る処理」に専念できます。設定と処理を分けられるので、見通しがよくなります。
また、最初は配列で作っていたとしても、JSONに移行するのは比較的簡単です。対応表の中身を別ファイルに移して、PHPで読み込むだけだからです。大げさな仕組みではありませんが、小さな整理としてはかなり効果があります。
JSONファイルにディレクトリ名と表示名を書く
たとえば、breadcrumb.json というファイルを作り、次のように書きます。
{
"": "ホーム",
"blog": "ブログ",
"php": "PHP",
"laravel": "Laravel",
"javascript": "JavaScript",
"about": "このサイトについて"
}
考え方は配列のときと同じです。キーにディレクトリ名を書き、値に表示名を書きます。ホームは空文字をキーにしておくと、そのまま使えます。
この形なら、表示名を修正したいときはJSONファイルを開くだけで済みます。PHPのロジックに触れずに設定だけ直せるのが便利です。
PHPでJSONを読み込んで名前を取得する
JSONファイルを使うときは、file_get_contents() でファイルを読み込み、json_decode() で配列に変換します。
$json = file_get_contents(__DIR__ . '/breadcrumb.json');
$nameMap = json_decode($json, true);
このあと、パンくずを作る処理は配列版とほとんど同じです。実際に書くと、次のようになります。
<?php
$json = file_get_contents(__DIR__ . '/breadcrumb.json');
$nameMap = json_decode($json, true);
if (!is_array($nameMap)) {
$nameMap = [
'' => 'ホーム'
];
}
$currentPath = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
$rawSegments = explode('/', trim($currentPath, '/'));
$segments = array_values(array_filter($rawSegments, function ($segment) {
return $segment !== '' && $segment !== 'index.php';
}));
$breadcrumbs = [
[
'name' => $nameMap[''] ?? 'ホーム',
'url' => '/'
]
];
$accumulatedPath = '';
foreach ($segments as $segment) {
$accumulatedPath .= '/' . $segment . '/';
$breadcrumbs[] = [
'name' => $nameMap[$segment] ?? $segment,
'url' => $accumulatedPath
];
}
$lastIndex = count($breadcrumbs) - 1;
if ($lastIndex >= 0) {
$breadcrumbs[$lastIndex]['url'] = null;
}
echo '<nav aria-label="パンくず">';
echo '<ol>';
foreach ($breadcrumbs as $item) {
echo '<li>';
if (!empty($item['url'])) {
echo '<a href="' . htmlspecialchars($item['url'], ENT_QUOTES, 'UTF-8') . '">';
echo htmlspecialchars($item['name'], ENT_QUOTES, 'UTF-8');
echo '</a>';
} else {
echo htmlspecialchars($item['name'], ENT_QUOTES, 'UTF-8');
}
echo '</li>';
}
echo '</ol>';
echo '</nav>';
配列版と比べると、対応表の部分がJSONの読み込みに置き換わっただけです。このくらいの差であれば、最初に配列で理解しておけば、JSON版もすぐ読めると思います。
JSONで管理する方法のメリットと注意点
JSONで管理する方法の大きなメリットは、処理と設定を分けられることです。PHPコードがすっきりしますし、表示名の管理だけを別ファイルで行えるので、保守性が上がります。あとでカテゴリが増えたときも、PHPの中を探して修正するより、JSONファイルを編集するほうが気楽です。
ただし、JSONにしたからといって万能になるわけではありません。ディレクトリ名をキーにしている以上、同じ名前のディレクトリが別の場所に出てきたときに困る可能性があります。また、最後の階層が記事のスラッグになっている場合、JSONに登録していなければ、そのままの英数字が表示されてしまいます。
つまり、JSONにすると管理は整理しやすくなりますが、構造そのものの限界は配列版と大きくは変わりません。それでも、小規模から中規模のサイトであれば、かなり実用的な方法です。
配列管理とJSON管理はどう使い分けるか
ここまで見てくると、配列でもJSONでもできることはかなり似ています。違いは、どこに名前の対応表を置くかです。
配列管理は、最初に理解するにはとてもよい方法です。1ファイルの中で完結するので、動きが見えやすいですし、初心者でも追いやすいです。まだ試作段階だったり、ページ数が少なかったりするなら、無理にJSONにしなくても問題ありません。
一方で、少しずつページが増え、カテゴリの管理も増えてくると、JSONのほうが扱いやすくなります。設定を別ファイルに出しておけば、PHPコード本体は読みやすく保てますし、後から見返したときにも整理されている印象になります。
個人的には、最初は配列で仕組みを理解し、そのあとJSONに切り出す流れが一番学びやすいと思います。いきなりJSONから入ることもできますが、まずは配列で「何をやっているのか」を理解したほうが、仕組みが頭に入りやすいです。
PHPでパンくずリストを作るときに気をつけたいこと
パンくずリストは単純そうに見えますが、実際に作ると細かい注意点がいくつかあります。まず、index.php のようなファイル名をそのままパンくずに含めたくないことがあります。そのため、URLを分解したあとに不要な要素を除外する処理を入れておくと安心です。
また、クエリ文字列は通常パンくずには含めません。?page=2 や ?sort=new のような情報は、現在の表示条件ではあっても、階層そのものではないからです。この点でも、parse_url() でパスだけを取り出しておくのが大事です。
さらに、最後の要素をリンクにするかどうかも考えどころです。一般的には、現在ページはリンクにせず、単なるテキストとして表示することが多いです。利用者にとっても、今いるページを改めてクリックする必要はないからです。
そして、表示名がJSONや配列に登録されていない場合の扱いも決めておくとよいです。今回のコードでは、登録がない場合はディレクトリ名そのものを表示するようにしています。開発中はこれでも十分ですが、本番運用では、なるべくすべての表示名を登録しておきたいところです。
まとめ
PHPでパンくずリストを作るときは、まず現在のURLを取得し、それを階層ごとに分解して、各ディレクトリ名に対応する表示名をあてる、という流れで考えるとわかりやすいです。
最初は配列で管理する方法が理解しやすく、仕組みを学ぶにはちょうどよいと思います。表示名の対応表がそのまま見えるので、何が起きているのかを追いやすいからです。その上で、管理する項目が増えてきたら、JSONに切り出すことで処理と設定を分離でき、見通しがよくなります。
つまり、配列管理とJSON管理は対立するものではなく、段階の違いと考えると自然です。まずは配列でパンくずリストの動きを理解し、次にJSONで整理していく。その流れで学ぶと、PHPでの実装がかなりわかりやすくなるはずです。

