CW(1,12A) - Multiarticle(1) - Dělení článků
Kolem roku 2000 se s prvními redakčními systémy (CMS v PHP a SQL) a s velkými novinářskými servery začaly objevovat dělené články. Internet u nás byl v plenkách a v západním světě jen o trošičku starší.
Stran návštěvnosti, reklam a ekonomického potenciálu webů se zjistilo, že jeden obsáhlý článek třeba na 10-20 stránek A4, který je třeba recenzí a porovnáním kusů hardwaru se všemi výkonnostními grafy a analýzami, je tak obří, že ho nakonec nikdo celý nepřečte. Trvalo by mu to přes dvě hodiny.
Proto vzniklo dělení článků, které je však automatizované. Štěpí článek do třeba třinácti stravitelnějších článků, přičemž smysluplné rozsekání textu je naprogramováno v PHP. Pro následování smyslu věci je vhodné vzít za kritérium automatického dělení společné podtéma, např. nadpisy a odstavce dílčího podtématu dát vždy k sobě a z nich udělat jednu podstránku. Dá se to dělit čistě podle počtu slov, což je velmi jednoduché a minimálně pracné, pro čtenáře však matoucí a když čtenář takový web prokoukne, má to rozhodně odpuzující účinek. Text tedy není dělen podle konstantního počtu znaků na stránku, ale např. podle počtu nadpisů odstavců sdružujícího tématu, kdy odstavce jsou zhruba stejně dlouhé, stejně jako obrázky v textu jsou zhruba stejně veliké a jejich počet je zhruba konstantní. PHP je tak mocné, že dokáže navíc vygenerovat klikatelnou osnovu, která zobrazuje jednotlivé části multičlánku jako odkazy, z nichž pro každou vygeneruje samostatnou url, například z názvu článku lomítko číslovka pořadí (/nazev-clanku/4). Tato osnova se nalézala zpravidla v horním pravém rohu nad textem vedle nadpisu stránky. Autor textu se s touto programovou částí nemusí párat, nemusí svůj dlouhý text ručně rozdělovat, prostě ho jen publikuje. Naprogramovaný PHP doplněk (modul, plugin) už má předpřipravený. Engine pro multičlánek ho sám rozpozná a rozseká do systému adres, které však fyzicky v databázi redakčního systému neexistují. Jsou to virtuálně existující, automaticky vygenerované adresy, lišící se pouze číslovkou na konci.
CMS řešení
Weby takto vybavené šly rychle nahoru, např. TomsHardware.com nebo Živě.cz, až fenomén Article splittingu zachvátil celý internet. Teprve když s tím přišly ve velmi komfortní pokročilé podobě CMS systémy let dvoutisících, jako Mambo, později dnešní Joomla, stala se tato funkce dostupná i pisatelům-neprogramátorům.
Joomla donedávna, pravděpodobně do dneška (2020) používala article splitting přímo v textu v editoru, kam uživatel vkládá vodorovnou čáru <HR> a Joomla už ví, že články takto vybavené musí rozdělit do většího počtu menších očíslovaných článků podle počtu vodorovných čar a vygeneruje k nim vlastní odkazy.
Moje PHP/XML/XPath řešení
V mém PHP webovém enginu uvádím tuto funkci na posledních místech, protože se jedná o problém zdaleka nejsofistikovanější. Podle Joomly již víme, že musíme články vybavit nějakým množstvím separátorů, na něž potom skript reaguje. Jaké separátory to budou, jestli budou obsahovat nějaké metainformace a jestli budou tvořeny párovými tagy, neřkuli jak je vůbec bude vaše aplikace rozpoznávat, to chce nejdříve za humny dobře promyslet.
Princip
U mého webu stejně jako u světových CMS (content management systémů) vlastní textový obsah je HTML kódem uloženým v databázi. Vaše databázová tabulka Content je prostě plná HTML - holý text prosycený HTML značkami. HTML je v redakčních systémech s pokročilými vlastními editory textu buď získáno automaticky přímo editací v administrativní části CMS, nebo je produktem nějakého skvělého wysiwyg programu (Dreamweaver) na lokálním počítači a do CMS je vloženo ručně přes editor databáze. Pak je tu třetí nejhorší možnost, že jak text tak HTML tagy jsou psány ručně v jakémkoli textovém editoru, ale to snad již dnes nikdo nedělá. Leda nějaký předpotopní masochista zastydlý v 90. letech.
HTML je vlastně XML
Ať tak či onak, HTML je podmnožinou XHTML a dá se na něj dívat jako na zvláštní případ XML (Extensible Markup Language) a to je přesně to, co budeme potřebovat. Vytáhnout kompletní HTML řetězec megačlánku z databáze a podívat se na něj programaticky jako na XML objekt. Jelikož textové elementy jako odstavce, nadpisy, odkazy, obrázky a všechno ostatní jsou obaleny svými přesně nadefinovanými a standardizovanými značkami, dokument je tak už krásně předstrukturován. Říká se tomu document object model DOM. Už nám svitlo, že? Na strojové čtení (parsování) dokumentu, filtrování a úpravy strukturovaného textu slouží jakýsi koncept XPath: A už jsme doma, to přece známe z úplných počátků našich experimentů s Javascriptem. XPath vidí jakýkoli XML (nebo HTML) text jako sestavu přesných pravidel a dokáže s nimi něco dělat. XPáth je hlavním konceptem, kvůli kterému vůbec strukturované XML texty existují. Text splňující pravidla XML se pak dá vlastně použít jako relační databáze, výpisům z DB a přepisům mezi databázemi XML také slouží.
PHP a XPáth
V naší PHP aplikaci se podíváme na kus jakéhokoli sebevětšího HTML textu, jako na strukturovaný dokument (PHP naštěstí má svoje funkce pro XPath). A už můžeme například vyfiltrovat všechny nadpisy úrovně tři <H3> a vypsat z nich osnovu, spočítat jejich počet a pořadí každého a vygenerovat mu vlastní část URL a propojit přesně vymezenou část obsahu s tímto (virtuálním) linkem. To je celý princip. Už to jen naprogramovat.
Weboví předpotopní stařešinové vám jistě namítnou, že XML je zbytečně složité a že to v PHP můžeme udělat přes holý text, a článek rozsekat na části s přesně definovaným počtem znaků nebo slov. Ano, šlo by to, ale je to řešení jako z doby kamenné. Jakmile tam máte nějaké obrázky, nadpisy a popisy obrázků nebo CSS pozicované rámce, PHP vezme zadaný počet slov a za nimi to usekne. Nehledí na to, že tím např. odtrhlo obraz od popisu, či přepůlilo nadpis nebo roztrhlo přímou řeč. PHP řešení s XPath je daleko elegantnější a dá se jimi text rozdělit třeba až po skončení odstavce. Zjistí se, jestli k němu ještě nepatří obrázek a k němu popis a teprve s tímto zohledněním roztrhne text až před začátkem druhého odstavce, přesně jako u kapitol v dobré knize. Proto se taky děleným článkům začalo říkat kapitoly.
Takže princip známe, teď už jen musíme sednout ke Googlu a najít, jak PHP pracuje s XPath.
A konečně řešení
<?php
//************DOM*xml***************
$dom = new domDocument;
Definujeme proměnnou DOM typu objekt a do něj načteme celý text obrovského článku určeného k rozdělení.
Ošetření, aby PHP zbytečně nevyhazovalo tolik warnings, které kolem čtení XML rozhodně budou, a nepůsobilo tím paniku.
libxml_use_internal_errors(true);
Toto je v PHP postup, jakým načtu jakýkoli text prosycený tagy, jako strukturovaný objekt HTML, což je speciálním případem XML a proto v něm funguje XPath. Nastavuje se tam kódování na dnešní UTF-8, protože bez tohoto nastavení by se vám z českých znaků udělala ASCII hatmatilka.
$dom->loadHTML('<?xml encoding="utf-8" ?>' . $ARTICdata[0]['TEX3obsah']);
Načetl jsem databázové pole TEX3obsah už někde v dřívějším skriptu do PHP proměnné, na kterou už se odteď budu dívat jako na strukturovaný document object model (DOM).
Toto jsou další dvě ošetření, jak PHP bude pracovat s tímto textem a aby nenačítalo místa s mnoha mezerami a tabulátory najednou, což se nám hodí i pro vyčištění kódu. Takovýchto upřesňujících parametrů si sami najdete a přidáte ještě mraky dalších.
libxml_clear_errors();
$dom->preserveWhiteSpace = FALSE;
A už se práší za kočárem
Zavádím proměnnou xpath a plním novým XPath objektem na základě DOMu načteného z databáze. Toto je vlastně stěžejní řádek:
$xpath = new DOMXpath($dom);
Dále už funkce, které prohledávají strukturu XHTML. Ty si nastudujte a vyzkoušejte z manuálu PHP. Ono to s nimi není zase tak jednoduché. Jednoduché ano, ale je tam plno příležitostí, kde nadělat chyby. Navíc logika procházení XHTML značkami je zcela oddělená od ukládání nebo zapisování do DOMu, takže si to nejdříve nacvičte na příkladech, pomocí. Konkrétně funkce getElements jsou pěkně ošemetné.
$section = $dom->getElementsByTagName('section')->item(0);
Znova zdůrazňuji, protože s tím jsem se natrápil tři dny: Orientace a procházení XML řetězcem vůbec nemá co do činění s osekáváním a vyjímáním kusů jeho obsahu. Jsou to dvě samostatné věci. V první řadě pouze přesně definujete kýženou pozici v DOM a teprve na konci programu se staráte o reálný výpis těchto prvků. Celou dobu se vlastně nezabýváte reálným obsahem XML textu, ale pouze lítáte v oblacích virtuálně existujícího DOM. Obsah bude existovat až na konci, výsledkem funkce saveHTML().
Logika dělení
Podle vzoru Joomly jsem také určil vlastní prvek jako separátor, ovšem na rozdíl od nepárového tagu <hr> v Joomle, používám párový tag <section></section>. Ono v tom XML se s tím snadněji pracuje. Nemusíte totiž vypisovat obsah před HR nebo po HR nebo mezi dvěma danými HR, ale vypíšete prostě obsah dané Section. A co jsem se díval na nejposlednější Joomlu, také nakonec zavedla párový tag.
Metaatribut description
Někdy dávám i metaatribut popisu subčlánku <section desc="Popis tohoto oddílu"></section> což se někdy hodí při generování webu.
Pokud daná kapitola má section description, použiji ho přednostně. Pokud ne, použiji pro kapitolu METAdescription ze sloupce v databázi, který je výchozí (shodný) pro všechny kapitoly jednoho multičlánku.
Nejdříve mám celý dlouhý multičlánek načten z databáze jako jeden řetězec HTML. Dále v něm hledám všechny výskyty párového tagu <section></section> a spočítám jejich počet.
$query = 'count(//section)';
$pocetvyskytu = $xpath->evaluate($query, $section);
Pokud má daná sekce náhodou vyplněn mnou definovaný HTML atribut @desc (description), pak ho vyjímám z XML(HTML) řetězce a dále s ním něco dělám: Např. dávám jako metatag description do hlavičky stránky nebo vyplňuji odkazy rel a next.
PHP proměnná permalink2 je v tomto případě číslovka pořadí konkrétní části článku, jak jsme si deklarovali v Article routeru. Tu jsem hned na začátku vyjmul z URL a teď ji mám jako PHP proměnnou dostupnou všem skriptům.
$SectDesc = $xpath->evaluate('string(//section[' . $permalink2 . ']/@desc)', $section);
//vyhodnotil jsem section description
Následuje druhý díl 112B.
Zdroje
PHP Manual. XML Manipulation.
https://www.php.net/manual/en/book.dom.php
Xpath cheatsheet. Devhints.io.
https://devhints.io/xpath