PHPPamokos.lt


4. Routes, URL adresų apdorojimas ir Middleware

Šioje pamokoje praplėsime žinias apie Routes veikimą - išmoksime ne tik iškviesti sudėtingesnius Views failus, bet ir struktūrizuosime visą kvietimų procesą.

Taigi, klausimas: kaip mes galime pasakyti Laraveliui, kad URL adresas /kontaktai turi parodyti mūsų kontaktinę informaciją? Kaip jau žinome, tam skirtas failas routes/web.php - jeigu teisingai atlikote praeitos pamokos namų darbus, tai jis turėtų atrodyti daugmaž taip:
Route::get('/', function () {
  return view('home');
});
Route::get('aiksteles', function() {
  return view('search');
});
Route::get('apie', function() {
  return view('about');
});
Route::get('kontaktai', function() {
  return view('contact');
});
Kiekvienas toks Route priskyrimas turi tris dalis:
  • Metodas (GET, POST ar kt.) - eina po dviejų dvitaškių, pvz Route::get
  • URL adresas (pirmas parametras skliausteliuose)
  • Closure funkcija kuri atlieka veiksmą (antras parametras)
 

Route metodai

Pradėkime aiškintis nuo pirmo dalyko - metodų. Jie gali būti tokie:
  • Route::get()
  • Route::post()
  • Route::put()
  • Route::delete()
  • Route::any()
Trečiojo ir ketvirtojo varianto (PUT ir DELETE) jūs tiesiogiai greičiausiai nekviesite - vizualiam svetainės veikimui pilnai galima apsiriboti su GET ir POST metodais: pirmasis suveikia, tiesiog įrašius adresą naršyklėje, o antrasis - užpildžius formą puslapyje ir perduodant duomenis per POST.

Turėkite omenyje, kad jeigu aprašysite tam tikram adresui tik POST metodą, tai įrašę tą patį adresą tiesiog naršyklėje - gausite Laravel klaidą:
Route::post('apie', function()
{
  return view('about');
});
Tiesa, čia reikia paminėti, kaip Laravel parodo klaidas naršyklėje - čia matote detalią klaidos analizę, bet ji pasirodys tik tada, kai konfigūracijoje įjungsite debug režimą - tai yra faile .env pagrindiniame kataloge:
APP_DEBUG=true
Jei šis parametras bus nustatytas kaip false, tada Laravel rodys tik bendrą klaidą "Whoops, looke like something went wrong" - būtent taip turėtumėte nustatyti "gyvoje" svetainėje.

Jei debug režimas išjungtas ir naršyklėje matomas tik "Whoops" užrašas, realią klaidą vis tiek galima pamatyti, tik ji paslėpta nuo lankytojų - juk mes nenorime, kad jie matytų mūsų puslapio realų kodą, klaidas ir užklausas, tai būtų labai nesaugu (dėl to "gyvose" svetainėse visada išjunkite debug). Klaidos įrašomos į klaidų logą, kuris yra faile storage/logs kataloge.

 
Na ir galiausiai yra metodas Route::any(), kuris apdoroja bet kurį iš keturių kitų metodų. Asmeniškai nerekomenduoju jo naudoti, nes tada vis tiek viduje turėsite tikrinti, koks metodas yra kviečiamas - geriau tai patikėti Routes failui.
 

Route: URL adresai

Antra Route kreipinio dalis yra URL adresas, kuris apdorojamas iš naršyklės: Galite nurodyti ir gilesnę URL struktūrą:
Route::get('katalogas/automobiliai/lexus', function()...
O jei norite apdoroti titulinio puslapio adresą (kaip mes jau tai darėme praeitoje pamokoje), naudokite '/' struktūrą:
Route::get('/', function()...
 

Route: Closure funkcija

Trumpai - tiems, kas neprisimena: Closures yra anoniminės funkcijos, kurios pasirodė PHP kalboje nuo versijos 5.3. Tai yra, kaip vieną iš funkcijos parametrų galima aprašyti kitą funkciją, kuri net neturi pavadinimo ir naudojama būtent tik ten. Tiesa, galima Closure funkciją aprašyti atskirai ir priskirti jai kintamąjį, plačiau apie tai galite pasiskaityti oficialioje PHP dokumentacijoje.

Taigi, funkcijos viduje galite rašyti bet kokį PHP kodą ar logiką, svarbiausia yra grąžinti tekstą su return komanda. Kaip ir jau matėme anksčiau, dažniausiai grąžinamas view() rezultatas, bet yra dar bent keli variantai. Kad ir paprastai galima tiesiog išvesti norimą tekstą:
Route::get('apie', function()
{
  return 'Tekstas apie mus';
});
 

Route: URL parametrai

Be abejo, didesniame projekte URL adresai nebūna visi statiniai - kaip aprašyti parametrus, kai adresas gali būti pvz /katalogas/[kategorija] arba /preke/[id]? Route komanda atrodo taip:
Route::get('katalogas/{category}', function($category)
{
  return view('catalog', ['category' => $category]);
});
Kaip matote, parametras URL adrese pasirodo su figūriniais skliaustais {category}, o Closure funkcijoje kaip kintamasis $category su tuo pačiu pavadinimu.

Gali būti ir daugiau nei vienas parametras:
Route::get('katalogas/{category}/{item}', function($category, $item)
{
  return view('catalog',
    ['category' => $category, 'item' => $item]);
});
Gali būti aprašomi ir tokie parametrai, kurie gali ir būti, ir nebūti (beveik kaip Šekspyras) - pvz kad viena taisykle aprašytume URL adresą ir su paieškos parametrais, ir be. Tam prie parametro pabaigoje tiesiog įrašomas klaustukas.
Pavyzdys:
Route::get('search/{category?}', function($category = null)
{
  return view('catalog',
    ['category' => $category]);
});
Šiame pavyzdyje suveiks ir adresas /search, ir adresas /search/phones. Atkreipkite dėmesį, kad priskiriama ir reikšmė pagal nutylėjimą (null, ar galite priskirti ir kitokią), nes kitaip gausite Laravel klaidą.
 

Route: JSON ir kiti Response

Kartais reikia grąžinti ne HTML tekstą į naršyklę, o kitokį rezultatą - pavyzdžiui JSON masyvą. Tam tikslui Laravel turi klasę Response. Jei dar tiksliau, Response klasė yra paimta iš Symfony frameworko, tai jei kada teks dirbti su Symfony, logika ten bus labai panaši.
Štai kaip tai atrodo:
Route::get('json', function()
{
  return Response::json(
    ['category' => 'cars', 'item' => 'toyota-prius']);
});
Kitas variantas - grąžinti failą parsisiuntimui, pvz kokį PDF. Tai vėlgi daroma per Response:
Route::get('download/{file}', function($file)
{
  return Response::download($file);
});
 

Route: Redirect

Dar vienas iš URL apdorojimo rezultatų - po kažkokių veiksmų perkėlimas į kitokį puslapį. Tai daroma su komanda Redirect::to()
Route::post('login', function()
{
  // veiksmai prisijungimui
  return redirect('welcome');
});
Taip pat galima perduoti kintamuosius kartu su Redirect perkėlimu:
Route::post('login', function()
{
  // veiksmai prisijungimui
  return redirect('welcome')->with('message', 'Welcome home!');
});
Šiuo atveju kintamasis message yra laikinai saugomas sesijoje vienam puslapiui, ir jį galima bus pasiekti su sesijos komanda Session::get('message'). Apie sesijas ir jų apdorojimą per Laravel kalbėsime vėliau atskirame skyrelyje.

 

Route su pavadinimais

Dar vienas patogus dalykas, ypač dirbant su didesniu projektu, yra Route komandai suteikti konkretų pavadinimą, kurį galima bus naudoti kitose komandose. Patogumas yra tas, kad jeigu norėsite pakeisti kažkokio puslapio URL adresą, ir jis bus naudojamas keliose vietose, tai užteks pakeisti adresą tik vienoje vietoje - kur aprašytas tas Route veiksmas.
Pavyzdys:
Route::get('first', array('as' => 'welcome', function()
{
  return view('welcome');
}));
Ir dabar jau galime naudoti tą welcome pavadinimą kituose sakiniuose:
Route::post('login', function()
{
  // veiksmai prisijungimui
  return redirect()->route('welcome');
});
arba Blade šablonuose - nuorodose:
<a href="{{ route('welcome') }}">Sveiki atvykę</a>
 

Route: Middleware

Praktinis pavyzdys: sakykime, kad turime projekto sritį tik administratoriams ir norime prieš kiekvieną puslapio užsikrovimą patikrinti, ar žmogus prisijungęs. Tokiems atvejams ir yra naudojamos Middleware klasės - kai reikia įvykdyti kažkokį patikrinimą dar prieš pagrindinę Route komandos logiką.

Pačios Middleware klasės aprašomos kataloge app/Http/Middleware. Keturios tokios klasės mums siūlomos kartu su Laravel diegimu, o bendrai tokios klasės struktūra atrodo taip - pažiūrėkime į pavyzdį:
namespace App\Http\Middleware;

use Closure;

class AdultMiddleware
{

  /**
  * Handle an incoming request.
  *
  * @param \Illuminate\Http\Request $request
  * @param \Closure $next
  * @return mixed
  */
  public function handle($request, Closure $next)
  {
    if (Session::get('age') < 18) {
      return redirect('/home');
    }

    return $next($request);
    }
}
Esmė tokia - visas veiksmas vyksta funkcijoje handle(), parametrus galite palikti tuos pačius, ir return sakinio irgi nelieskite. O tarp jų ir yra visas patikrinimas - jeigu kažkokia užklausos sąlyga netenkinama (pvz mūsų atveju pagal sesiją vartotojo amžius iki 18 metų) - tada su return komanda kvieskite klaidą arba darykite, kaip šiuo atveju, redirect į klaidos puslapį. Dabar - kaip Middleware panaudoti ir priskirti konkrečiam adresui? Visų pirma, reikia duoti mūsų Middleware klasei sutrumpintą vardą, tai daroma faile app/Http/Kernel.php, ten yra toks masyvas:
protected $routeMiddleware = [
  'auth' => \App\Http\Middleware\Authenticate::class,
  'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
  'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
];
Prie jo apačioje pridedame savo Middleware klasę ir pavadiname ją, tarkime, adult:
protected $routeMiddleware = [
  'auth' => \App\Http\Middleware\Authenticate::class,
  'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
  'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
  'adult' => \App\Http\Middleware\AdultMiddleware::class,
];
Ir tada galima filtrą naudoti jau faile app/Http/routes.php:
Route::get('welcome', array('before' => 'adult', function()
{
    return view('welcome');
}));
Logika tokia - jeigu before nustatytas Middleware suveikia taip, kad ten vykdoma komanda return, tai paties pagrindinio Route bloko komandos jau nebevykdomos - kitaip tariant, užklausa "nepraeina filtro".

Taip pat gali būti Middleware ne tik before, bet ir after tipo - sintaksė analogiška, o pritaikymas labiau veiksmams, kurie reikalingi po viso puslapio apdorojimo - pavyzdžiui, statistikai įrašyti į log failus.

Apie Laravel siūlomus Middleware (pvz auth ar csrf) ir jų panaudojimą kalbėsime ateityje atskirame skyrelyje.
 

Route: grupavimas

Dar vienas funkcionalumas - kad Route užklausas galima grupuoti, pritaikant tą patį Middleware. Sakykime, kelis adresus norite apsaugoti autorizacijos filtru, tai atrodytų taip:
Route::group(array('before' => 'auth'), function()
{
    Route::get('dashboard', function()
    {
        return view('dashboard');
    });

    Route::get('user_profile', function()
    {
        return view('user_profile');
    });
});
Tai tiek apie Route galimybes. Jų yra ir daugiau, bet apsiribojau plačiausiai pritaikomais, kad nebūtų per daug informacijos iš karto. Jei norite pasigilinti dar giliau, galite kreiptis į oficialią dokumentaciją.
 

Namų darbai

Šios pamokos namų darbų tikslas - tiesiog patiems atkartoti visus Routes variantus, paminėtus pamokoje.

Pradžioje - sakykime, kad turite View failą resources/views/page.php. Su juo padarykite štai ką:
  • Kreipkitės į jį su GET metodu ir view() funkcija
  • Kreipkitės į jį su GET metodu ir view() funkcija su parametrais - perduokite kokį nors kintamąjį, pvz Title
  • Iš praeitos pamokos: panaudokite Blade sakinius šablone. Pvz, padarykite, kad jei perduodamas parametras Title yra ilgesnis už 20 simbolių, išvesti klaidą.
Tada išbandykite įvairius Route rezultato variantus:
  • JSON rezultatą su perduodamu masyvu
  • PDF failo parsisiuntimą
  • Perkėlimą į kitą puslapį (Redirect)
  • Perkėlimą į kitą ROUTE su jo pavadinimu
Na, ir tada Middleware - čia gan svarbus ir dažnai naudojamas dalykas.
  • Sukurkite savo Middleware - sakykime, ar dabartiniai metai yra lyginiai
  • Panaudokite tą Middleware, prieš kviesdami savo tą patį resources/views/page.php failą: jeigu metai lyginis skaičius, išveskite tekstą "Lyginis"
O pabaigai - ar nepamiršote mūsų kuriamos krepšinio aikštelių duomenų bazės? Pabandykite pritaikyti Routes taisykles jos puslapiams. Jau buvome tai padarę pagrindiniam puslapiui ir puslapiams "Apie", "Kontaktai" bei "Aikštelės", o dabar pridėkime ir šituos:
  • /aiksteles/[miestas]
  • /aikstele/[pavadinimas]
  • /admin (prisijungimo puslapis)
  • /admin/aiksteles
  • /admin/miestai
  • /admin/aiksteliu_tipai
Galbūt ne visiems Routes veiksmam jau turime realių įgūdžių (dar nepraėjome, kaip veikia administratoriaus autorizacija), bet pabandykite suformuoti savo routes/web.php turinį, o vėliau jį dar tobulinsime. Pasitikrinimui - galite šiam skyreliui tinkamą routes/web.php variantą parsisiųsti čia.



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