PHPPamokos.lt


9. Formos ir jų validacija

Formos yra labai dažnas puslapių elementas - jos reikalingos ir registracijai, ir informacijos pildymui, ir naujienlaiškio prenumeratai. Panagrinėkime, kokius patogumus čia siūlo Laravel.

Ne veltui praeitoje pamokoje išmokome, kaip diegti išorinius paketus - būtent toks paketas mums padės dirbti su formomis. Taigi, į savo composer.json failą į "require" skiltį įrašykite štai ką:
"laravelcollective/html": "~5.4"
Tada praleiskite composer update ir paketas bus įdiegtas.

Dar turime padaryti du pakeitimus faile config/app.php - užregistruoti šio paketo Service Provider ir Facade. Į masyvą providers įrašomę naują reikšmę
Collective\Html\HtmlServiceProvider::class,
Ir į masyvą aliases įrašomę naują reikšmę
'Form' => Collective\Html\FormFacade::class

Ir tada galime naudotis Form klase. Pradėkime nuo Views dalies - Blade šablonų kalba siūlo patogių funkcijų. Vietoje HTML rašymo galime kiek sutaupyti laiko ir rašyti taip:

HTML variantas:
<form action="http://www.website.com/search" method="post" enctype="multipart/form-data">
Blade variantas:
{!! Form::open(array('url' => 'search', 'files' => true)) !!}
Rašymas gaunasi trumpesnis ir šiek tiek suprantamesnis - funkcijai Form::open() paduodami reikiami parametrai. Yra ir naudingų sutrumpinimų - kaip pateiktame pavyzdyje parametras files=true įterpia eilutę enctype="multipart/form-data", kuri reikalinga, jei jūsų formoje yra įkeliami failai. Arba patogu, kad nereikia nurodinėti metodo POST - jis priskiriamas pagal nutylėjimą.

Lygiai taip pat vietoj </form> galima rašyti:
{!! Form::close() !!}
Atkreipkite dėmesį, kad yra naudojami {!! !!} skliaustai, o ne {{ }}, nes funkcijų darbo rezultatas yra HTML kodas, kuriam nereikia funkcijos htmlentities().
 

Formos laukai

Kaip mes įprastai HTML ir PHP kalbose kuriame formos laukus:
<input type="text" name="title" value="Default title" />
<input type="text" name="title" value="<?=$title;?>" />
<textarea name="fulltext">A big text</textarea>
Kaip tą patį siūlo daryti Laravel:
{!! Form::text('title', 'Default title') !!}
{!! Form::text('title', $title) !!}
{!! Form::textarea('fulltext', 'A big text') !!}
Turbūt pastebėjote dėsningumą - formos laukų funkcijos turi du parametrus: lauko pavadinimą ir priskirtą reikšmę. Iš tikro, parametrų gali būti ir daugiau, kaip trečią parametrą galima paduoti masyvą papildomų atributų, kurie bus surašyti kaip HTML lauko atributai. Pvz:
{!! Form::text('field_name', $field_value, array('id' => 'field_id', 'class' => 'textfield')) !!}
HTML kodas gaunasi toks:
<input type="text" name="field_name" value="<?=$field_value;?>" id="field_id" class="textfield" />
Taip pat apžvelkime kitų laukų sintaksę:

1. Tekstinis laukas atitinkantis el.pašto adresą:
{!! Form::email('email', $email_value) !!}
2. Label prie formos lauko "email":
{!! Form::label('email', 'Email') !!}
3. Radio mygtukas:
{!! Form::radio('color', 'red', true) !!} Red
{!! Form::radio('color', 'black') !!} Black
Trečias parametras su reikšme true nurodo kad mygtukas turi būti checked.

4. Checkbox mygtukas:
{!! Form::checkbox('terms', '1', true) !!} I agree with terms
Trečias parametras su reikšme true nurodo kad mygtukas turi būti checked.

5. Select iškrentantys sąrašai (dropdowns):
Pradžioje užpildome reikšmių masyvą:
$cities = array(
  '1' => 'Vilnius',
  '2' => 'Kaunas',
  '3' => 'Alytus'
);
O po to jį paduodame į Blade šabloną:
{!! Form::select('city', $cities, 1) !!}
Trečias parametras su reikšme 1 nurodo, kuris variantas parenkamas kaip aktyvus su selected.

6. File laukai:
{!! Form::file('photo') !!}
7. Hidden laukai:
{!! Form::hidden('action', $action) !!}
8. Submit mygtukas:
{!! Form::submit('Save data') !!}
Atkreipkite dėmesį, kad submit mygtukui nereikia nurodyti name, reikia tik value.
 
Svarbi tema yra ir formos užpildymo saugumas bei apsauga nuo CSRF - kad talentingi įsilaužėliai nepripildytų laukų savo norimomis reikšmėmis ir nenulaužtų jūsų sistemos. Vienas iš populiaresnių įsilaužimo būdų yra taip vadinamas "Cross-site request forgery" arba trumpiau CSRF. Jo esmė - kad jūsų skripto rezultatas gali teoriškai būti kraunamas iš kito puslapio ar visai iš kitos formos.

Labiausiai paplitęs apsaugos nuo CSRF būdas yra formoje pridėti papildomą identifikatorių-lauką, kuris būtų tikrinamas apdorojant duomenis ir užtikrintų, kad duomenys ateina būtent iš tos formos ir tik vieną kartą.

Laravel, naudojant šį HTML paketą, automatiškai į formą prideda hidden lauką, kuris ir pasirūpina visa apsauga, mums nedarant nieko papildomai.

Ir viskas, Token lauko patikrinimas vyks automatiškai, ir jei jis nebus korektiškas - bus gauta klaida TokenMismatchException. Negi nenuostabu?
 

Formos apdorojimas

Kaip mes pripratę gauti duomenis iš užpildytos formos? Iš $_POST arba $_GET masyvų, ar ne? Laravel čia siūlo savo pagalbininką pavadinimu Request. Taigi, sakykime, Controlleryje po formos užpildymo kviečiame funkciją store() ir jos parametras yra Request tipo kintamasis:
public function store(Request $request)
{
  $name = $request->input('name');
}
Taip vietoje $_POST['name'] mes rašome $request->input('name'). Atkreipkite dėmesį, kad paties parametro $request perdavinėti nereikia - Controllerio mechanizmas į metodus store() ir update() perduoda šį kintamąjį automatiškai. Dar patogus dalykas - patikrinti, ar POST laukas egzistuoja ir reikšmė nėra tuščia:
if ($request->has('title')) { ... }
Beje, funkcija has() egzistuoja ir kitose pagalbinėse laravel klasėse, tokiose kaip Session - bet apie tai tolimesniuose skyreliuose.
 

Form Request validacija

Be abejo, formos užpildymas gali nepavykti iš pirmo karto - turime patikrinti, ar duomenys teisingi. Tam Laravel turi patogų validacijos mechanizmą Form Requests - tiesiog sukuriame atskirą Requests klasę ir joje nurodome, kokius duomenis ir kaip patikrinti.

Komandinėje eilutėje įrašome:
php artisan make:request CreateUserRequest
Čia CreateUserRequest yra mūsų sugalvotas klasės pavadinimas. Ir susigeneruoja failas app/Http/Requests/CreateUserRequest.php:
namespace App\Http\Requests;

use App\Http\Requests\Request;

class CreateUserRequest extends Request
{
  /**
   * Determine if the user is authorized to make this request.
   *
   * @return bool
   */
  public function authorize()
  {
    return false;
  }

  /**
   * Get the validation rules that apply to the request.
   *
   * @return array
   */
  public function rules()
  {
    return [
      //
    ];
  }
}
Kaip matote, yra dvi funkcijos - authorize() ir rules(). Pirmoje reikia patikrinti, ar šiuo metu prisijungęs vartotojas turi teisę atlikti šį veiksmą, o antroje surašomos taisyklės konkretiems formos laukams. Pirmoje authorize() funkcijoje siūlau visada daryti return true; ir prisijungimo patikrinimus atlikti kitais būdais - apie juos kalbėsime kitoje pamokoje. Pereikime prie įdomesnės dalies - rules() surašymo. Mūsų užduotis - šioje funkcijoje užpildyti validacijos taisykles - tiksliau, jos masyve. Kiekvienam formos laukui galime priskirti po vieną ar kelias taisykles. Sakykime, taip:
[
  'name' => 'required',
  'email' => 'required|email'
];
Panagrinėkime, kokios taisyklės gali būti čia naudojamos. Jų yra daug, bet išvardinsime populiariausias:

required Reiškia, kad laukas privalomas
email Reiškia, kad laukas turi būti el.pašto adresas
alpha Laukas tik iš abėcėlės raidžių (a-z)
alpha_num Laukas tik iš abėcėlės raidžių (a-z) ir skaičių (0-9)
date Laukas yra data (turi atitikti PHP funkciją strtotime())
min:X Lauko reikšmė turi būti ne mažiau X, o jei laukas eilutė tai ne trumpesnė nei X simbolių
max:X Lauko reikšmė turi būti ne daugiau X, o jei laukas eilutė tai ne ilgesnė nei X simbolių
exists:table,column Laravel patikrina, ar reikšmė egzistuoja DB lentelėje "table" laukelyje "column"
unique:table,column Laravel patikrina, ar reikšmė neegzistuoja DB lentelėje "table" laukelyje "column"
Kaip matėte pavyzdyje, vienam laukui galima taikyti kelias taisykles, atskiriant jas | simboliu.

Dabar - apie tai, kaip panaudoti tą mūsų sukurtą Request klasę. Ogi Controlleryje mūsų funkcijai store() reikia paduoti būtent mūsų klasės tipo kintamąjį:
public function store(CreateUserRequest $request)
{
  // ...
}
Ir viskas, jokios pačios validacijos aprašinėti nereikia - Laravel pasirūpins, kad laukų reikšmės būtų patikrintos, ir jei duomenys neatitinka aprašytų taisyklių, bus atliktas veiksmas return back();, kas realiai reiškia, kad vartotojas bus perkeltas atgal į formos puslapį. Maža to, automatiškai bus perduodami ir formos duomenys laukai, ir klaidų masyvas $errors. Kurį apdoroti reikia taip:
@if($errors->any())
  @foreach ($errors->all() as $error)
    <div>{{ $error }}</div>
  @endforeach
@endif
 

Praktika: mūsų formų perdarymas į Blade

Taigi, su naujomis žiniomis puolame jas pritaikyti ir, kaip jau sakiau anksčiau, tobuliname savo sukurtas formas - tas, kur administratorius gali valdyti miestus, aikštelių tipus ir pačias aikšteles. Čia parodysiu, kaip bus su miestais, o kitas dvi formas, khm, paliksiu namų darbams.

Pradedame nuo Blade ir nuo failo resources/views/admin/cities/create.blade.php, jis dabar atrodys taip:

@extends('base')

@section('content')
<h2>Naujas miestas</h2>

@if($errors->any())
@foreach ($errors->all() as $error)
{{ $error }}<br />
@endforeach
<br />
@endif

{!! Form::open(array('url' => 'admin/miestai')) !!}
Pavadinimas:
<br />
{!! Form::text('title', old('title')) !!}
<br />
{!! Form::submit('Saugoti') !!}
{!! Form::close() !!}
@stop
Visus formos elementus pakeičiame Blade komandomis. Taip pat išvedame klaidas, jei tokių bus. Atkreipkite dėmesį į old() - ši funkcija išves seną reikšmę, jei forma nepraeis validacijos. Kalbant apie validaciją, einame prie Request dalies - sugeneruojame štai tokią klasę:
php artisan make:request CreateCityRequest
Ir tada pačioje klasėje...
class CreateCityRequest extends Request
{
  public function rules()
  {
    return [
      'title' => 'required|min:3'
    ];
  }
}
Ir dabar prie Controllerio - app/Http/controllers/AdminCitiesController.php ir pakeičiame funkciją store():
use App\Http\Requests\CreateCityRequest;

class AdminCitiesController extends Controller {

// ...

  public function store(CreateCityRequest $request)
  {
    $city = new City;
    $city->title = $request->input('title');
    $city->save();
    return redirect('admin/miestai');
  }
}

Taigi, jeigu miesto pavadinimas tuščias ar trumpesnis nei 3 simboliai - mus grąžins į tą pačią formą su klaidos pranešimu. Dar padarykime taip, kad klaidos pranešimas būtų lietuviškas, o ne bendras "The title must be at least 3 characters.".

Dar atkreipsiu dėmesį, kad redagavimo formos atidarymas atrodys kiek kitaip - jis kreipiasi su PUT metodu į funkciją update():

{!! Form::open(array('url' => 'admin/miestai/' . $city->id, 'method' => 'put')) !!}

Štai ir viskas. Kitų dviejų formų perdarymas ir validacija, kaip sakiau, yra jūsų namų darbai. Aišku, tradiciškai, jeigu tingite ar tiesiog norėsite pasitikrinti - štai archyvas parsisiuntimui.



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