IT rekvalifikace s garancí práce. Seniorní programátoři vydělávají až 160 000 Kč/měsíc a rekvalifikace je prvním krokem. Zjisti, jak na to!
Hledáme nové posily do ITnetwork týmu. Podívej se na volné pozice a přidej se do nejagilnější firmy na trhu - Více informací.

4. diel - Dokončenie kalkulačky v Symfony

Vitajte u ďalšej lekcie online kurzu o tvorbe webových aplikácií v PHP frameworku Symfony. V tomto tutoriálu nadviažeme na tvorbu kalkulačky z minulej lekcie, Prvé aplikácie v Symfony , a dokončíme ju. Minule sme teda definovali operácie modelu, ale to ešte nie je všetko, takže bez ďalšieho otáľania ideme na to! :)

Model

Základné operácie by sme mali, ale to nie je všetko. Model by mal byť rozšíriteľný a podľa MVC by sme pri zmene modelu, tj. Napr. Pri pridaní ďalšej operácie, najlepšie nemali meniť ani kontrolér, či šablónu. Aby sme to dosiahli, bude potrebné ešte niekoľko vecí dorobiť.

Src / Entity / Operation.php

Symfony štandardne reprezentuje svoje dátové zdroje ako tzv. Entity. My si tak pre naše operácie kalkulačky tiež definujeme jednu takú entitu. Aj keď to na prvý pohľad možno nie je zrejmé, je to výhodný ťah nielen z hľadiska modelu, ale túto entitu môžeme ďalej využiť aj v rámci Symfony formulárov. Navyše, ako už bolo vyššie popísané, prináša nám možnosti jednoduchého rozšírenia, či skrátenie modelu o operácii, ba dokonca aj o počet členov v danej operácii. Samotná entita potom bude vyzerať takto:

<?php

namespace App\Entity;

use App\Model\Calculator;
use Symfony\Component\Validator\Constraints as Assert;

/**
 * Definice operace kalkulačky.
 * @package App\Entity
 */
class Operation
{
    /**
     * @var int Operace.
     * @Assert\NotBlank(message = "Tohle pole je povinné.")
     */
    private $operation = Calculator::ADD;

    /**
     * @var int První číslo operace.
     * @Assert\NotBlank(message = "Tohle pole je povinné.")
     * @Assert\Type(
     *     type="int",
     *     message="Hodnota {{ value }} není validní číslo."
     * )
     */
    private $x = 0;

    /**
     * @var int Druhé číslo operace.
     * @Assert\NotBlank(message = "Tohle pole je povinné.")
     * @Assert\Type(
     *     type="int",
     *     message="Hodnota {{ value }} není validní číslo."
     * )
     */
    private $y = 0;

    /**
     * Getter pro operaci.
     * @return int operace
     */
    public function getOperation(): int
    {
        return $this->operation;
    }

    /**
     * Setter pro operaci.
     * @param int $operation operace
     * @return Operation
     */
    public function setOperation(int $operation): self
    {
        $this->operation = $operation;
        return $this;
    }

    /**
     * Getter pro první číslo operace.
     * @return int první číslo operace
     */
    public function getX(): int
    {
        return $this->x;
    }

    /**
     * Setter pro první číslo operace.
     * @param int $x první číslo operace
     * @return Operation
     */
    public function setX(int $x): self
    {
        $this->x = $x;
        return $this;
    }

    /**
     * Getter pro druhé číslo operace.
     * @return int druhé číslo operace
     */
    public function getY(): int
    {
        return $this->y;
    }

    /**
     * Setter pro druhé číslo operace.
     * @param int $y druhé číslo operace
     * @return Operation
     */
    public function setY(int $y): self
    {
        $this->y = $y;
        return $this;
    }

    /**
     * Ošetření dělení nulou.
     * @return bool true jestli se dělí nulou, jinak false
     * @Assert\IsFalse(message = "Nelze dělit nulou.")
     */
    public function isDividedByZero(): bool
    {
        return $this->getOperation() == Calculator::DIVIDE && $this->getY() == 0;
    }
}

Vidíte, že v princípe sa jedná o klasickú triedu s definíciou atribútov, ich Getter a setter. Čo je tu naozaj zaujímavé sú anotácie. Pomocou nich si totiž táto trieda stráži, aké dáta je možné do jej atribútov uložiť. V neposlednom rade tiež ošetruje delenie nulou tak, že nemôže byť nastavená operácie delenia a zároveň v deliteľmi uložená nula. Ak by vás ďalej do detailu zaujímala všetky tieto validačné pravidlá, odkáže vás na oficiálnej dokumentáciu.

Src / Model / Calculator.php

Určite ste si všimli konštánt, ktoré naše Entita trieda využíva a pochádzajú z triedy Calculator. My si ich tam teraz doplníme a zároveň ešte dodáme metódy pre prácu s našou entitou operácie:

...
/** Definice konstant pro operace. */
const
    ADD = 1,
    SUBTRACT = 2,
    MULTIPLY = 3,
    DIVIDE = 4;

/**
 * Getter pro existující operace.
 * @return array asociativní pole konstant pro operace a jejich slovního pojmenování
 */
public function getOperations(): array
{
    return [
        'Sčítání' => self::ADD,
        'Odčítání' => self::SUBTRACT,
        'Násobení' => self::MULTIPLY,
        'Dělení' => self::DIVIDE
    ];
}

/**
 * Zavolá zadanou operaci a vrátí její výsledek.
 * @param Operation $operation zadaná operace
 * @return int|null výsledek operace nebo null, pokud zadaná operace neexistuje
 */
public function calculate(Operation $operation): ?int
{
    $x = $operation->getX();
    $y = $operation->getY();

    switch ($operation->getOperation()) {
        case self::ADD:
            return $this->add($x, $y);
        case self::SUBTRACT:
            return $this->subtract($x, $y);
        case self::MULTIPLY:
            return $this->multiply($x, $y);
        case self::DIVIDE:
            return $this->divide($x, $y);
        default:
            return null;
    }
}
...

Teraz by sme mali byť schopní pridať ďalšiu operáciu do modelu a pritom v kontroleru aj v šablóne nemusíme zmeniť v podstate nič. A keďže je to z modelu už naozaj všetko, môžeme sa presunúť práve na kontrolér.

Získanie modelu z prezentačného

Keď je reč o kontroleru, je jasné, že v ňom budeme potrebovať zabezpečiť prístup k nášmu modelu. To sa v Symfony štandardne rieši pomocou DI (Dependency Injection). To pre nás znamená, že vďaka štandardnej konfigurácii v súbore config/services.yml, si budeme môcť na požiadanie nechať injektovať náš model do nášho kontroleru.

Kontrolér

Teraz sa presunieme na vytváranie kontroléra.

Src / Controller / CalculatorCon­troller.php

V našom Symfony projekte si v priečinku src/Controller/ vytvoríme súbor CalculatorController.php a v ňom triedu CalculatorController, ktorá rozširuje Symfony\Bundle\FrameworkBundle\Controller\Controller. Tým sme si vytvorili základ kontroleru, do ktorej ešte doplníme predvolenú metódu pre vykresľovanie šablóny a webový formulár našej kalkulačky. Vo výsledku bude teda vyzerať takto:

<?php

namespace App\Controller;

use App\Entity\Operation;
use App\Model\Calculator;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;

/**
 * Kontroler kalkulačky.
 * @package App\Controller
 */
class CalculatorController extends AbstractController
{
    /**
     * Výchozí vykreslovací metoda tohoto kontroleru.
     * @param Request    $request    HTTP požadavek
     * @param Calculator $calculator Model pro práci s operacemi kalkulačky.
     * @return Response HTTP odpověď
     * @Route("/", name="homepage")
     */
    public function index(Request $request, Calculator $calculator): Response
    {
        $calculatorForm = $this->createFormBuilder(new Operation())
            ->add('operation', ChoiceType::class, [
                'label' => 'Operace:',
                'choices' => $calculator->getOperations(),
                'expanded' => true,
                'multiple' => false
            ])
            ->add('x', null, ['label' => 'První číslo:'])
            ->add('y', null, ['label' => 'Druhé číslo:'])
            ->add('calculate', SubmitType::class, ['label' => 'Spočítej výsledek'])
            ->getForm();

        // Zpracování formuláře kalkulačky.
        $calculatorForm->handleRequest($request);
        if ($calculatorForm->isSubmitted() && $calculatorForm->isValid())
            $result = $calculator->calculate($calculatorForm->getData());

        // Vykreslení šablony s předáním formuláře a výsledku.
        return $this->render('calculator/index.html.twig', [
            'calculatorForm' => $calculatorForm->createView(),
            'result' => isset($result) ? $result : null
        ]);
    }
}

Tu je potrebné sa pozastaviť hneď nad niekoľkými vecami. Prvý z nich je routovanie, ktoré je v Symfony štandardne riešené opäť pomocou anotácií. Rozoberať tu všetky možnosti asi nemá úplne zmysel, my sa s nimi budeme postupne zoznamovať v priebehu seriálu, ak by bol niekto veľa zvedavý, opäť odkázať na oficiálnej dokumentáciu.

Ďalej tu môžete vidieť získanie modelu pomocou DI v parametri metódy index(). To asi nevyžaduje ďalší komentár.

Potom tu vidíme vytváranie a spracovanie formulára našej kalkulačky. Spracovanie je pomerne priamočiare s delegáciou na náš model. U vytváranie by som však poukázal na skutočnosť, že celý formulár je vytváraný pomocou tzv. Builder, čo je ďalší zo štandardných Symfony postupov, priamo nad našou subjektom operácie. To umožňuje automatické doplnenie typov a validácia polí formulára práve na základe typov a validácia dát v entite samotnej.

Nakoniec vidíte vykreslenie príslušnej šablóny s odovzdaním formulára a prípadného výsledku, na ktorú sa hneď vzápätí pozrieme. :)

Šablóna (View)

Z prvej lekcie už vieme, že Symfony používa šablónovacích systém - Twig.

Templates / calculator / index.html.twig

Konkrétne šablónu vytvoríme nasledovne. Prejdeme do zložky templates/ a vytvoríme tu zložku calculator/. V nej potom vytvoríme ešte súbor index.html.twig, ktorý poslúži ako naše šablóna a my do neho napíšeme nasledujúce:

{% extends 'base.html.twig' %}

{% block stylesheets %}
    {{ parent() }}
    <style>
        .wrapper { margin: 1em auto; max-width: 800px; width: 95%; font: 18px/1.5 sans-serif; }
    </style>
{% endblock %}

{% block body %}
    <div class="wrapper">
        <h1>{% block title %}Kalkulačka{% endblock %}</h1>

        {% if result %}
            <h2>Výsledek je: {{ result }}</h2>
        {% endif %}

        {{ form(calculatorForm) }}
    </div>
{% endblock %}

Ako je vidieť, začneme definíciou bloku pre CSS, kde pomocou volania parent() zachováme prípadné štýly z nadradenej šablóny base.html.twig. Ďalej najprv upravíme <h1>, ktorý bude slúžiť zároveň aj ako html element <title> pre celú stránku. Prepíšeme zvyšný obsah na naše vypísanie výsledku treba do <h2> a pomocou {{ result }} tu zobrazíme dáta odovzdané z kontroleru. Nesmieme zabudnúť na podmienku, že výsledok sa zobrazí iba ak nie je null. Celý definovaný formulár potom vykreslíme do šablóny pomocou funkcie form() a názvu nášho formulára ( 'calculatorForm').

A tým je naša práca na šablóne aj na celom projekte hotová. Ak sa teraz pozriete na URL projektu, uvidíte tu plne funkčné kalkulačku a môžete na nej vyskúšať všetky chytáky, ktoré vás napadnú. :)

Ak vám nie je čokoľvek jasné, stiahnite si projekt z priloženého archívu a pozrite na riešenie. Kódu v aplikácii toľko nemáme, po chvíli by ste sa v princípe mali zorientovať. Ak ani to nepomôže, určite vám MVC ešte objasní seriál Jednoduchý redakčný systém v PHP objektovo (MVC).

To je pre dnešné lekciu naozaj všetko. Ale nebojte, nabudúce, v lekcii Jednoduchý redakčný systém v Symfony - Štruktúra projektu , začneme úplne novú poriadnu aplikáciu v Symfony :)


 

Mal si s čímkoľvek problém? Stiahni si vzorovú aplikáciu nižšie a porovnaj ju so svojím projektom, chybu tak ľahko nájdeš.

Stiahnuť

Stiahnutím nasledujúceho súboru súhlasíš s licenčnými podmienkami

Stiahnuté 411x (12.96 MB)
Aplikácia je vrátane zdrojových kódov v jazyku PHP

 

Predchádzajúci článok
Prvé aplikácie v Symfony
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 - Štruktúra projektu
Č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