Zarábaj až 6 000 € mesačne! Akreditované rekvalifikačné kurzy od 0 €. Viac informácií.

6. diel - Jednoduchý redakčný systém v Symfony - Model článkov

Vítam vás všetkých u pokračovanie kurzu tutoriálov o tvorbe webových aplikácií v PHP frameworku Symfony. V minulej lekcii, Jednoduchý redakčný systém v Symfony - Štruktúra projektu , sme si pripravili projektovú štruktúru pre jednoduchý redakčný systém a ako som sľúbil, dnes sa rovno bez zbytočných rečí vrhneme na implementáciu. Takže ideme na to! :)

Databázy

V prvom rade sa pozrieme na nastavenie i obsah databázy. V Symfony sa väčšinou pre prácu s databázou používa knižnica tretích strán Doctrine. Tá pracuje s princípom O bjektově R elačního M apování (ORM), ktorého základ tvorí entity. S jednou takou entitou sme sa už stretli v lekcii kurze o tvorbe operácie kalkulačky.

Src / Entity / Article.php

Teraz si obdobne vytvoríme entitu reprezentujúci článok v našom redakčnom systéme, následne ju obohatíme o anotácie z Doctrine knižnice a necháme si podľa nej vygenerovať celú relačnej databázovú štruktúru. Doctrine potom zaistí, že položky z našej databázy v danej tabuľke sa automaticky namapujete na inštancie onej Entita triedy (teda na objekty), čo je práve princíp celého ORM. Naša trieda entity článkov bude teda vyzerať takto:

<?php

namespace App\Entity;

use Doctrine\ORM\Mapping as ORM;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Symfony\Component\Validator\Constraints as Assert;

/**
 * Reprezentuje záznamy databázové tabulky článků v redakčním systému.
 * @ORM\Entity(repositoryClass="App\Repository\ArticleRepository")
 * @UniqueEntity("url", message="Článek s touto URL adresou již existuje!")
 * @package App\Entity
 */
class Article
{
    /**
     * @var int Unikátní ID článku.
     * @ORM\Id()
     * @ORM\GeneratedValue()
     * @ORM\Column(type="integer")
     */
    private $id;

    /**
     * @var string Titulek článku.
     * @ORM\Column(type="string")
     * @Assert\NotBlank(message="Titulek článku nemůže být prázdný!")
     */
    private $title;

    /**
     * @var string Text (obsah) článku.
     * @ORM\Column(type="text")
     * @Assert\NotBlank(message="Obsah článku nemůže být prázdný!")
     */
    private $content;

    /**
     * @var string Unikátní URL adresa článku.
     * @ORM\Column(type="string", unique=true)
     * @Assert\NotBlank(message="URL adresa článku nemůže být prázdná!")
     */
    private $url;

    /**
     * @var string Krátký popis článku.
     * @ORM\Column(type="string")
     * @Assert\NotBlank(message="Popis článku nemůže být prázdný!")
     */
    private $description;

    /**
     * Getter pro ID článku.
     * @return int ID článku
     */
    public function getId(): int
    {
        return $this->id;
    }

    /**
     * Getter pro titulek článku.
     * @return null|string titulek článku
     */
    public function getTitle(): ?string
    {
        return $this->title;
    }

    /**
     * Setter pro titulek článku.
     * @param string|null $title titulek článku
     * @return Article sebe
     */
    public function setTitle(string $title = null): self
    {
        $this->title = $title;
        return $this;
    }

    /**
     * Getter pro obsah článku.
     * @return null|string obsah článku
     */
    public function getContent(): ?string
    {
        return $this->content;
    }

    /**
     * Setter pro obsah článku.
     * @param string|null $content obsah článku
     * @return Article sebe
     */
    public function setContent(string $content = null): self
    {
        $this->content = $content;
        return $this;
    }

    /**
     * Getter pro URL adresu článku.
     * @return null|string URL adresa článku
     */
    public function getUrl(): ?string
    {
        return $this->url;
    }

    /**
     * Setter pro URL adresu článku.
     * @param string|null $url URL adresa článku
     * @return Article sebe
     */
    public function setUrl(string $url = null): self
    {
        $this->url = $url;
        return $this;
    }

    /**
     * Getter pro popis článku.
     * @return null|string popis článku
     */
    public function getDescription(): ?string
    {
        return $this->description;
    }

    /**
     * Setter pro popis článku.
     * @param string|null $description popis článku
     * @return Article sebe
     */
    public function setDescription(string $description = null): self
    {
        $this->description = $description;
        return $this;
    }
}

Triedu si môžete nechať aj automaticky vygenerovať príkazom php bin/console make:entity, čo je veľmi praktické a šetrí to čas :)

Všimnite si tu hlavne nových ORM anotácií, ktoré "transformujú" onú triedu na príslušnú databázovú tabuľku a jej atribúty na stĺpce onej tabuľky s daným typom a vlastnosťami.

Za zmienku tiež stojí anotácie @UniqueEntity, ktorá v kombinácii s unikátnou URL článku zabezpečujú, že takáto entita sa vyskytne iba raz. To sa hodí napr. Keď budeme pomocou formulára vytvárať nové.

Keď už spomíname formulára, ďalej si môžete všimnúť, že sme tu rovno pripravili aj anotácie, ktoré využijeme pri tvorbe formuláre pre prácu s článkami, ktorý tým pádom môžeme tiež vytvoriť na základe tejto entity ;)

Nastavenia prístupových údajov a vytvorenie databázy

Aby sme si teraz mohli nechať vygenerovať databázovú štruktúru, musíme ešte nastaviť prístupové údaje do našej databázy. To urobíme tak, že upravíme hodnotu premennej DATABASE_URL vnútri súboru .env:

...
DATABASE_URL=mysql://<uživatelské jméno>:<heslo>@localhost:3306/<název databáze>
...

Databázu potom nemusíte vytvárať ručne, ale pre jej vytvorenie rovno použiť Doctrine príkaz:

php bin/console doctrine:database:create

Generovanie databázové štruktúry

Pre vygenerovanie našej relačné databázové štruktúry v podobe jednej tabuľky článkov opäť použijeme Doctrine príkazy.

Najskôr vytvoríme novú tzv. Migráciu pomocou príkazu:

php bin/console make:migration

Tento príkaz vytvorí PHP skript v priečinku src/Migrations/, kde zafixuje aktuálne databázovú štruktúru. To je výhodné najmä ak by sme ju do budúcnosti ďalej menili, pretože ďalšie migračné skripty môžu vychádzať z predchádzajúcej verzie a len aplikovať zmeny. : -`

Nasledujúcim príkazom potom aplikujeme všetky migračné zmeny:

php bin/console doctrine:migrations:migrate

Ak ste všetko urobili dobre, mali by ste v nastavenej databáze vidieť novú tabuľku article.

Ak si chcete vypísať všetky dostupné Doctrine príkazy a ich opis, použite príkaz php bin/console list doctrine

Predpripravené články

My si do našej tabuľky článkov doplníme ešte dva predpripravené články, jeden pre úvodnú stránku a jeden pre vypísanie chyby. To býva štandardom u väčšiny redakčných systémov.

Podľa oficiálneho postupu by som si pre tieto články mal pripraviť tzv. Doctrine Fixture. To by však vyžadovalo inštaláciu externého bundlu DoctrineFixtu­resBundle a toho vás chcem nateraz ušetriť. Vyriešil som to teda obyčajným SQL skriptom, ktorý stačí spustiť nad vašou databázou:

INSERT INTO `article` VALUES ('1', 'Úvod', '<p>Vítejte na našem webu!</p>\r\n\r\n<p>Tento web je postaven na <strong>jednoduchém redakčním systému v Symfony frameworku</strong>. Toto je úvodní článek, načtený z databáze.</p>', 'uvod', 'Úvodní článek na webu v Symfony v PHP');
INSERT INTO `article` VALUES ('2', 'Stránka nebyla nalezena', '<p>Litujeme, ale požadovaná stránka nebyla nalezena. Zkontrolujte prosím URL adresu.</p>', 'chyba', 'Stránka nebyla nalezena.');

Model

Presunieme sa k vytváraniu modelu. Triedam v modelovej vrstve, ktoré majú pracovať s Doctrine entitami, sa hovorí repozitára. To je trochu rozdiel oproti modelu kalkulačky, ale v podstate je len kozmetický.

Src / Repository / ArticleReposi­tory.php

Vytvorme si teda teraz repozitár pre správu článkov:

<?php

namespace App\Repository;

use App\Entity\Article;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\ORM\ORMException;
use Symfony\Bridge\Doctrine\RegistryInterface;

/**
 * Repositář pro správu článků v redakčním systému.
 * @method Article|null find($id, $lockMode = null, $lockVersion = null)
 * @method Article|null findOneBy(array $criteria, array $orderBy = null)
 * @method Article[]    findAll()
 * @method Article[]    findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
 * @package App\Repository
 */
class ArticleRepository extends ServiceEntityRepository
{
    /** @inheritdoc */
    public function __construct(RegistryInterface $registry)
    {
        parent::__construct($registry, Article::class);
    }

    /**
     * Vrátí článek z databáze podle jeho URL.
     * @param string $url URl článku
     * @return Article|null první článek, který má danou URL nebo null pokud takový neexistuje
     */
    public function findOneByUrl(string $url): ?Article
    {
        return $this->findOneBy(['url' => $url]);
    }

    /**
     * Uloží článek do systému.
     * Pokud není nastaveno ID, vloží nový, jinak provede editaci.
     * @param Article $article článek
     * @throws ORMException Jestliže nastane chyba při ukládání článku.
     */
    public function save(Article $article): void
    {
        $this->getEntityManager()->persist($article);
        $this->getEntityManager()->flush($article);
    }

    /**
     * Odstraní článek s danou URL.
     * @param string $url URL článku
     * @throws ORMException Jestliže nastane chyba při mazání článku.
     */
    public function removeByUrl(string $url): void
    {
        if (($article = $this->findOneByUrl($url))) {
            $this->getEntityManager()->remove($article);
            $this->getEntityManager()->flush();
        }
    }
}

Ak ste si nechali generovať triedu entity článku, budete mať vygenerovanú aj základnú kostru jej repozitára, ktorú iba upravíte.

Dôležité je spomenúť, že každý repozitár by mal dediť z triedy ServiceEntityRepository, ktorá mu umožňuje pracovať s entitami, vyberať ich a ukladať i mazať ich z databázy. Každá inštancia triedy Article potom reprezentuje jeden záznam v databáze.

Čo stojí za povšimnutie je, že ukladanie alebo mazanie entity by sme mali ešte potvrdiť volaním flush(). Dovtedy sa totiž fyzicky nevykoná SQL príkaz nad databázou.

To je z dnešnej lekcie všetko. Nabudúce, v lekcii Jednoduchý redakčný systém v Symfony - Výpis článku , sa budeme venovať kontrolerům a šablónam. Tým náš projekt sprevádzkujeme :)


 

Predchádzajúci článok
Jednoduchý redakčný systém v Symfony - Štruktúra projektu
Všetky články v sekcii
Základy frameworku Symfony pre PHP
Preskočiť článok
(neodporúčame)
Jednoduchý redakčný systém v Symfony - Výpis článku
Článok pre vás napísal Jindřich Máca
Avatar
Užívateľské hodnotenie:
Ešte nikto nehodnotil, buď prvý!
Autor se věnuje převážně webovým technologiím, ale má velkou zálibu ve všem vědeckém, nejen ze světa IT. :-)
Aktivity