Hlavní navigace

Úvod do frameworku Symfony: naša prvá aplikácia

Ján Bodnár

V druhom článku o Symfony si vytvoríme a opíšeme našu prvú aplikáciu. Pre naše príklady potrebujeme mať nainštalovaný PHP 7.1.3+ a composer. Aplikácie budeme spúšťať pod zabudovaným serverom.

Doba čtení: 9 minut

Sdílet

Symfony console utilita

Každý Symfony projekt má vo svojom bin adresári console utilitu. Táto utilita plní viacero úloh. Poskytuje nám informácie o projekte, manažuje lokálny webový server, poskytuje linting, alebo generuje šablóny kódu, tzv. scaffolding. (Pre scaffolding potrebujeme mať nainštalovaný Symfony maker.)

$ php bin/console
Symfony 4.2.4 (env: dev, debug: true)

Usage:
    command [options] [arguments]

Options:
    -h, --help            Display this help message
    -q, --quiet           Do not output any message
    -V, --version         Display this application version
        --ansi            Force ANSI output
        --no-ansi         Disable ANSI output
    -n, --no-interaction  Do not ask any interactive question
...

Všetky možnosti utility console dostaneme, ak ju spustíme bez volieb: php bin/console.

Prvá aplikácia

Pomocou composera vytvoríme prvú skeleton aplikáciu.

$ composer create-project symfony/skeleton first

Vygeneruje sa Symfony aplikácia s minimálnymi závislosťami. Projekt sa vytvorí v podadresári first.

$ cd first

Nezabudnime sa premiestniť do nového adresára. Toto je častá chyba u začiatočníkov. V krátkosti si opíšeme obsah nášho projektového adresára.

$ ls -Ap
.env        bin/           composer.lock  public/  symfony.lock  vendor/
.gitignore  composer.json  config/        src/     var/

V adresári bin máme už spomínanú console utilitu. V adresári public sa umiesťňujú webové súbory, vrátane HTML, CSS a JS súborov. V tomto momente sa tam nachádza len súbor index.php, ktorý má úlohu front controllera a vykonáva bootstrapping. Front controller je návrhový vzor, ktorého úlohou je prijať a preposlať všetky prichádzajúce požiadavky od klientov. Spring používa pre tento objekt prihliehavý názov dispatcher. Pod bootstrappingom sa myslí štart systému, v našom prípade Symfony frameworku. Pre nás je však tento súbor nepodstatný a nemusíme sa ním zaoberať.

Závislosti projektu sa nachádzajú v adresári vendor. Ako nám už názov napovedá, config adresár obsahuje rôzne konfiguračné súbory projektu. Zdrojový kód sa píše do adresára src. Adresár var obsahuje dočasné dáta, ako sú napríklad logy.

V súbore composer.json máme zaznamenané závislosti composera. V pomocnom súbore composer.lock máme presné verzie knižníc a ich závislostí. Kvôli prenositeľnosti a minimalizícii konfliktov.

V súbore .gitignore sa nachádzajú súbory a adresáre, ktoré sa majú vyňať z posielania do repositárov. (Repozitáre sú úložiská kódu, kde si vývojári skladujú svoj vytvorený kód. Symfony pracuje s git systémom.) Patria sem napríklad adresáre vendor alebo var.

Podľa najnovších vývojárskych trendov sa citlivé a dôležité konfiguračné údaje uchovávajú v premenných prostredia. Tieto premenné s východzími nastaveniami máme v súbore .env. Nájdeme tam napríklad databázové prihlasovacie údaje. Súbor .env uľahčuje nasadzovanie aplikácie na rôzne systémy. Keďže sa komituje do repozitárov, nesmú sa v ňom nachádzať citlivé údaje.

Tu došlo v minulom roku ku zmene, keď Symfony mal súbor .env.dist, ktorý sa komitoval, a .env, ktorý bol z repozitárov vyňatý. V najnovších verziách sa upustilo od .env.dist súboru a .env sa naopak komituje. Úlohu .env.dist nahradili .env.local alebo .env.(dev/test/prod).local.

Ďalším krokom býva štandardne inicializácia nového git repozitára. Keďže však našou úlohou je spoznávať Symfony, git zatiaľ nebudeme potrebovať.

$ composer info
...
symfony/finder               v4.2.4  Symfony Finder Component
symfony/flex                 v1.2.0  Composer plugin for Symfony
symfony/framework-bundle     v4.2.4  Symfony FrameworkBundle
symfony/http-foundation      v4.2.4  Symfony HttpFoundation Component
symfony/http-kernel          v4.2.4  Symfony HttpKernel Component
symfony/polyfill-mbstring    v1.10.0 Symfony polyfill for the Mbstring extension
symfony/routing              v4.2.4  Symfony Routing Component
...

Pomocou príkazu composer info si môžeme zistiť, aké závislosti máme v danom momente nainštalované. Keďže sme si vytvorili skeleton aplikáciu, budeme potrebovať nainštalovať celý rad ďalších komponentov. Preto sa bude náš zoznam závislostí pomaly ale isto rozrastať.

$ php bin/console about
-------------------- -------------------------------------------
    Symfony
-------------------- -------------------------------------------
    Version              4.2.4
    End of maintenance   07/2019
    End of life          01/2020
-------------------- -------------------------------------------
    Kernel
-------------------- -------------------------------------------
    Type                 App\Kernel
    Environment          dev
    Debug                true
    Charset              UTF-8
    Cache directory      ./var/cache/dev (4.5 MiB)
    Log directory        ./var/log (0 B)
-------------------- -------------------------------------------
    PHP
-------------------- -------------------------------------------
    Version              7.2.11
...

Podrobné informácie o projekte môžeme získať pomocou príkazu php bin/console about.

Pridávanie základných závislostí

Nainštalujeme si tri základné závislosti. Ako názvy použijeme aliasy balíčkov.

$ composer req dumper profiler server --dev

Niektoré balíčky môžu mať aj viacero aliasov. Napríklad profiler je alias pre symfony/profiler-pack. Súhrn aliasov pre balíčky nájdeme na flex.symfony.com

Voľbou --dev sa tieto balíčky zaradia do development prostredia. V Symfony existujú tri základné prostredia: dev, test a prod. Je možnosť si vytvoriť tiež ďalšie vlastné prostredie. Pod prostredím (Environment) sa chápe báza kódu s určitými konfiguračnými nastaveniami.

Dumper nám poskytuje výborné nástroje na debugovanie: dump(), resp. dd(). Pomocou nich môžeme počas vývoja prezerať obsah premenných. Funkcia dd() je kombináciou dump() a exit().

public function about(Request $request): Response
{
    dump($request);
    ...
}

V našom prípade si prezeráme obsah objektu Request. Ide o veľmi šikovný a dobre vyladený nástroj, ktorý je neoceniteľný pre vývojára.

Profiler je ladiaci panel, ktorý nám počas vývoja aplikácie poskytuje podrobné informácie o požiadavkách, logoch, alebo zabezpečení.

Server nám dáva k dispozícii vývojársky PHP server. Ten sa spúšťa príkazom php bin/console server:start. (Pre Windows server:run). Po spustení servera nájdeme na adrese http://127.0.0.1:8000/ Symfony uvítaciu stránku.

Symfony maker

Symfony maker vytvára čiastočne zhotovené triedy, testy, migrácie, Twig šablóny, rôzne súbory a adresáre. Pomáha nám generovať opakujúci sa kód; v angličtine sa takému kódu hovorí boilerplate. Procesu tvorby častí projektu sa hovorí scaffolding.

$ composer req maker

Príkaz nainštaluje symfony/maker-bundle. Bundle termín sa používa pre Symfony knižnice, ktoré sú dodatočné ku core systému. Pre knižnice, ktoré tvoria jadro systému sa používa termín component.

$ php bin/console list make
...
make:command                Creates a new console command class
make:controller             Creates a new controller class
make:crud                   Creates CRUD for Doctrine entity class
make:entity                 Creates or updates a Doctrine entity class ...
make:fixtures               Creates a new class to load Doctrine fixtures
make:form                   Creates a new form class
...

Zoznam príkazov pre maker dostaneme pomocou php bin/console list make.

Chystáme sa vytvoriť prvý controller. Keďže sa pritom používajú anotácie, potrebujeme tiež doinštalovať sensio/framework-extra-bundle. Balík si stiahneme pomocou jeho aliasu annotations.

$ composer req annotations

Ide o bundle, ktorý umožňuje konfigurovať controllery pomocou anotácií. Inštalácia týchto rôznych balíčkov môže to vyzerať na prvý pohľad komplikovane. Avšak composer nás vždy upozorní na chýbajúcu závislosť a po čase sa tieto úkony úplne zautomatizujú.

$ php bin/console make:controller HomeController

created: src/Controller/HomeController.php
created: templates/home/index.html.twig

Pomocou php bin/console make:controller vytvoríme nový controller s názvom HomeController. Symfony maker vytvorí pritom dva súbory: HomeController.php a index.html.twig.

Controller

Controller je trieda, ktorá spracuje požiadavku od klienta, deleguje úlohy pre biznis logiku, a vráti klientovi výstup vo forme HTML, JSON, alebo PDF. Požiadavka je spracovaná vo funkcii, ktorej sa hovorí akcia (action). Symfony dokumentácia neodporúča používať slovo akcia v názvoch funkcií.

Symfony má vo svojich best practices nasledovné odporúčanie:

Symfony follows the philosophy of „thin controllers and fat models“. This means that controllers should hold just the thin layer of glue-code needed to coordinate the different parts of the application.

Controller sa nemá využívať na vykonávanie biznis logiky. Controller deleguje úlohy iným častiam aplikácie a preposiela výsledky. Ideálne by mali byť akcie čo najštíhlejšie. V našich príkladoch budeme niekedy kvôli jednoduchosti vykonávať biznis logiku aj v controlleri; v produkčnom nasadení sa však treba pridŕžať tohoto odporúčania.

Mapovaniu URL cesty požiadavky (napr. /about) na funkciu controllera sa hovorí routing. Routing v Symfony je možné nastaviť pomocou anotácií, YAML súboru, XML súboru, alebo pomocou PHP kódu. Táto komplexná flexibilita mi silne pripomína Spring framework. Najčastejšie sa zrejme na routing používajú anotácie.

Textová odpoveď

Obyčajnú textovú odpoveď môžeme vytvoriť s Symfony\Component\HttpFoundation\Response. HttpFoundation komponent tvorí objektovú vrstvu nad HTTP špecifikáciou. Ukrýva pre nami detaily ako sú PHP globálne premenné.

<?php
// src/Controller/HomeController.php

namespace App\Controller;

use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;

class HomeController extends AbstractController
{
    /**
    * @Route("/home", name="home")
    */
    public function index(): Response
    {
        return new Response("Hello there",  Response::HTTP_OK,
            ['content-type' => 'text/plain']);
    }
}

Na URL cestu /home sme namapovali pomocou @Route anotácie funkciu index(). PHP nemá anotácie ako má Java alebo Python, preto bola vytvorená zvlášť knižnica pre parsovanie anotácií v komentároch. Funkcia vracia jednoduchú textovú odpoveď.

class HomeController extends AbstractController

Symfony maker nám vytvoril kostru controllera, ktorý dedí z triedy AbstractController. Dediť môžeme aj z triedy Controller, Symfony však odporúča AbstractController. AbstractController nám dáva k dispozícii predpripravené funkcie pre renderovanie šablón, forwarding, redirecting, alebo tvorbu JSON odpovede.

$ public function index(): Response

Všimnime si používanie typových hintov. Od PHP verzie 7 môžeme používať typové hinty pre návratové hodnoty funkcií. To nám umožňuje zachytiť veľa chýb pri vývoji.

Potom, čo si naštartujeme development server, môžeme si otestovať poslanie našej prvej požiadavky.

$ curl localhost:8000/home
Hello there

Na takého jednoduché testovanie sa hodí utilitka curl.

JSON odpoveď

Pre odpoveď vo formáte JSON použijeme Symfony\Component\HttpFoundation\JsonResponse.

/**
* @Route("/colours", name="colours")
*/
public function colours(): JsonResponse
{
    $colours = ['red', 'green', 'blue', 'steelblue'];

    return new JsonResponse(['colours' => $colours]);
}

Nová akcia vracia dáta vo formáte JSON.

$ curl localhost:8000/colours
{"colours":["red","green","blue","steelblue"]}

Znova si to otestujeme pomocou curl.

Twig šablóna

Ďalej si ukážeme ako poslať HTML odpoveď, ktorú vygenerujeme pomocou Twig šablóny.

$ composer req twig

Potrebujeme si najprv nainštalovať Twig. Ten sa nachádza v symfony/twig-bundle. Šablóny sa v Symfony ukladajú do adresára templates.

/**
* @Route("/names", name="names")
*/
public function names(): Response
{
    $names = ['Paul', 'Thomas', 'Julia', 'Betty'];

    return $this->render('home/index.html.twig', ['names' => $names]);
}

Tretia akcia vracia HTML výstup. Premenná $names obsahuje dáta, ktoré sa prepošlú šablóne na spracovanie. HTML výstup sa vygeneruje pomocou funkcie render(), ktorú sme zdedili z AbstractController  triedy. Ako prvý parameter funkcia prijíma názov šablóny. Druhým parametrom sú dáta.

{# templates/home/index.html.twig #}
{% extends 'base.html.twig' %}

{% block title %}Home page{% endblock %}

{% block body %}

<ul>
{% for name in names %}

<li>{{ name }}</li>

{% endfor %}
</ul>

{% endblock %}

Twig šablóny majú zaužívanú príponu html.twig. V šablóne prejdeme dáta ktoré sme dostali pomocou directívy for. Všetky mená tak vygenerujeme do nezotriedeného listu. Twig používa {% %} pre directívy, {# #} pre komentáre a {{}} pre output.

Zobrazenie namapovaných ciest

Máme vytvorené tri cesty. Všetky cesty si môžeme zobraziť pomocou Symfony console utility.

$ php bin/console debug:router -e prod
--------- -------- -------- ------ ----------
    Name      Method   Scheme   Host   Path
--------- -------- -------- ------ ----------
    home      ANY      ANY      ANY    /home
    colours   ANY      ANY      ANY    /colours
    names     ANY      ANY      ANY    /names
--------- -------- -------- ------ ----------

Príkaz php bin/console debug:router -e prod zobrazí nami vytvorené cesty. Keďže máme aktivovaný profiler a ten má vlastné cesty, pridali sme voľbu -e prod, ktorá ich vynechá z výpisu.

V ďalšej časti sa budeme zaoberať request-response cyklom a prejdeme si ďalšie možnosti, ktoré nám ponúka AbstractController.