ITnetwork summer 2020
80 % bodů zdarma na online výuku díky naší Letní akci!

Lekce 8 - Výpis článků z databáze v PHP (MVC)

V minulé lekci, Databázový wrapper, jsme si vytvořili databázový wrapper nad PHP ovladačem PDO. Databázi máme již také připravenou, nic nám nebrání s ní komunikovat.

Připojení

Jako první se musíme k databázi připojit. To provedeme v index.php, těsně před vytvořením směrovače:

// Připojení k databázi
Db::pripoj("127.0.0.1", "root", "", "mvc_db");

Údaje si samozřejmě změňte podle svého webhostingu, takto jsou vyplněné pro localhost. Databázi máme v aplikaci přístupnou, pojďme se ji na něco zeptat.

Model - Správce článků

Vytvoříme třídu s logikou ohledně práce s články, která bude obsahovat jednotlivé SQL dotazy. Třída bude samozřejmě v modelech a jmenovat se bude SpravceClanku. Pro každou databázovou entitu si v našem systému vytvoříme nějakou podobnou třídu. SpravceClanku bude mít následující podobu:

<?php

// Třída poskytuje metody pro správu článků v redakčním systému
class SpravceClanku
{

    // Vrátí článek z databáze podle jeho URL
    public function vratClanek($url)
    {
        return Db::dotazJeden('
            SELECT `clanky_id`, `titulek`, `obsah`, `url`, `popisek`, `klicova_slova`
            FROM `clanky`
            WHERE `url` = ?
        ', array($url));
    }

    // Vrátí seznam článků v databázi
    public function vratClanky()
    {
        return Db::dotazVsechny('
            SELECT `clanky_id`, `titulek`, `url`, `popisek`
            FROM `clanky`
            ORDER BY `clanky_id` DESC
        ');
    }

}

Třída má celkem 2 metody:

  • vratClanek() vrací jeden článek z databáze podle jeho URL. Získávání dat od uživatele není záležitost modelu, všimněte si, že URL mu jednoduše přijde v argumentu metody a neřeší odkud se vzalo. Do podmínky SQL dotazu proměnnou nevložíme přímo, ale místo její hodnoty vložíme zástupný znak (otazník). Všechny parametry SQL dotazu následně předáme databázi jako hodnoty v poli, ona si je do dotazu sama a bezpečně dosadí.
  • vratClanky() vrací seznam všech článků (bez jejich obsahu). Články jsou seřazené sestupně podle ID, tedy od nejnovějších po nejstarší. Všimněte si, že nevybíráme jejich obsah, metodu budeme používat pouze pro výpis seznamu.

Pohledy

Budeme potřebovat 2 pohledy. Jeden pro článek a druhý pro seznam článků.

clanek.phtml

U článku vypíšeme nadpis a potom jeho obsah. Pohled bude následující:

<header>
    <h1><?= $titulek ?></h1>
</header>
<section>
    <?= $obsah ?>
</section>
Tento výukový obsah pomáhají rozvíjet následující firmy, které dost možná hledají právě tebe!

Druhý pohled necháme na konec lekce.

Kontroler

Máme model, máme pohled, zbývá náš známý prostředník - kontroler, který vše spojí dohromady. ClanekKontroler bude mít následující podobu:

class ClanekKontroler extends Kontroler
{
    public function zpracuj($parametry)
    {
        // Vytvoření instance modelu, který nám umožní pracovat s články
        $spravceClanku = new SpravceClanku();

        // Získání článku podle URL
        $clanek = $spravceClanku->vratClanek($parametry[0]);
        // Pokud nebyl článek s danou URL nalezen, přesměrujeme na ChybaKontroler
        if (!$clanek)
            $this->presmeruj('chyba');

        // Hlavička stránky
        $this->hlavicka = array(
            'titulek' => $clanek['titulek'],
            'klicova_slova' => $clanek['klicova_slova'],
            'popis' => $clanek['popis'],
        );

        // Naplnění proměnných pro šablonu
        $this->data['titulek'] = $clanek['titulek'];
        $this->data['obsah'] = $clanek['obsah'];

        // Nastavení šablony
        $this->pohled = 'clanek';
    }
}

Kontroler si vytvoří model a získá od něj článek podle URL adresy. Pokud článek nebyl nalezený, vyhodnotí se proměnná s ním jako false a přesměrujeme na ChybaKontroler. Hlavičku stránky nastavíme podle článku, dále pohledu předáme titulek a obsah, aby článek mohl vypsat. Nakonec nastavíme pohled na clanek.phtml.

Pojďme si vše vyzkoušet. Když aplikaci zapneme, uvidíme vypsaný úvodní článek:

Úvod
localhost/cla­nek/uvod

Můžete si zkusit zadat URL neexistujícího článku, budete přesměrování na chybovou stránku.

clanky.phtml

Seznam článků bude o něco složitější, protože potřebujeme vypsat jejich seznam z pole článků, které dostaneme od databáze. Jak že to ale uděláme, když zatím umíme vypisovat jen jednotlivé proměnné a zde potřebujeme vypsat obsah kolekce? Pojďme si rozšířit naše znalosti o PHP syntaxi, přesněji o šablonové verzi cyklů.

Šablonová syntaxe PHP

Kromě direktivy <?= nám PHP nabízí šablonové ekvivalenty nejběžnějších konstrukcí jazyka. Můžeme tak do pohledu (šablony) vložit minimální část logiky, která nebude znepřehledňovat HTML kód. Tyto šablonové ekvivalenty nám totiž umožňují vkládat PHP do HTML.

Bez znalosti šablonové syntaxe PHP by výpis seznamu článků do tabulky vypadal asi takto:

<h1>Seznam článků</h1>
<table>
    <?php
        foreach ($clanky as $clanek)
        {
            echo('<tr><td><h2>
                    <a href="clanek/' . $clanek['url'] . '">
                            ' . $clanek['titulek'] . '</a>
                    </h2>' . $clanek['popisek']);
            echo('</td></tr>');
        }
    ?>
</table>

HTML je nezvýrazněné, šablona nepřehledná., ztrácíme se v uvozovkách jak řetězce spojujeme. Pojďme kód převést do šablonové verze:

<h1>Seznam článků</h1>
<table>
<?php foreach ($clanky as $clanek) : ?>
    <tr>
        <td>
            <h2><a href="clanek/<?= $clanek['url'] ?>"><?= $clanek['titulek'] ?></a></h2>
            <?= $clanek['popisek'] ?>
        </td>
    </tr>
<?php endforeach ?>
</table>

Všimněte si, že cyklus foreach má za sebou napsanou dvojtečku a potom se ukončí PHP direktiva. Toto je šablonová verze foreach, která pro každý prvek vyechuje HTML kód, který je napsaný pod ním a to až do značky endforeach. Podobně lze přepsat i cykly for a while nebo podmínky if. HTML je zapsané jako HTML, ne jako string. Proměnné vložíme do HTML pomocí <?=, jak jsme zvyklí. Soubor clanky.phtml si s tímto obsahem ve složce pohledy vytvořte.

Pozn.: V šablonách stále neošetřujeme HTML entity a vystavujeme se tak útoku XSS. Napravíme to hned v příštím dílu.

Úprava ClanekKontroler

Seznam článků bude vypisovat ClanekKontroler a to v případě, když mu nezadáme žádný parametr. Kontroler jednoduše podmínkou rozpůlíme na dvě části. Jedna vypisuje konkrétní článek a druhá seznam. Pro tyto akce by se daly použít 2 kontrolery, ale obvykle se zapisují do jednoho, když spolu úzce souvisí. Metodu zpracuj() upravíme do následující podoby:

public function zpracuj($parametry)
{
    // Vytvoření instance modelu, který nám umožní pracovat s články
    $spravceClanku = new SpravceClanku();

    // Je zadáno URL článku
    if (!empty($parametry[0]))
    {
        // Získání článku podle URL
        $clanek = $spravceClanku->vratClanek($parametry[0]);
        // Pokud nebyl článek s danou URL nalezen, přesměrujeme na ChybaKontroler
        if (!$clanek)
            $this->presmeruj('chyba');

        // Hlavička stránky
        $this->hlavicka = array(
            'titulek' => $clanek['titulek'],
            'klicova_slova' => $clanek['klicova_slova'],
            'popis' => $clanek['popisek'],
        );

        // Naplnění proměnných pro šablonu
        $this->data['titulek'] = $clanek['titulek'];
        $this->data['obsah'] = $clanek['obsah'];

        // Nastavení šablony
        $this->pohled = 'clanek';
    }
    else
    // Není zadáno URL článku, vypíšeme všechny
    {
        $clanky = $spravceClanku->vratClanky();
        $this->data['clanky'] = $clanky;
        $this->pohled = 'clanky';
    }
}

Nyní přejděme na kontroler clanek a nezadáme URL článku, který se má zobrazit. Zobrazí se seznam všech článků na webu:

Your page
localhost/clanek/

V příští lekci, Zabezpečení šablon, se budeme věnovat zabezpečení šablon proti XSS.


 

Stáhnout

Staženo 1563x (14.46 kB)
Aplikace je včetně zdrojových kódů v jazyce PHP

 

Předchozí článek
Databázový wrapper
Všechny články v sekci
Jednoduchý redakční systém v PHP objektově (MVC)
Článek pro vás napsal David Čápka
Avatar
Jak se ti líbí článek?
32 hlasů
Autor pracuje jako softwarový architekt a pedagog na projektu ITnetwork.cz (a jeho zahraničních verzích). Velmi si váží svobody podnikání v naší zemi a věří, že když se člověk neštítí práce, tak dokáže úplně cokoli.
Unicorn College Autor sítě se informační technologie naučil na Unicorn College - prestižní soukromé vysoké škole IT a ekonomie.
Aktivity (11)

 

 

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

Avatar
Vojtech Palec
Redaktor
Avatar
Vojtech Palec:11. února 13:28

Ano. Mně se pouze neukazuje ta hlavní šablona.

 
Odpovědět
11. února 13:28
Avatar
Mego
Člen
Avatar
Odpovídá na Vojtech Palec
Mego:11. února 13:32

Tak to by si tušim v SmerovacKontroller mal mať nastavený ten $this->view('sablona'). Neviem ako presne sa to volá, lebo ja som si to písal hneď v angličtine.

Odpovědět
11. února 13:32
Radšej 15 minút skôr, ako 15 sekúnd neskoro...
Avatar
Vojtech Palec
Redaktor
Avatar
Odpovídá na Mego
Vojtech Palec:11. února 16:18

To mám taky. Ta šablona se mi neukazuje jen v případě, že načítám z databáze.

Editováno 11. února 16:19
 
Odpovědět
11. února 16:18
Avatar
Mego
Člen
Avatar
Odpovídá na Vojtech Palec
Mego:12. února 11:13

Tak mozno databaza hodi nejaky skryty error, ktore zabrani dalsiemu spracovaniu skriptu? Daj kod, bez toho ti nikto nepomoze

Odpovědět
12. února 11:13
Radšej 15 minút skôr, ako 15 sekúnd neskoro...
Avatar
Vojtech Palec
Redaktor
Avatar
Odpovídá na Mego
Vojtech Palec:12. února 15:43
class ClanekKontroler extends Kontroler{
    //put your code here

    public function zpracuj($parametry) {
        $spravceClanku = new SpravceClanku();


        $clanek = $spravceClanku->vratClanek($parametry[0]);

        if (!$clanek)
            $this->presmeruj('chyba');

        // Hlavička stránky
        $this->hlavicka = array(
            'titulek' => $clanek['titulek'],
            'klicova_slova' => $clanek['klicova_slova'],
            'popis' => $clanek['popisek'],
        );

        $this->data['titulek'] = $clanek['titulek'];
        $this->data['obsah'] = $clanek['obsah'];

        $this->pohled = 'clanek';
    }
}

...

<?php

class SmerovacKontroler extends Kontroler
{
        // Instance controlleru
        protected $kontroler;

        // Metoda převede pomlčkovou variantu controlleru na název třídy
        private function pomlckyDoVelbloudiNotace($text)
        {
                $veta = str_replace('-', ' ', $text);
                $veta = ucwords($veta);
                $veta = str_replace(' ', '', $veta);
                return $veta;
        }

        // Naparsuje URL adresu podle lomítek a vrátí pole parametrů
        private function parsujURL($url)
        {
                // Naparsuje jednotlivé části URL adresy do asociativního pole
        $naparsovanaURL = parse_url($url);

                $naparsovanaURL["path"] = ltrim($naparsovanaURL["path"], "/");

                $naparsovanaURL["path"] = trim($naparsovanaURL["path"]);

                $rozdelenaCesta = explode("/", $naparsovanaURL["path"]);
                return $rozdelenaCesta;
        }

    // Naparsování URL adresy a vytvoření příslušného controlleru
    public function zpracuj($parametry)
    {
                $naparsovanaURL = $this->parsujURL($parametry[0]);

                if (empty($naparsovanaURL[0]))
                        $this->presmeruj('clanek/uvod');

                $tridaKontroleru = $this->pomlckyDoVelbloudiNotace(array_shift($naparsovanaURL)) . 'Kontroler';

                if (file_exists('kontrolery/' . $tridaKontroleru . '.php'))
                        $this->kontroler = new $tridaKontroleru;
                else
                        $this->presmeruj('chyba');

        $this->kontroler->zpracuj($naparsovanaURL);


                $this->data['titulek'] = $this->kontroler->hlavicka['titulek'];
                $this->data['popis'] = $this->kontroler->hlavicka['popis'];
                $this->data['klicova_slova'] = $this->kontroler->hlavicka['klicova_slova'];


                $this->pohled = 'rozlozeni';
    }

}
class Db {
    //put your code here
    private static $spojeni;

    private static $nastaveni= array(
         PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
    PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8",
    PDO::ATTR_EMULATE_PREPARES => false,

    );


    public static function pripoj($host, $uzivatel, $heslo, $databaze)
{
    if (!isset(self::$spojeni))
    {
        self::$spojeni = @new PDO(
            "mysql:host=$host;dbname=$databaze",
            $uzivatel,
            $heslo,
            self::$nastaveni
        );
    }
}

     public static function dotazJeden($dotaz, $parametry=array()){
         $navrat= self::$spojeni->prepare($dotaz);
         $navrat->execute($parametry);
         return $navrat->fetch();

     }

     public static function dotazVsechny($dotaz, $parametry=array()){
         $navrat= self::$spojeni->prepare($dotaz);
         $navrat= execute($parametry);
         return $navrat->fetchAll();
     }

     public static function dotazSamotny($dotaz, $parametry=array()){
         $vysledek = self::dotazJeden($dotaz, $parametry);
         return vysledek[0];
     }

     public static function dotaz($dotaz, $parametry=array()){
         $navrat= self::$spojeni ->prepare($dotaz);
         $navrat->execute($paramety);
         return $navrat->rowCount();
     }

}
<?php
mb_internal_encoding("UTF-8");
/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */

function autoLoad($trida){
    if(preg_match('/Kontroler$/', $trida))
        require("Kontrolery/".$trida.".php");

    else
        require("Modely/".$trida.".php");

}

spl_autoload_register("autoLoad");


Db::pripoj("127.0.0.1", "root", "", "mvc_db");

$router= new SmerovacKontroler();
$router ->zpracuj(array($_SERVER['REQUEST_URI']));
$router->vypisPohled();

...

class SpravceClanku {
    //put your code here

    public function vratClanek($url){

        return Db::dotazJeden(
                'SELECT `clanek_id`, `titulek`, `obsah`, `popisek`, `klicova_slova`
                FROM `clanek`
                WHERE `url` = ? ', array($url));
    }

    public function vratClanky(){
        return Db::dotazVsechny(
                '
                SELECT `clanky_id`, `titulek`, `url`, `popisek`
                FROM `clanky`
                ORDERED BY `clanky_id` DESC '

                );
    }

}
Editováno 12. února 15:44
 
Odpovědět
12. února 15:43
Tento výukový obsah pomáhají rozvíjet následující firmy, které dost možná hledají právě tebe!
Avatar
Vojtech Palec
Redaktor
Avatar
 
Odpovědět
12. února 15:51
Avatar
Vojtech Palec
Redaktor
Avatar
Vojtech Palec:12. února 15:52

Tabulka

 
Odpovědět
12. února 15:52
Avatar
Vojtech Palec
Redaktor
Avatar
Odpovídá na Mego
Vojtech Palec:12. února 21:17

A teď jsem našel pomocí f12 v editor stylů

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="cs" xml:lang="cs">
<head>
<title>Objekt nenalezen!</title>
<link rev="made" href="mailto:[email protected]" />
<style type="text/css"><!--/*--><![CDATA[/*><!--*/
    body { color: #000000; background-color: #FFFFFF; }
    a:link { color: #0000CC; }
    p, address {margin-left: 3em;}
    span {font-size: smaller;}
/*]]>*/--></style>
</head>

<body>
<h1>Objekt nenalezen!</h1>
<p>


    Požadované URL nebylo na tomto serveru nalezeno.



    Zdá se, že odkaz na
    <a href="http://localhost/Clanek/uvod">odkazující
    stránce</a> je chybný nebo zastaralý. Informujte, prosím, autora
    <a href="http://localhost/Clanek/uvod">této stránky</a>
    o&nbsp;chybě.



</p>
<p>
Pokud si myslíte, že toto je chyba serveru, kontaktujte, prosím,
<a href="mailto:[email protected]">webmastera</a>.

</p>

<h2>Error 404</h2>
<address>
  <a href="/">localhost</a><br />
  <span>Apache/2.4.41 (Win64) OpenSSL/1.1.1c PHP/7.4.1</span>
</address>
</body>
</html>
 
Odpovědět
12. února 21:17
Avatar
Mego
Člen
Avatar
Odpovídá na Vojtech Palec
Mego:13. února 6:38

Vsak to vyzera, ze ti natiahlo aj sablona.phtml aj clanek.phtml. akurat css nemas, ale to nevidim ani v zdrojaku. Ta chyba, co si poslal, to sa ti kde zobrazuje?

Odpovědět
13. února 6:38
Radšej 15 minút skôr, ako 15 sekúnd neskoro...
Avatar
Vojtech Palec
Redaktor
Avatar
Odpovídá na Mego
Vojtech Palec:13. února 7:32

Css mi funguje, když rozkliknu třeba kontakt, nebo na hlavní stránce. A ta chyba je z editor stylů, když stisknu f12 na prohlížeči ve Firefoxu :-)

 
Odpovědět
13. února 7:32
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 64. Zobrazit vše