PHPPamokos.lt


8. Request Object ir patogesnis Validation procesas

Formų validacija yra turbūt vienas dažniausiai pasikartojančių funkcionalumų šiuolaikiniuose web-projektuose - to reikia praktiškai visiems. Ir Laravel jau turi savyje patogią Validator klasę. Bet jau nuo seno kyla ginčai - kaip architektūriškai ją geriau naudoti - per Controller ar Model nelankstu (nes patogiau atskirti validaciją į atskirus failus), bet kurti atskirą struktūrą vien validacijai - papildomas darbas. Tai Laravel 5 versijoje pasirodė toks dalykas kaip Form Request Validation.

Iš karto reikia pabrėžti, kad senas Validator variantas vis dar veikia, ir vis dar galite jį naudoti, bet galbūt, perskaitę šį skyrelį, persigalvosite ir pradėsite naudoti naują būdą.

Kiek plačiau apie validacijos problemą

Turbūt, jei mažai dirbote su projektų architektūriniais sprendimais, galvojate - o kur čia iš viso yra bėda? Juk įprasta - Controlleryje pvz funkcijoje store() iškvieti validaciją, jei nepraeina - parodai klaidą, jei praeina - įvykdai save(). O bet tačiau: pagal visą MVC logiką ir filosofiją Controlleris turi atsakyti tik už kontroliavimą - užklausos gavimą, modelių iškvietimą ir view atvaizdavimą. Modelis atsakingas tik už darbą su duomenų baze. Tai validacija, struktūriškai žiūrint, neįsipaišo nei ten, nei ten.

Dar kitas dalykas - skirstant visą projektą "logikos sluoksnis", daug lanksčiau yra validaciją turėti kaip kažkokį atskirą sluoksnį, kuris dirbtų vien tik su validacija, ir kad jei kažkas keistųsi - galima būtų pakeisti validaciją kitokiomis sąlygomis būtent tame atskirame sluoksnyje ir nesirūpinti dėl pagrindinės logikos veikimo. Tuo pačiu taip atskirtas kodas taptų lengviau skaitomas ir lankstesnis.

Ir galiausiai problema - jei turite krūvą formų su validacija, laukai gan dažnai pasikartoja, ir gaunasi DRY (Don't Repeat Yourself) principo pažeidimai bei nemažai copy-paste kodo.

Tai ir Laravel 4 versijoje jau buvo bandymų atskirti validaciją į atskirus servisus - apie tai rašė Sitepoint, Culttt ir kiti.

Naujas sprendimas - Form Request Validation

Kai pasiaiškinome problemą, atėjo laikas pažiūrėti į sprendimą - kaip penktoje Laravel versijoje Taylor Otwell atskyrė validacijos funkcionalumą. Jei skaitėte skyrelį apie Middleware tai čia bus labai panašus principas. Pirmiausia su Artisan sukuriame Request klasę:

php artisan make:request CreateUserPostRequest

Klasės pavadinimas CreateUserPostRequest gali būti koks tik norite, bet priimta laikytis tokio standarto: [Veiksmas][Objektas][Metodas]Request. Sakykime, jei kurtumėte klasę knygų update() veiksmo validacijai, pavadinimas galėtų būti UpdateBookPostRequest.

Po šios komandos vykdymo automatiškai susikuria failas kataloge app/Http/Requests (ar prisimenate logiką, kad viskas kas susiję su URL užklausų apdorojimu buvo iškelta į katalogą app/Http?).

namespace App\Http\Requests;

use App\Http\Requests\Request;

class CreateUserPostRequest extends Request {

  public function authorize()
  {
    return true;
  }

  public function rules()
  {
    return [];
  }

}

Ši Request klasė reikalinga užklausos apdorojimui - pasinaudojus šia klase, iš Controllerio nereikės iš viso kviesti validacijos - tereikės priskirti FormRequest klasę, kurią naudojame. O šioje klasėje yra du metodai:

  • Metodas authorize() skirtas vartotojo teisių patikrinimui ir grąžina true, jei su teisėmis viskas gerai, o jei vartotojas neturi teisės prieiti prie šios informacijos apdorojimo - grąžiname false (tada naršyklė grąžina HTTP kodą 403). Jei tokio patikrinimo jums nereikia ar jis vykdomas kažkur prieš tai, tiesiog grąžinkite return true;.
  • Metodas rules() grąžina seną gerą mums pažįstamą validacijos laukų masyvą, kurį Laravel ir naudos validacijos patikrinimui. Čia ir aprašome, kuris laukas privalomas, ar jis turi būti skaičius ar dar kas nors - visas šias taisykles.

Kaip panaudoti FormRequest klasę

Dabar pažiūrėkime į mūsų Controllerį ir į tai, kaip panaudoti mūsų ką tik sukurtą CreateUserPostRequest. Konkrečiai - duomenų įterpimo metodas store() dabar atrodys taip:

public function store(CreateUserPostRequest $request)
{
    User::create($request->all());
    return redirect('users');
}

Ir viskas. Nereikia jokios validacijos kviesti ar kažko tikrinti - visas tas funkcionalumas ramiai gyvena toje FormRequest klasėje, kurią tiesiog reikia paduoti kaip parametrą su kintamuoju $request. Tiesa, atkreipkite dėmesį, kad naudojame ne Input::all() duomenis, o $request->all() - jau atfiltruotą variantą.

Pastaba. Nepamirškite Controllerio pradžioje įkelti tos FormRequest klasės naudojimo: use app/Http/Requests/CreateUserPostRequest.

Ir paskutinis klausimas šiame skyrelyje - ar naudoti tą pačią FormRequest klasę ir store(), ir update() metodams. Tai galima daryti, jeigu validacijos taisyklės visiškai vienodos. Bet jei skiriasi - sakykime, įterpiant privalomas asmens kodas, o atnaujinant jo keisti nebegalima - tada kuriame atskirą klasę UpdateUserPostRequest, ir tada naudojame ją metode function update(UpdateUserPostRequest $request).

Controller Method Injection

Pabaigai - žvelgiant į FormRequest panaudojimą, negalima nepaliesti dar vienos Laravel 5 galimybės - kuri ir leidžia "magiškai" panaudoti išorinę validaciją ir apdoroja visą tą fone, nerašant papildomo kodo.

Laravel 4 versijoje esantis IOC Container leido įkelti klasės objektą (kuris dažniausiai priklausė kažkokiam interfeisui) kaip parametrą į kitos klasės konstruktorių, taip užtikrinant lankstumą:

class UserController extends BaseController {

  public function __construct(UserRepositoryInterface $users)
  {
    $this->users = $users;
  }

}

Laravel 5 versijoje tokia galimybė atsirado ne tik konstruktoriams: dabar custom-klasės objektą kaip parametrą galima įkelti į bet kokį metodą. Būtent tuo pagrindu ir veikia FormRequest funkcionalumas, prisiminkime:

public function store(CreateUserPostRequest $request)
{
    User::create($request->all());
    return redirect('users');
}

Laravel, mums nematant, atras klasės CreateUserPostRequest aprašymą, atliks joje numatytus veiksmus, ir tada pratęs mūsų metodo store() vykdymą.



(c) 2015-2018. Visais klausimais kreipkitės povilas@laraveldaily.com