Lekce 4 - Dokončení kalkulačky v Laravel
V minulé lekci, První aplikace v Laravel, jsme rozpracovali jednoduchou kalkulačku v Laravel. Vytvořili jsme si model, kontroler a pohled.
V dnešním kurzu si propojíme jednotlivé části naší aplikace a podíváme se také na validaci formuláře.
Routování
Abychom se mohli podívat, jak naše aplikace aktuálně vypadá, potřebujeme ke kontroleru přistoupit přes nějakou URL adresu. Na to nám slouží routování, jeho proces jsme si popsali v úvodní lekci. Pojďme si nyní definovat naší první routu, která kontroler napojí na nějakou adresu.
Definované routy pro náš web najdeme v souboru
routes/web.php
. Tento soubor je od instalace nezměněný:
<?php use Illuminate\Support\Facades\Route; /* |-------------------------------------------------------------------------- | Web Routes |-------------------------------------------------------------------------- | | Here is where you can register web routes for your application. These | routes are loaded by the RouteServiceProvider within a group which | contains the "web" middleware group. Now create something great! | */ Route::get('/', function () { return view('welcome'); });
Zatím máme jako výchozí definovanou pouze jednu routu. Jedná se o
zobrazení hlavní stránky přes GET (statická metoda get()
třídy Route
) a zpracovává se anonymní
funkcí. Takto můžeme zpracovávat jakoukoliv routu. My jsme si již
vytvořili kontroler a ten také teď využijeme. Na konec souboru přidáme
vlastní definování GET routy, kde místo vytvoření funkce odkážeme na
akci našeho kontroleru:
Route::get('calculator', [CalculatorController::class, 'index']);
Nesmíme zapomenout importovat třídu kontroleru:
use App\Http\Controllers\CalculatorController;
Nyní pokud zadáme do URL naší aplikace /calculator
, uvidíme
následující kalkulačku:
Je na čase si kalkulačku zprovoznit. Vrátíme se tedy ke kontroleru, kde zpracujeme POST formulář.
Zpracování odeslání formuláře
V našem kontroleru CalculatorController
si definujeme novou
metodu, kterou pojmenujeme calculate()
. Ta nám bude přijímat
požadavky z formuláře a následně zobrazovat výsledek:
/** * Zpracuj požadavek formuláře a zobraz výsledek spolu s formulářem kalkulačky. * * @param Calculator $calculator * @return View */ public function calculate(Calculator $calculator): View { $a = request()->input('a'); $b = request()->input('b'); $operation = request()->input('operation'); $result = $calculator->calculate($operation, $a, $b); return view('calculator', [ 'operations' => $calculator->getOperations(), 'result' => $result, 'a' => $a, 'b' => $b, ]); }
Stejně jako u zobrazení kalkulačky, i zde získáváme instanci modelu
Calculator
do proměnné $calculator
přes
dependency injection. Ta zajímavá část ale začíná až
uvnitř metody.
Pro získání odeslaných hodnot používáme metodu input()
Laravel třídy Request
, kterou získáme pomocí helper funkce
request()
. Následně získané hodnoty použijeme pro naší
metodu calculate()
, kterou jsme si definovali v minulé lekci v
modelu Calculator
. A dále zobrazíme pohled
calculator.blade.php
přes helper funkci view()
stejně jako v metodě index()
. Pohledu opět předáme dostupné
operace kalkulačky, navrch jsme však přidali výsledek a odeslaná čísla
přes formulář, abychom je opět mohli zobrazit.
Následně si vytvoříme novou routu v routovacím souboru webu
routes/web.php
stejně jako jsme to udělali výše. Nyní si však
přidáme POST akci:
Route::post('calculator', [CalculatorController::class, 'calculate']);
Všimli jste si, že jsme definovali dvě routy se stejným názvem, ale jedna je GET a druhá POST? I přesto bude naše kalkulačka bez problému fungovat. Framework si sám zjistí, o jaký požadavek se jedná, a následně zavolá danou metodu kontroleru:
- Pro zobrazení (GET) se tedy zavolá metoda
index()
v kontroleruCalculatorController
. - Po odeslání formuláře (POST) se zavolá
calculate()
ve stejném kontroleru.
Pokud však zkusíme nyní formulář odeslat, dostaneme chybu 419 i přesto, že vše by mělo fungovat. Nebo ne?
CSRF token
Laravel chrání naše požadavky proti CSRF útoku. Tato ochrana je spuštěna pro všechny akce, které nejsou čtecí (všechny krom GET). A co že je vlastně ten CSRF útok?
Představte si situaci, kdy jsme vytvořili nějaký populární blog a někdo by vytvořil úplně jinou stránku, kam by dal formulář vybízející ke vkládání vulgárních příspěvků. Tento formulář by ovšem nenechal odesílat data na jeho stránku, nýbrž na náš blog. Nic netušící uživatelé by rázem psali příspěvky na náš blog, i když by se na této stránce vůbec nenacházeli a nevěděli, že tam něco posílají.
Musíme formulář tedy opatřit CSRF tokenem, pomocí kterého se ověřuje, že požadavek byl odeslán přes naší stránku a ne přes stránku cizí.
To se dělá pomocí Blade direktivy @csrf
, která se přemění
na {{ csrf_field() }}
(tento delší zápis se používal ve
starších verzích). Direktivou bude formuláři vygenerováno skryté pole s
CSRF tokenem. Upravíme si tedy formulář v pohledu
calculator.blade.php
(ve složce resources/views
):
<form method="POST" action="/calculator"> @csrf Operace:
Pokud nyní zkusíme odeslat formulář, bude už vše fungovat tak, jak bychom si představovali.
CSRF ochrana pomocí middleware
CSRF ochrana je zajištěna jedním z middlewarů Laravel frameworku. To je
vrstva, kterou se požadavek zpracuje ještě předtím, než se dostane ke
kontroleru. Takovéto middlewary zpracovávající všechny akce najdeme
definované v souboru app/Http/Kernel.php
. Máme jich hned několik
pro skupinu web
:
protected $middlewareGroups = [ 'web' => [ \App\Http\Middleware\EncryptCookies::class, \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class, \Illuminate\Session\Middleware\StartSession::class, // \Illuminate\Session\Middleware\AuthenticateSession::class, \Illuminate\View\Middleware\ShareErrorsFromSession::class, \App\Http\Middleware\VerifyCsrfToken::class, \Illuminate\Routing\Middleware\SubstituteBindings::class, ], 'api' => [ 'throttle:api', \Illuminate\Routing\Middleware\SubstituteBindings::class, ], ];
Přes tyto všechny middlewary projde náš požadavek, než se dostane k metodě našeho kontroleru. Můžeme vidět, že dalšími z nich jsou například šifrování cookies a vytvoření relace (session). Pokud někdy budete chtít vytvářet svůj vlastní middleware, toto je přesně místo, kde ho budete přidávat. Následně tak bude aplikován pro všechny akce na vaší webové stránce.
Validace formuláře
Jako programátor musíme předpokládat, že ne vždy uživatel vyplní do políčka to, co po něm požadujeme. Pokud zkusíme zadat do políčka s číslem text, naše aplikace vyvolá chybu, protože model dokáže přijímat pouze celá čísla:
Tomu se můžeme vyhnout validací. Validační pravidla lze
definovat v kontroleru přímo v metodě dané akce. My využijeme pouze pár
pravidel, všechny lze najít v oficiální
dokumentaci. V našem případě se jedná o to, že všechna políčka
musí být vyplněná a že požadujeme pouze celá čísla. Také vybraná
operace musí být podporovaná naším modelem. Upravíme si tedy metodu
calculate()
v našem kontroleru:
/** * Zpracuj požadavek formuláře a zobraz výsledek spolu s formulářem kalkulačky. * * @param Calculator $calculator * @return View * * @throws ValidationException */ public function calculate(Calculator $calculator): View { $this->validate(request(), [ 'a' => ['required', 'integer'], 'b' => ['required', 'integer', 'not_in:0'], 'operation' => [ 'required', Rule::in(array_keys($calculator->getOperations())), ], ]); $a = request()->input('a');
Začneme od anotace, kdy místo zpracování výjimky
ValidationException
ji předáme frameworku. Ten se o ní sám
dokáže postarat, kdy uživatele přesměruje zpět i s chybami, kterých se
dopustil.
Následně voláme metodu validate()
, která je definovaná v
třídě Controller
a náš kontroler ji dědí. Té musíme
předat instanci třídy Request
, již lze získat opět přes
helper funkci request()
. Jako druhý parametr předáváme pole s
nastavenými validačními pravidly. Ty mohou být definované pouze jako text.
Pro jiné, jako například pravidlo in
, je lepší použít
třídu Rule
. Také si všimněte, že jsme vyřešili problém pro
dělení nulou, kdy druhé číslo nesmí být 0
.
Myslím si, že další pravidla není třeba vysvětlovat, všechny jsou popsané v uvedené dokumentaci.
Pokud vaše prostředí nedokáže automaticky rozpoznat třídy z jiného jmenného prostoru, nezapomeňte si na začátek souboru třídy kontroleru přidat následující importy:
use Illuminate\Validation\Rule; use Illuminate\Validation\ValidationException;
Vypsání chyb
Nyní už stačí jen vypsat chyby v našem pohledu, abychom informovali
uživatele o omylech, kterých se dopustil. Chyby jsou obsažené v proměnné
$errors
, ta je automaticky předávaná všem pohledům.
Takovýchto "nedefinovaných" proměnných je více, nám však stačí zatím
pouze tato. Upravíme si tedy náš pohled a pod nadpis přidáme výpis
chyb:
<h1>Kalkulačka</h1> @if ($errors->any()) <ul> @foreach ($errors->all() as $error) <li>{{ $error }}</li> @endforeach </ul> @endif <form method="POST" action="/calculator">
$errors
je instance třídy ViewErrorBag
. Pro
zjištění, zda-li vůbec existují nějaké chybové hlášky, abychom
nezobrazovali prázdný seznam, použijeme metodu any()
. Všechny
chybové hlášky poté získáme přes metodu all()
a vypíšeme
je. Pokud byste však chtěli získat chybovou hlášku pouze pro určité
políčko, lze použít metodu first()
a předat ji název
políčka:
$errors->first('email')
Nám každopádně postačí i pouze seznam chyb
Pokud zkusíme zadat jako druhé číslo nulu a přes "Inspect element" (klávesa F12 v prohlížeči) vytvoříme neexistující operaci kalkulačky, dostaneme dvě chybové hlášky:
Nyní máme plně fungující kalkulačku vytvořenou přes Laravel, na které jsme si ukázali naprosté základy tohoto PHP frameworku. Pokud vám není cokoliv jasné, stáhněte si projekt z přiloženého archivu a podívejte se na řešení. Můžete se též zeptat v diskuzi pod článkem, pokud-li čelíte nějakému problému. Každopádně s potřebnými znalostmi, které jsem uvedl v první lekci, by vše mělo být po chvilce jasné.
V následujícím kvízu, Kvíz - Výpis, routy a MVC v Laravel, si vyzkoušíme nabyté zkušenosti z předchozích lekcí.
Měl jsi s čímkoli problém? Stáhni si vzorovou aplikaci níže a porovnej ji se svým projektem, chybu tak snadno najdeš.
Stáhnout
Stažením následujícího souboru souhlasíš s licenčními podmínkami
Staženo 280x (11.99 MB)
Aplikace je včetně zdrojových kódů v jazyce PHP