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 DoctrineFixturesBundle 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 / ArticleRepository.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