CW(2,3) - BREADCRUMBS modul
Breadcrumbs jsou zásadním prvkem navigace a zpřehlednění webu. Příklad breadcrumbs algoritmu v PHP.
Ačkoli na internetu najdete pro PHP přehršel návodů na kdejakou jednotlivost, jejich konečná suma v případě Breadcrumbs modulu se náhle tenčí. Vlastní breadcrumbs totiž mohou být docela hardcore záležitostí a žádný univerzální přístup zde neplatí.
Co to jsou breadcrumbs?
V terminologii Joomly jsou breadcrumbs právě tyto klikatelné odkazy na vršku stránky vyjadřující organizační hierarchii vašeho webu (váš systém kategorií a podkategorií článků). Pro obsahový web jsou nesmírně důležité, protože usnadňují uživateli orientaci, kde se na vašem webu zrovna nalézají a zároveň navigaci, jak se dostat do jiné sekce a dílčí podsekce obsahu.
Pojmy kategorie a sekce již v Joomle stejně jako v HTML splývají.
Při řešení doplňku breadcrumbs v PHP jde vlastně o to, že systém vašich kategorií představuje matici. Něco jako tabulku řádků a sloupců v excelu, přičemž oba rozměry mají zpravidla zcela jiný počet polí pro daný článek obsahu.
Hledání polohy v matici
Nejšílenější programátoři matematického stylu se vlastně snaží a nadbytečně složité řešení polohy a pohybu v matici v PHP. Svoje krkolomné řešení pak nabízejí v plén každému zájemci jako univerzální, ovšem univerzální je možná pouze algoritmus, nikoli jeho konkrétní napojení na bussiness logiku každé dané aplikace, která je každá nakonec unikátem.
Toto řešení vám nedá několik dnů spát a na jiných webech stejně bude nepoužitelné, takže ho nedoporučuji.
Střední řešení
Do toho jsem se nejdříve pustil i já a několik let ho provozoval. Po přechodu na Laravel jsem ho však už nemohl použít a při jeho vývoji jsem taky strávil hezkých pár dnů pekelného rozčarování.
Když máte SQL tabulku obsahu veškerého webu, kvůli breadcrumbs do ní tak jako tak budete muset dodefinovat několik organizačních sloupců, které jinak vlastně nejsou potřeba, ale princip breadcrumbs se bez nich neobejde.
Jsou to sloupce jako parent nebo child každého článku. Já jsem se jal řešit to pomocí parentů. U každého článku tedy v PHP testuji, jestli a jaká je jeho rodičovská kategorie, jestli vůbec a jaká je jeho další nadkategorie, případně další a další...
Dostanete se do pekelných algoritmů obcházejících matici přes několik řádkových asociativních polí. Nebo pomocí řetězce znaků, kde zase musíte identifikovat oddělovače (typicky lomítka) a z řetězce vytáhnout podkategorie a nadkategorie.
Když to pak potřebujete napasovat na vaši kompletní Content Table v SQL, stejně se do toho zamotáte. Následující ukázky jsou ilustrační.
V první PHP funkci nejdříve zjišťuji sloupec parent, tedy ten, do kterého článek bezprostředně náleží.
Doslova: Najdi parent tam, kde permalink z URL rovná se URLaliasu článku.
<?php
function parentCLANKU() {
global $pdo, $permalink, $parentCLANKU;
$dotaz = $pdo->prepare("SELECT PARENT FROM CONTENT WHERE URLalias=:permalink");
$dotaz->bindValue(':permalink', $permalink);
$dotaz->execute();
$parentCLANKU = $dotaz->fetchColumn();
return $parentCLANKU;
}
V druhé PHP funkci zjišťuji, jestli se rodičovská kategorie článku náhodou nenalézá také ještě v jedné nadkategorii. A vidíte, že kvůli tomu už jsem si udělal další SQL tabulku CATLIST mých asi 15 podkategorií, která má dva sloupce. V jednom je název podkategorie shodný s parentem článku a v druhém případná nadkategorie nebo prázdno.
Doslova: Najdi parent z tabulky seznamu kategorií tam, kde název kategorie rovná se názvu kategorie článku.
function meziCAT() {
//zjistí PARENT z tab kategorií podle parentu článku
global $pdo, $parentCLANKU, $meziCAT;
$dotaz = $pdo->prepare("SELECT PARENT FROM CATLIST WHERE CATnazevID=:parentCLANKU LIMIT 1");
$dotaz->bindValue(':parentCLANKU', $parentCLANKU);
$dotaz->execute();
$meziCAT = $dotaz->fetchcolumn();
}
A takto dál štěpíte a větvíte a přidáváte, až dojdete k tomu, že stromová struktura kategorií vašeho webu by pro tyto účely měla být co nejjednodušší. Takže jsem hierarchii článků přeorganizoval tak, aby byla maximálně ve dvou úrovních kategorií plus jedné pseudoúrovni znamenající přímo daný článek. Ono také upřímně řečeno web, který má více než tři úrovně hierarchie obsahu působí na diváka docela děsivě. Dříve to bývaly weby státních úřadů a nadnárodních firem, ale dnes hierarchii také zjednodušili, jak mohli, a nebo ji úplně zrušili, takže každý článek je samotářem v jediné přeplněné kategorii, která je množinou všech článků webu. Pak není potřeba breadcrumbs vůbec zobrazovat, což zase u velkých webů nepůsobí úplně profesionálně.
Breadcrumbs v CMS systémech a PHP frameworcích
A to nemluvím o tom, že třeba Joomla odráží hierarchii breadcrumbs také v URL, do čehož vás nutí také Yii2 framework a CodeIgniter. Což jsem rovnou zrušil jako kontraproduktivní. Například Laravel tohle rozhodnutí nechává zcela na vás, ale při své genialitě vám k tomu zároveň poskytuje pomůcky, jak vám programování breadcrumbs algoritmu silně zjednodušit.
Tedy dál: Mám jenom dvě úrovně kategorií, takže už je to hračka.
Teď už řeším generování HTML odkazů do breadcrumbs. Z databáze tedy musím nejdříve nasát data k parentu článku a to se děje v tabulce obsahu (CONTENT).
function nasajPARENTclankuCATdata() {
global $pdo, $parentCLANKU, $PARENTclankuCATdata, $CATnazev, $CATurl;
if (isset($parentCLANKU)) {
$dotaz = $pdo->prepare("SELECT * FROM CONTENT WHERE CATnazevID=:parentCLANKU AND ATTR='uvod' LIMIT 1");
$dotaz->bindValue(':parentCLANKU', $parentCLANKU, PDO::FETCH_ASSOC);
$dotaz->execute();
$PARENTclankuCATdata = $dotaz->fetchAll();
V takto vzniklém asociativním poli PARENTclankuCATdata mě dále zajímá:
Nadpis článku (TEX1nadpis),
a jeho část URL (URLalias).
$CATnazev = $PARENTclankuCATdata[0]['TEX1nadpis'];
$CATurl = $PARENTclankuCATdata[0]['URLalias'];
return;
}
}
Obdobně je to u funkce, která nasává data nadpisu a odkazu pro kategorii.
function nasajmeziCATdata() {
global $pdo, $meziCAT, $meziCATnazev, $meziCATurl, $meziCATdata;
if (isset($meziCAT)) {
$dotaz = $pdo->prepare("SELECT * FROM CONTENT WHERE CATnazevID=:meziCAT AND ATTR='uvod' LIMIT 1");
$dotaz->bindValue(':meziCAT', $meziCAT);
$dotaz->execute();
$meziCATdata = $dotaz->fetchAll(PDO::FETCH_ASSOC);
if (!empty($meziCATdata[0]['TEX1nadpis'])) {
$meziCATnazev = $meziCATdata[0]['TEX1nadpis'];
}
if (!empty($meziCATdata[0]['URLalias'])) {
$meziCATurl = $meziCATdata[0]['URLalias'];
}
return;
}
}
Vykreslování breadcrumbs
A nakonec samotné vykreslování breadcrumbs pomocí funkce, která navíc ještě dnes, aby toho bylo málo, zohledňuje pokyny Googlu pro Rich text snippets pro zkomfortnění vyhledaných výsledků na googlu: To je další klacek pod nohy tvůrcům webu od Googlu, o tom v samostatném článku.
function vykreslitBREADCRUMBS() {
Jak vidíte funkce vykreslující HTML potřebuje všechna pole dat, které jsme zjistili a získali funkcemi předchozími.
global $parentCLANKU, $meziCAT, $ARTICdata, $CATnazev, $CATurl, $meziCATnazev, $meziCATurl;
//má php je doktor, nemá php není doktor
$path_parts = pathinfo($meziCATurl);
$meziext = $path_parts['extension'];
$path_parts = pathinfo($CATurl);
$catext = $path_parts['extension'];
?>
<ol vocab="https://schema.org/" typeof="BreadcrumbList">
<li property="itemListElement" typeof="ListItem">
<a href="/" property="item" typeof="WebPage"><span property="name">DC</span></a>
<meta property="position" content="1">
</li>
⋙
<?php
Když není mezikategorie prázdná, musí být v breadcrumbs zobrazena jako první, protože je v hierarchii rodičovská ke kategorii článku (parent).
if (!empty($meziCAT)) {
if ($meziext == "php") {
?><li property="itemListElement" typeof="ListItem"><a href = "<?= $meziCATurl ?>" property="item" typeof="WebPage"><span property="name"><?= $meziCATnazev ?></span></a><meta property="position" content="2"></li> ⋙ <?php } else {
?><li property="itemListElement" typeof="ListItem"><a href = "<?= $meziCATurl ?>" property="item" typeof="WebPage"><span property="name"><?= $meziCATnazev ?></span></a><meta property="position" content="2"></li> ⋙ <?php }
} ?>
Jako druhá přijde rodičovská kategorie článku, protože každý článek mého webu náleží do právě jedné kategorie. Jako třetí by byly údaje článku samotného, ale ten nezobrazuji, protože každý vidí jeho nadpis a jeho adresu přímo v prohlížeči.
if (!empty($parentCLANKU)) {
if ($catext == "php") {
?><li property="itemListElement" typeof="ListItem"><a href = "<?= $CATurl ?>" property="item" typeof="WebPage"><span property="name"><?= $CATnazev ?></span></a><meta property="position" content="2"></li> ⋙ <?php }
else {
Tohle je pouze název článku bez odkazu, ale později jsem jej vypustil, protože ho každý zná, když už se něj z internetu dostal.
?><li property="itemListElement" typeof="ListItem"><a href = "<?= $CATurl ?>" property="item" typeof="WebPage"><span property="name"><?= $CATnazev ?></span></a><meta property="position" content="2"></li> ⋙ <?php
}
$ARTICdata[0]['TEX1nadpis']
?></ol>
<?php
}
}
?>
Takže vidíte, že obyčejné breadcrumbs jsou dost krkolomným řešením. Tedy pokud je chcete algoritmem.
Namátkové zdroje
Tady vidíte jeden obstarožní návod z internetu, díky němuž si uvědomíte, že lepší to nebude, a že celý breadcrumbs engine prostě musíte vymyslet sami, šité na míru vašeho webu. Nejlepším se stejně ukazuje, jako jsem to dělal já nebo následující autorka, snažit se pochopit jak fungují breadcrumbs u velkých CMS systémů pomocí jejich návrhu databáze, a podle toho uděláte vlastní řešení brcr.
http://stephanieleary.com/2004/01/absurdly-simple-php-breadcrumbs/
Následující diskusní vlákno na StackOverflow dokazuje, že co člověk to řešení. Také, že problém breadcrumbs je pro nejednoho silnou výzvou. Ale rozhodně vás neodrazuji. Breadcrumbs jsou nutnost! Zejména, když google dneška s nimi počítá i ve svých Rich text snippets! Když se do toho na tři dny ponoříte, rozhodně nakonec zvládnete sebekomplikovanější řešení. Už to, že jste se pustil do programování vlastní webové PHP/SQL aplikace znamená, že to dokážete.
https://stackoverflow.com/questions/2594211/simple-dynamic-breadcrumb
Zde vidíte další prd platné řešení. Navádí nás na myšlenku, že můžeme být klidní, protože ideální většinový přístup k věci neexistuje.
https://blog.sentry.io/2016/05/27/php-breadcrumbs
Sprostě a jednoduše
Nakonec jsem to udělal úplně sprostě tak, že bez algoritmu. Žádná řídící logika. Pouze jsem udělal zvláštní tabulku CATLIST, která je skutečně jako v Excelu a odráží kompletní strukturu webu s přesně vypsanou hierarchií všech kategorií. V PHP potom jednoduše zjistíte pouze parent článku, vyhledáte ho v catlistu a do HTML prostě jen vypíšete posloupnost jeho nadkategorií. Maximálně jednoduché, ale vyžaduje zvláštní tabulku hierarchie kategorií s definicí úplně všech. Pak při každém přidání nové kategorie obsahu ji musíte náležitě doplnit i v catlistu, což je jednou za uherský rok.