14. díl - Kniha návštěv - Zobrazování textů z databáze

PHP Databáze pro začátečníky Kniha návštěv - Zobrazování textů z databáze

(pokračování tohohle textu)

Zobrazování příspěvků je druhá základní funkce návštěvní knihy - bez ní by to nebyla kniha, ale černá díra.

První otázka je, v jakém pořadí chceme vzkazy ukazovat: buď odshora dolů od nejstaršího k nejnovějšímu (což se líp čte, proto se to používá na fórech nebo tam, kde se očekávají hodně dlouhé příspěvky), nebo obráceně (takže bez zdlouhavého rolování rovnou vidíte nejnovější konec diskuse). Dejme tomu, že vzkazy v naší knize budeme řadit od nejnovějšího. Kód pro zobrazení všech vzkazů najednou by mohl vypadat třeba takhle:

$vysledek=mysql_query("SELECT datumvlozeni, autor, obsah FROM nkniha ORDER BY id DESC",$spojeni);
while ($radek=mysql_fetch_array($vysledek)):
  echo '<p><b>'.$radek['autor'].'</b> ';
  echo '('.$radek['datumvlozeni'].')<br>';
  echo $radek['obsah'].'</p>';
endwhile;

To je asi nejjednodušší možná varianta. Má jeden podstatný nedostatek: texty sypeme do HTML kódu stránky tak, jak je uživatelé napsali, a prohlížeči, snaž se. Můžete si být jisti, že se brzy najdou vtipálci, kteří vám knihu zaneřádí v lepším případě blikajícími obrázky přes celou obrazovku (stačí tag <img>) a odkazy na stránky prodávající zaručeně pravé hodinky a zázračné pilulky, v horším případě pak klientskými skripty a aktivními objekty, které si s počítačem návštěvníka mohou dělat, co chtějí. Takže tudy ne, přátelé. Chce to text projít a potenciálně nebezpečné tagy zneškodnit.

První možnost je funkce strip_tags. Ta ze zadaného textu veškeré HTML tagy (včetně komentářů a úseků <?php ... ?>) prostě vymaže. Kdyby se vám to nehodilo, můžete ještě doplnit druhý parametr se seznamem tagů, které se mají zachovat, např.:

$novy_text=strip_tags($puvodni_text,'<br><p><img>');

V takovém případě ale pozor, že u zachovaných tagů zůstanou všechny jejich atributy, i ty potenciálně nebezpečné (onload, onmouseover apod.).

No jo, ale co když nám do knihy někdo bude chtít napsat, že a<b nebo že nemá rád tag <font>? V takovém případě by mu strip_tags smazala půl příspěvku. Naštěstí existuje druhá možnost, funkce htmlspecialchars. Ta aktivní znaky jako <, ", & a podobně přepíše na HTML entity (&lt; &quot; &amp; atd.), takže je potom prohlížeč jednak zobrazí tak, jak byly napsány, a jednak je nebude vyhodnocovat jako HTML kód, takže nám nehrozí žádné skriptové útoky.

Nebezpečného HTML kódu jsme se tedy zbavili, ovšem pisatelé tím přišli o veškeré možnosti formátování, včetně tak základní věci, jako je zalamování řádků. Enter napsaný do textového pole sice vloží do textu znak \n (nový řádek), ale ten se v HTML projeví jenom jako mezera. Naštěstí i na tohle máme chytrou funkci, nl2br, která před všechny znaky \n vloží tagy <br />. Kdybyste chtěli klasický tvar <br>, musíte přidat druhý parametr s hodnotou false:

$novy_text=nl2br($puvodni_text,false);

Při true nebo při vynechaném druhém parametru se vkládá varianta s lomítkem.

To je sice docela hezké, ale poněkud jednoúčelové. Co kdybychom chtěli nahrazovat úseky textu nějak obecněji? Třeba textového smajlíka ":-)" grafickým "<img src="smajlik1­.gif">"? S tím si poradí funkce str_replace(co,čím,kd­e):

$novy_text=str_replace(':-)','<img src="smajlik1.gif">',$puvodni_text);

Str_replace je velice užitečná věc, která určitě najde uplatnění i v mnoha jiných aplikacích.

Poslední věc, která by se nám mohla nelíbit, je datum. Pokud ho zobrazíme tak, jak nám přijde z databáze, bude vypadat nějak jako 2012-08-10. To je dobré leda tak pro Američany, my bychom radši 10. 8. 2012. Na formátování data má SQL funkci DATE_FORMAT, pro uvedený tvar data by vypadala takhle:

SELECT DATE_FORMAT(datumvlozeni,'%e. %c. %Y') FROM nkniha

První parametr je datum ke zformátování, druhý je požadovaný tvar. Každý kód %+písmeno má nějaký význam (třeba "číslo dne", "číslo měsíce bez úvodní nuly" apod.), kompletní seznam najdete v manuálu. Ostatní znaky (např. ty tečky a mezery) se zobrazí tak, jak jsou. Výsledkem funkce je textový řetězec.

No jo, jenže jak se ten výsledek bude jmenovat, čili jaký index máme použít ve funkcích mysql_fetch_array nebo assoc? Upřímně řečeno, nevím a ani to vědět nepotřebuju. V SQL se totiž dá použít alias, tj. předefinovat jméno libovolného sloupce. Dělá se to klíčovým slovem AS (česky "jako") a v našem případě by to mohlo vypadat třeba takhle:

$vysledek=mysql_query("SELECT DATE_FORMAT(datumvlozeni,'%e. %c. %Y') AS upravenedatum, autor, obsah FROM nkniha ORDER BY id DESC",$spojeni);
$radek=mysql_fetch_array($vysledek);
echo $radek['upravenedatum'];

Aliasy jsou docela užitečná věcička - jak u takovýchto funkcí s blíže neurčeným názvem výsledku, tak např. u výběru dat z více tabulek současně, kde se může sejít několik sloupců se stejným jménem (typicky třeba id) a je potřeba je nějak odlišit. Ale o tom si povíme jindy.

Ale dost už vylepšování textů, nebo nám z nich nic nezbyde. Další důležitou otázkou je, kolik příspěvků najednou chceme ukázat. Vypisovat všechny by nebylo moc praktické - čím víc jich bude, tím déle by se kniha načítala a v půl kilometru vysoké stránce by se stejně nikdo nevyznal. Takže to raději nějak omezíme, nejlépe stránkováním po určitém počtu vzkazů, a přidáme nějaké ovládací prvky, kterými si návštěvník může nalistovat všechno, co bude chtít.

Stránkování zobrazených příspěvků

Předpokládám, že na další stránky nás dostane nějaký odkaz "zobraz starší/novější vzkazy", čili nějaká proměnná předaná metodou GET (POSTový formulář s jedním tlačítkem by teoreticky šel taky, ale neprošli by přes něj vyhledávací roboti). Na určení polohy by se dala použít přímo položka id, ale předpokládám, že vzkazy budeme občas i mazat, takže některé hodnoty id přestanou existovat a byly by z toho potíže. Takže si zavedeme hodnotu "číslo stránky". Dejme tomu, že deset nejnovějších vzkazů bude na stránce 0, dalších 10 bude na stránce 1 atd., až k tomu úplně nejstaršímu. V SQL se výběr provede omezením Selectu pomocí klíčového slova LIMIT:

SELECT * FROM tabulka LIMIT x,n

Tím se z tabulky vybere n řádků, počínaje od xtého (x se počítá od nuly). Dá se to kombinovat i s dalšími omezujícími podmínkami, jako třeba WHERE (v takovém případě Limit ořezává až to, co po Where zbyde). V našem konkrétním případě tedy může příkaz vypadat např. takto:

$vysledek=mysql_query("SELECT * FROM nkniha ORDER BY id DESC LIMIT ".(10)*$stranka.",10",$spojeni);

Na stránce 0 se vybere deset nejnovějších. Na stránce 1 se těch deset nejnovějších přeskočí a vybere se dalších 10 od jedenáctého dál a tak dále. Číslo 10 je v závorce proto, že tečka jako operátor zřetězení má hodně malou prioritu, takže by si ji PHP vyložilo jako desetinnou tečku (tedy číslo 0.10) a skript by nefungoval. V praxi je každopádně výhodnější místo čísla použít proměnnou, abychom kvůli každé změně délky stránky nemuseli upravovat čísla rozházená po celém zdrojáku.

Pozn.: Možná vás napadlo, že bychom stejně dobře mohli z databáze vzít všechno a výsledky si probrat až v PHP v nějakém cyklu. Ano, teoreticky by to samozřejmě šlo, ale bylo by to výrazně pomalejší a náročnější na paměť. Zatímco SQL stačí jednou si přečíst zadaný příkaz, sáhnout do databáze a za pár milisekund vám vrátí požadovaný očesaný výběr, PHP musí v každém průchodu očesávacím cyklem znovu a znovu interpretovat každý řádek. V PHP obecně platí, že je vždycky výhodnější používat standardní funkce nebo pečlivě nakombinované databázové příkazy, než skládat složité algoritmy a cykly z jednoduchých příkazů.

Stránkovaný výběr tedy umíme, ale ještě se potřebujeme nějak dostat k hodnotě $stranka.

Dejme tomu, že výchozí stav bude stránka 0 - tu zobrazíme, pokud žádné číslo nepřijde nebo pokud místo čísla přijde nějaký nesmysl (ať už neúmyslný překlep nebo pokus o SQL injekci). Pokud nějaké platné číslo přijde, použijeme ho. Třeba takhle:

if (isset($_GET['stranka'])           //je nějaké číslo zadané?
    and is_numeric($_GET['stranka'])  //a je to opravdu číslo?
    and ($_GET['stranka']>=0))        //a je nezáporné?
  $stranka=$_GET['stranka'];        //ano, použijeme ho
  else $stranka=0;                  //ne, použijeme výchozí stránku

Jakmile známe aktuální stránku, můžeme sestavit stránkovací odkazy:

echo '<a href="nkniha.php?stranka='.$stranka-1.'">Novější příspěvky</a>';
echo '<a href="nkniha.php?stranka='.$stranka+1.'">Starší příspěvky</a>';

Samozřejmě bude potřeba umístit je na nějaké vhodné místo. Kam, to je věc názoru. Já osobně nemám rád, když mají podobu šipek doleva a doprava nebo slov "předchozí" a "další". Vzkazy v knize jsou poskládané svisle, tak proč by se mělo najednou listovat vodorovně? A slovem "předchozí" se myslí předchozí (dříve napsané, tedy starší) vzkazy, nebo předchozí (dříve navštívená, tedy novější) stránka? Ale to už je jenom kosmetický detail.

Docela příjemná vychytávka může být to, že odkaz nahoru skočí na spodní konec novější stránky (odkazem s #kotvou), což čtenářům ušetří mačkání Endu (stejně by četli odspoda). Také není od věci odkaz na novější stránku nezobrazovat, pokud jsme na stránce 0. Obdobně můžeme naopak nezobrazit odesílací formulář, pokud na stránce 0 nejsme. O něco těžší by bylo nezobrazit odkaz na starší stránku, pokud jsme úplně na začátku knihy. Dále můžeme přidat odkazy úplně na začátek a úplně na konec. Trefit se na stránku 0 je triviální, číslo poslední stránky by se vypočítalo z celkového počtu vzkazů v knize. Ten by se zjistil takto:

SELECT COUNT(*) FROM nkniha

Příkaz nám vrátí jednoprvkovou návratovou tabulku, ze které si příslušné číslo vytáhneme třeba přes mysql_fetch_row.

Nyní tedy víme, jak ukládat vzkazy do databáze a jak je z ní vybírat a zobrazovat. Zbývá poslední kapitola: obrana proti spambotům.


 

  Aktivity (1)

Článek pro vás napsal Mircosoft
Avatar
Autor je amatérský pascalista, assemblerista a bastlíř. Profesionálně psal nebo píše v HLASM, Rexxu, Cobolu, ST, LAD, FBD, PHP, SQL, JS, Basicu a pár dalších jazycích, které kupodivu stále existují a používají se :-).

Jak se ti líbí článek?
Celkem (4 hlasů) :
55555


 



 

 

Komentáře
Zobrazit starší komentáře (3)

Avatar
David Čápka
Tým ITnetwork
Avatar
David Čápka:

K seriálu dodáváme postupně i zdrojové kódy, potom si to můžeš porovnat :)

Odpovědět 12.9.2012 15:42
Miluji svou práci a zdejší komunitu, baví mě se rozvíjet, děkuji každému členovi za to, že zde působí.
Avatar
kubp
Člen
Avatar
kubp:

Můj kód:

$vysledek=mysql_query("SELECT * FROM clanky ORDER BY id DESC LIMIT ".10*$stranka.",10",$spojeni);

Parse error: syntax error, unexpected '.10' (T_DNUMBER) in C:\xampp\htdoc­s\neco\index.php on line 81

Netuším proč mám error.

 
Odpovědět 13.3.2013 21:56
Avatar
Mircosoft
Redaktor
Avatar
Mircosoft:

Zajímavé, tohle jsem nevěděl. Tečku vedle čísla interpret pochopil jako desetinnou tečku, ne jako operátor zřetězení. Dej desítku do závorky, pak to bude fungovat.
Díky za upozornění, musím to v článku opravit.

 
Odpovědět 14.3.2013 10:09
Avatar
Kit
Redaktor
Avatar
Odpovídá na Mircosoft
Kit:

Skutečně zajímavé. Možná by stačilo dát za tečku mezeru, ale i tak je to divočina. Tyhle implicitní konverze v PHP neměly být.

Odpovědět 14.3.2013 10:14
Vlastnosti objektů by neměly být veřejné. A to ani prostřednictvím getterů/setterů.
Avatar
kubp
Člen
Avatar
kubp:

Děkuju, už vše funguje.

 
Odpovědět 14.3.2013 16:14
Avatar
MarweXtv
Člen
Avatar
Odpovídá na kubp
MarweXtv:

Php není zrovna jednoduchý tak si to máš přečíst znova popř. přepsat :)

 
Odpovědět 20.3.2013 15:19
Avatar
Jiří Gracík
Redaktor
Avatar
Jiří Gracík:

Ahoj, není to asi zásadní problém, ale možná by bylo fajn dopsat nahoru že je to 13. díl :) Přecházel jsem sem z 14. dílu, přejdu sem, aha, to je asi začátek, pak znova vidím pokračování tohoto dílu ... oprav to prosím, nám slabším jedincům to nedochází :D

Odpovědět  +1 7.5.2013 18:54
Creating websites is awesome till you see the result in another browser ...
Avatar
David Čápka
Tým ITnetwork
Avatar
Odpovědět 7.5.2013 19:15
Miluji svou práci a zdejší komunitu, baví mě se rozvíjet, děkuji každému členovi za to, že zde působí.
Avatar
loading84
Člen
Avatar
loading84:

Mi nefunguje tohle.

echo '<a href="nkniha.php?stranka='.$stranka-1.'">Novější příspěvky</a>';
echo '<a href="nkniha.php?stranka='.$stranka+1.'">Starší příspěvky</a>';
 
Odpovědět 10.10.2015 16:27
Avatar
loading84
Člen
Avatar
loading84:

Hergot chlape.... maš tam chybu... takhle to je spravně

echo '<a href="index.php?stranka='.($stranka-1).'">Novější příspěvky</a>';

echo '<a href="index.php?stranka='.($stranka+1).'">Starší příspěvky</a>';

se zavorkama!!!!!!!!!!­!!

 
Odpovědět  +1 10.10.2015 16:55
Děláme co je v našich silách, aby byly zdejší diskuze co nejkvalitnější. Proto do nich také mohou přispívat pouze registrovaní členové. Pro zapojení do diskuze se přihlas. Pokud ještě nemáš účet, zaregistruj se, je to zdarma.

Zobrazeno 10 zpráv z 13. Zobrazit vše