Pouze tento týden sleva až 80 % na e-learning týkající se C# .NET
Nauč se s námi víc. Využij 50% zdarma na e-learningové kurzy.
discount week 50

Lekce 5 - První objektová komponenta v PHP - Galerie obrázků

V minulé lekci, Zapouzdření v PHP, jsme si vysvětlili zapouzdření.

Na dnešek si vytvoříme galerii obrázků.

Motivace

Často chceme na našich stránkách zobrazit nějakou galerii obrázků, ať již se jedná o fotky z dovolené nebo o screenshoty našeho programu. Psát ručně HTML tabulku s obrázky je poněkud pracné, hlavně když jich je v dané složce hodně. Budeme tedy chtít zautomatizovat výpis obrázků z nějaké složky do přehledné tabulky a nejlépe pomocí objektové komponenty, kterou budeme moci používat vícekrát a třeba i na více webech.

Založení struktury projektu

Vytvoříme si novou složku pro projekt, já jsem ji pojmenoval "galerie". V ní vytvoříme soubor index.php a složku "tridy", ve které vytvoříme soubor Galerie.php. Nakonec vytvoříme složku s obrázky, tu jsem pojmenoval jednoduše "obrazky". Do ní si připravíme několik obrázků, já jsem použil ty výchozí "Ukázky obrázků" z operačního systému Windows. Ke každému obrázku je nutné vytvořit ještě jeho miniaturu (třeba v GIMPu). Mohlo by to za nás dělat PHP při nahrávání obrázků, ale nahrávání již není předmětem tohoto tutoriálu a můžeme ho dodělat někdy jindy. Zvolil jsem si, že miniatura bude široká 160px a její název bude vždy končit na _nahled. Vaše složka by mohla vypadat třeba nějak takto:

Složka s obrázky pro PHP Galerii

Galerie

Máme připravená data a strukturu, pusťme se do programování. Začneme samozřejmě třídou Galerie, jejíž instance bude reprezentovat galerii obrázků. Pojďme si promyslet, jaké atributy bude třída mít.

Atributy

Budeme chtít určit kolik náhledů se má zobrazit v jednom řádku, tedy kolik sloupců bude mít tabulka s obrázky. Dále budeme chtít samozřejmě uchovávat cestu ke složce s obrázky. Ani jeden atribut není důvod zpřístupňovat zvenčí, stačí, když si je necháme zadat v konstruktoru.

Kód třídy s těmito atributy a konstruktorem bude vypadat následovně:

class Galerie
{

    private $slozka;
    private $sloupcu;

    public function __construct($slozka, $sloupcu)
    {
        $this->slozka = $slozka;
        $this->sloupcu = $sloupcu;
    }

}

Metody

Přejděme k metodám. Jednu ve třídě již samozřejmě máme (konstruktor), přidáme tam 2 další.

Nacti

Tento výukový obsah pomáhají rozvíjet následující firmy, které dost možná hledají právě tebe!

Metoda nacti() prohledá složku a uloží si do paměti náhledy obrázků. K prohledání složky nám PHP nabízí třídu Directory. Vzpomínáte si, jak jsme si říkali, že se v PHP začínají objevovat objekty? Directory je jedním z nich. Bohužel lze instanci vytvořit jen pomocí funkce dir(), no, nic není dokonalé. Na instanci nás zajímají 2 metody:

  • read() - Načte obsah dalšího souboru nebo složky ve složce a vrátí ho jako textový řetězec
  • close() - Ukončí čtení složky

Naše metoda nacti() by mohla vypadat prozatím takto:

public function nacti()
{
    $slozka = dir($this->slozka);

    while ($polozka = $slozka->read())
    {

    }
    $slozka->close();
}

Vytvoříme si instanci Directory a pomocí metody read() čteme postupně složky a soubory v této složce. Metoda vrátí false v případě, že jsme se prokousali až na "konec" složky (za poslední soubor nebo složku). Díky tomu můžeme dát načítání názvu souboru do while cyklu, který skončí ve chvíli, kdy načteme poslední. Název budeme mít uvnitř cyklu dostupný v proměnné $polozka. Možná je trochu nezvyklé, že v podmínce while cyklu provádíme rovnou i přiřazení do proměnné, ale dělá se to tak velmi často.

Každý soubor nyní načítáme 2x (miniatura + originál) a k tomu nám funkce ještě vrátí vždy 2 složky navíc. Jsou to složky s názvem "." a "..", které označují současnou a nadřazenou složku. My budeme chtít zobrazovat jen miniatury, proto do cyklu umístíme podmínku, že nás zajímají pouze soubory, obsahující v názvu "_nahled.". Tyto soubory si uložíme do pole. Jelikož chceme, aby bylo toto pole přístupné pro metodu vypis(), musíme ho přidat třídě jako privátní atribut:

private $soubory = array();

Nyní se přesuňme do těla cyklu while a přidejme název souboru do našeho pole pokaždé, když se jedná o náhled. Použijeme k tomu funkci strpos(), která vrací pozici podřetězce v řětězci. Pokud se v názvu položky vyskytuje od druhého znaku někde text "_nahled.", bude položka přidána do našeho pole. Pokud ne, funkce vrátí false.

if (strpos($polozka, '_nahled.'))
{
    $this->soubory[] = $polozka;
}

*Funkce strpos() vrátí 0 v případě, že řetězec podřetězcem začíná (protože 0 označuje první pozici). V našem případě by se hodnota 0 vyhodnotila jako nepravda a podmínka by neplatila. To je správně, soubor _nahled.jpg by se vázal k obrázku bez názvu a není tedy platný. Pokud bychom však chtěli opodmínkovat kompletně výskyt podřetězce, třeba v jiné aplikaci, používá se k odlišení 0 a hodnoty false operátor !== takto: *

if (strpos($polozka, '_nahled.') !== false)
{
    $this->soubory[] = $polozka;
}

Nyní podmínka neprojde pouze s hodnotou false a projde s hodnotou 0. V našem případě se nám však spíše hodí první varianta.

Vypis

Tu složitější část máme za sebou, přesuňme se nyní k výpisu HTML tabulky, tedy k metodě vypis(). I když by načtení i výpis mohly být v jedné metodě, jedná se o 2 logické úlohy a proto je lepší je rozdělit do 2 metod. Metoda by měla dělat vždy jednu věc a měli bychom ji být schopni popsat bez spojky a. Tedy "Metoda zobraz() zobrazí HTML tabulku s náhledy", nikoli "Metoda zobraz() načte a zobrazí galerii".

Metoda vypis() vyechuje začátek tabulky. Dále proiterujeme cyklem foreach miniatury a ty vyechujeme do buňky tabulky jako tag img. Kolem tagu img samozřejmě uděláme odkaz na originální obrázek. Ten získáme velmi jednoduše, stačí v názvu miniatury nahradit "_nahled." za ".". Nahrazení provedeme funkcí str_replace(). Aby nebyla celá tabulka v jednom řádku, budeme si počítat kolikátý sloupec vypisujeme. Jakmile dosáhneme hodnoty nastavené v atributu $sloupcu, uzavřeme řádek, začneme nový a vynulujeme počítadlo sloupců.

Kód metody vypis() by mohl vypadat nějak takto:

public function vypis()
{
    echo('<table id="galerie"><tr>');
    $sloupec = 0;
    foreach ($this->soubory as $soubor)
    {
        $nahled = $this->slozka . '/' . $soubor;
        $obrazek = $this->slozka . '/' . str_replace('_nahled.', '.', $soubor);
        echo('<td><a href="' . htmlspecialchars($obrazek) . '"><img src="' . htmlspecialchars($nahled) . '" alt=""></a></td>');
        $sloupec++;
        if ($sloupec >= $this->sloupcu)
        {
            echo('</tr><tr>');
            $sloupec = 0;
        }
    }
    echo('</tr></table>');
}

Použití

Přesuneme se do index.php, vložíme sem základní HTML strukturu a vytvoříme instanci galerie. Do konstruktoru předáme složku a počet sloupců. Následně zavoláme nacti() a vypis().

<!DOCTYPE html>

<html lang="cs-cz">
    <head>
            <meta charset="utf-8" />
            <title>Galerie obrázků</title>
    </head>

    <body>
        <h1>Galerie obrázků</h1>
        <?php
        require_once('tridy/Galerie.php');

        $galerie = new Galerie('obrazky', 5);
        $galerie->nacti();
        $galerie->vypis();

        ?>
    </body>
</html>

Máme hotovo.

Když přidáme minimum CSS, může vaše aplikace vypadat třeba takto (zdrojový kód jako vždy níže ke stažení, k zobrazování originální obrázků jsem ještě přidal JavaScriptový plugin LightBox):

Objektová galerie obrázků v PHP

Možná vylepšení

Metoda vypis() není úplně ideální, jelikož se HTML kód echuje a proto je práce s ním poněkud nepřehledná. Dalším, již zmíněným vylepšením, by mohl být nahrávač obrázků, který nám na FTP nahraje obrázky do galerie a sám vytvoří miniatury. Pro naše účely je však aplikace dostačující a myslím, že má i tak praktické využití.

Naprogramovali jsme objektovou komponentu, kterou můžeme používat na různých webech, pro různé složky a na různě široké tabulky.

V příští lekci, Referenční a primitivní datové typy v PHP, si vysvětlíme referenční datové typy v PHP.


 

Měl jsi s čímkoli problém? Stáhni si vzorovou aplikaci níže a porovnej ji se svým projektem, chybu tak snadno najdeš.

Stáhnout

Stažením následujícího souboru souhlasíš s licenčními podmínkami

Staženo 2946x (6.54 MB)
Aplikace je včetně zdrojových kódů v jazyce PHP

 

Předchozí článek
Zapouzdření v PHP
Všechny články v sekci
Objektově orientované programování (OOP) v PHP
Článek pro vás napsal David Čápka
Avatar
Jak se ti líbí článek?
47 hlasů
David je zakladatelem ITnetwork a programování se profesionálně věnuje 13 let. Má rád Nirvanu, sushi a svobodu podnikání.
Unicorn university David se informační technologie naučil na Unicorn University - prestižní soukromé vysoké škole IT a ekonomie.
Aktivity

 

 

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

Avatar
Jiří Pecher:18.10.2019 20:40

Ahoj, výraz "vyechuje" je mi jasný :-) Co však prosím znamená výraz "proiterujeme"? ;-)

Editováno 18.10.2019 20:40
Odpovědět
18.10.2019 20:40
"Když nemám co dělat, pracuji."
Avatar
Odpovídá na Jiří Pecher
Michal Šmahel:18.10.2019 23:30

Ahoj, vychází to z iterátoru. Více si můžeš přečíst různě po internetu (např. tady). Úplně zjednodušeně se jedná o projití nějaké kolekce (v PHP je kolekcí vlastně i klasické pole) prvek po prvku. V PHP se k tomu často používá cyklus foreach (jako např. i v článku).


Když by ses chtěl dozvědět trochu více: Iterátor je návrhový vzor, který ti zajišťuje možnost "jednoduchého" projití nějaké kolekce (v PHP pole, jinak různé seznamy, množiny apod.), aniž bys musel složitě řešit, jak je tato kolekce uvnitř vlastně řešena. Iterátor zajistí určité rozhraní, díky němuž lze kolekci vložit do běžného foreach cyklu, který s ním díky jeho implementaci dokáže pracovat jako např. s klasickým PHP polem. Opět velmi zjednodušeno, ale myslím, že nemá smysl to více rozebírat. Prozatím je to jistě až až, v případě zájmu samozřejmě můžeš pročíst internet. Bez dalších podkladů a znalosti rozhraní a obecné teorie kolem návrhových vzorů to však nemá příliš smysl nějak hlouběji zkoumat.

Odpovědět
18.10.2019 23:30
Nejdůležitější je motivace, ovšem musí být doprovázena činy.
Avatar
Dávid Dopirák:21.11.2020 16:40

Ahoj,
Chcel by som vedieť, ako ináč zapísať:

while($polozka = $zlozkaNacitaj->read())

Skúšal som to zapísať:

$polozka = $zlozkaNacitaj->read();

       while($polozka) ...

ale pak mi to vôbec nechce načítať. Pýtam sa na základe toho čo je napísané tu:

Možná je trochu nezvyklé, že v podmínce while cyklu provádíme rovnou i přiřazení do proměnné, ale dělá se to tak velmi často.

Díky

 
Odpovědět
21.11.2020 16:40
Avatar
Milan Turyna
Redaktor
Avatar
Odpovídá na Dávid Dopirák
Milan Turyna:21.11.2020 18:45

Nejsem na pocitaci takze ti neposlu ukazkovy kod, kazdopadne cyklus while funguje na principu ze bude vykonávat určitou akci dokud podminka bude platit, v momentalnim pripade dokud $polozka bude true. While cyklus nepouzivam kazdopadne metoda pravděpodobne vraci dalsi hodnotu z nejakeho seznamu/pole/re­sultsetu a kdybys promennou nastavil jiz pred cyklem tak by se cyklus provedl pouze jednou protoze by se hodnota promenne nemenila a po jednom opakovani by byla false.

Pokud ji ovsem nastavime ve while() tak metoda read pokazde zmeni obsah promenne a presune offset na dalsi element - dalsi radek, az se proiteruji vsechny radky, tak uz nebude co vracet a cyklus se skonci protoze promenna uz nebude true.

Neumim moc vysvetlovat teorii, radsi bych ti to popsal v praxi na kodu.

https://www.itnetwork.cz/…-for-a-while

Fungovat by tez mohlo:

$polozka = $xxx->read();
$i=0;
while ($polozka) {
   $polozka = $xxx->read();
   echo $i++;
}
//1
//2
//3
//4
//5
// az do celkoveho poctu elementu
Editováno 21.11.2020 18:46
 
Odpovědět
21.11.2020 18:45
Avatar
Odpovídá na Milan Turyna
Dávid Dopirák:21.11.2020 22:47

Chápem to teda správne, že v mojom prípade som celú podmienku zadal akoby mimo cyklu ? A preto mi "nekonečný" cyklus while po 2 minútach vypíše hlášku??

Fatal error: Maximum execution time of 120 seconds exceeded in D:\xampp\htdoc­s\triedyOOP\tri­edy\Galeria.php on line 32

Hovoril si, že nepoužívaš while. Používaš teda vždy cyklus for s konečným počtom opakovaní ?

 
Odpovědět
21.11.2020 22:47
Tento výukový obsah pomáhají rozvíjet následující firmy, které dost možná hledají právě tebe!
Avatar
Milan Turyna
Redaktor
Avatar
Odpovídá na Dávid Dopirák
Milan Turyna:22.11.2020 8:23

Spletl jsem si to, polozka nebude false po jednom opakovani ale pokud mas aspon mas aspon jeden element v poli tak zustane po celou dobu true a potom ano, napise ti to ten error protoze to je jako kdybys vzal

while(true) {... }
 /*
bude to provadet opakovani do ty doby, dokud server neselze popripade dokud se nedojde k maximalnimu nastavenemu casu pro spusteni skriptu
*/

Používám foreach všude kde to jde a jinak for, ale v celku na tom cyklu neni nic spatneho, jen proste me vyhovuji vice tyto dva.

Editováno 22.11.2020 8:25
 
Odpovědět
22.11.2020 8:23
Avatar
Odpovídá na Milan Turyna
Dávid Dopirák:22.11.2020 9:35

Díky. Dal by som palec hore ale ešte nemám skill. ;)

 
Odpovědět
22.11.2020 9:35
Avatar
Lukáš Vavřík:22.11.2020 9:51

Přátelé máte v tutoriálu chybu.

Přepsal jsem si ho tak jak je to v tutoriálu uvedené a snažil se tomu porozumět. Když to použiji přesně tak jak to tu je tak to nefunguje a vyhazuje mi to v cyklu "foreach" chybu.

Po stažení zdrojového kódu jsem objevil, že v cyklu "while" je navíc

..  (is_file($this->slozka . '/' . $polozka) ...

bez této části to nefunguje.
Toto by chtělo opravit a trochu rozvést. Díky

 
Odpovědět
22.11.2020 9:51
Avatar
Odpovídá na Lukáš Vavřík
Lukáš Vavřík:22.11.2020 9:56

Aha :D tak já měl chybu ještě někde jinde :D Pardon. Škoda že tu nejdou komentáře mazat či editovat

 
Odpovědět
22.11.2020 9:56
Avatar
Dušan Kovářík:15. července 23:04

Tohle je velice zajímavá aplikace. Akorát je mi trochu záhadou, jaktože mi správně funguje přístup ke složce "obrazky" z třídy, která je ve složce "tridy", přičemž složku "obrazky" nemám uvnitř složky "třídy"... Budu se muset na tu třídu Directory podívat více do hloubky.

 
Odpovědět
15. července 23:04
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 75. Zobrazit vše