Laravel Routing - útvonalválasztás (2. rész)

Attila | 2022. 02. 14. 14:24 | Olvasási idő: 4 perc

Címkék: #Blade #Laravel #MVC #Nézet (View) #PHP #Routing

Azt már tisztáztuk, hogy az MVC tervezési minta milyen útvonalon képes kiszolgálni a felhasználói kéréseket. Ebben a bejegyzésben azt nézzük meg, hogy a kérések kiszolgálása során hogyan tudunk adatot visszaküldeni a felhasználónak. Most még csak az útvonal fájlon keresztül tesszük ezt meg, de a későbbiekben a Controller felhasználásakor is hasonlóan fogjuk majd csinálni, úgyhogy érdemes már most jól elsajátítani ezeket a technikákat.
Pass data to view

Ahogy arra a cím is utal, főleg az útvonal fájlunkban (routes/web.php), illetve a nézet fájljainkban (resources/views mappában) fogunk tevékenykedni.

Mielőtt rátérnék, hogy hogyan is lehet adatokat átadni a nézeteknek, még két egyszerű dolgot próbáljunk ki:

  1. az első amikor az útvonal visszatérése még nem egy konkrét nézet, hanem csak egy szöveg. Például a főoldal útvonalánál egy sima return 'Hello world' utasítást írjunk meg és próbáljuk ki a főoldal lekérését.
  2. másodszor próbáljuk ki azt, amikor nem csak egy sima szöveggel, hanem egy tömbbel térünk vissza ugyanott. Például így: return ['foo' => 'bar']; --> ekkor a böngészőben egy JSON-t kapunk vissza és az itt definiált értéket. Érdemes a böngészőben különböző fejlesztői kiegészítést használni, és akkor nem csak a JSON fájl tartalmát tudjuk megtekinteni, hanem a válasz (response) fejléc részét is, amiben látjuk, hogy a Content-Type egy json típusú fájl. Ez nagyon hasznos akkor, ha mondjuk egy API-t akarunk összeállítani.

Info: PHP-ban is léteznek tömbök, ugyanúgy ahogy a többi programozási nyelvben is. Az imént mi egy úgynevezett asszociatív tömböt használtunk, amikor nem egész számot alkalmazunk a tömbindexekhez (0, 1, 2, ...), hanem valamilyen szöveges értéket.


Arról, hogy mi is a JSON fájl, itt lehet olvasni bővebben. Számunkra talán első körben elég most annyit tudni, hogy ez egy szöveges fájl, amelyben adatokat tudunk tárolni kulcs-érték párok segítségével. Amiért nagyon jók ezek a fájlok, az főleg az egyszerűségükből adódik, valamint hogy mindkét oldalon (kliens és szerver részen is) egyszerűen tudjuk kezelni és feldolgozni őket. Korábban erre az XML fájltípust használták, azonban az eléggé bonyolult is tud lenni, aminek nehézkessé válhat a kezelése, úgyhogy mindenképpen inkább a JSON használatát javaslom.

Ennyi kis bevezető után, térjünk rá a mostani fő témánkra, az adatok átadására a nézeteknek. Maradjunk az iménti példa "útvonalán", vagyis először egy szöveges változóban lévő adatot adjunk át az útvonalon keresztül a nézetnek.

Info: PHP-ban a változóneveket mindig $ jellel kezdjük.

$username = 'Attila';
return view('welcome', [
  'name' => $username
]);

Vegyük észre, hogy továbbra is a view() segédfüggvényt használjuk, amiben az 1. paraméter a nézet neve lesz, míg a 2. paraméter egy asszociatív tömb (erre utal a szögletes zárójel [] páros), amiben a 'name' kulcshoz, hozzárendeljük a $username érték párt. Ez volt tehát az első rész, ahol az útvonalnál elküldjük az adatot, már csak a nézetben megkapott adatot kell kiírni a felhasználónak.

A welcome nézetben a változóban lévő adat kiíratását elvégezhetjük többféle módon. Kezdjük a kicsit ódivatú PHP-s kiíratással, aztán majd megnézzük a modern Blade-es kiíratást is. Ahova pedig kiírjuk, az bárhova lehetne, de én a content osztályú div-en belül a title osztályú div-be teszem a kiíratást.

Szia <?php echo $name; ?>

Ez igazából egy nagyon régies megoldás, sokan nem is szeretik emiatt a PHP-t, mert a kvázi HTML fájlba ilyen módon tele lehet pakolni PHP kódokkal. A PHP kódot karakterekkel kell lezárni. Az echo az pedig egy sima kiíratást jelent, a $name változó kiíratását. Vegyük azért észre a mindig hangsúlyozott névkonvenciót. Amit küldtünk adatot az útvonal fájlból, az egy asszociatív tömb volt (kulcs-érték pár), ebből a nézetben a kulcsra tudunk hivatkozni és azt megjeleníteni a felhasználónak. Ennél a fenti megoldásnál egy fokkal elegánsabb, de még mindig csak egy "sima" PHP-s kiíratás van itt:

Szia <?= $name; ?>

Sűrűn előfordult az, hogy egy-egy kiíratásra használták csak a PHP-s beágyazást a kvázi HTML kódba, emiatt ezt az egyszerűsített formát is lehet alkalmazni (ekvivalens a fenti megoldással). Viszont mi egy Blade fájlt használunk, ami még egyszerűbbé teszi az életünket, úgyhogy a kiíratásra bőven elég csak ezt használnunk:

Szia {{ $name }}

A kiíratásokat kipróbálhatjuk egymás után a böngészőben, mindig ilyen eredményt kell kapnunk:



Köszönthetünk persze mást is, itt én saját magamnak írtam egy sziát. :-)

Most már működik a változó kiíratása, ezért most következhet egy tömb kiíratása. Először elküldjük a tömb adatait egy új útvonalnak, aztán majd rátérünk a nézetes megjelenítésre. A web.php-ban regisztráljuk az új útvonalat:

Route::get('/arraytest', function () {
  $tasks = [
    'Go to the store',
    'Go to the market',
    'Go to the work'
  ];
  return view('arraytest', [
    'tasks' => $tasks
  ]);
});

Az útvonal neve arraytest, ebben definiáljunk egy $tasks nevű tömböt, ami három szöveges elemből áll. Utána pedig, a korábban látott módon a view() segédmetódus második paraméterében adjuk át ezt a tömböt, vagyis küldjük el az arraytest nevű nézetnek. Ez eddig oké, azonban ha most a böngészőben le szeretnénk kérni a localhost:8000/arraytest útvonalat, akkor hibát kapnánk, de szerencsére egy jó beszédes hibát, ami arra utal, hogy nincs még arraytest nevű nézetünk, amire a view segédmetódusban hivatkoztunk. Hozzuk ezért létre a resources/views mappában az arraytest.blade.php nevű fájlt. Létrehozás után pedig következhet a kiíratás, amit most is "hagyományosan" fogunk először megtenni:

<ul>
  <?php foreach($tasks as $task) : ?>
    <li><?= $task; ?></li>
  <?php endforeach; ?>
</ul>

Info: HTML-ben a "pöttyös" felsorolást az ul tag-gel tudunk csinálni, a benne lévő felsorolási pontokat pedig az li tag-gel tudjuk megcsinálni. Itt egy foreach-es ciklust tudunk alkalmazni, amivel a kapott tömbön végig tudunk lépkedni és annyi felsorolási pontot teszünk bele, amennyi elem van a kapott tömbben.

Így nézne ki a hagyományos php-s kiíratás a nézetben… azonban nekünk a Blade nézetrendszer segítséget nyújt és egyszerűsítéseket tudunk végrehajtani (az ul tag-eket meghagyhatjuk ugyanúgy):

@foreach($tasks as $task)
  <li><?= $task; ?></li>
@endforeach

A "háttérben" a fordító tudja, hogy emögött az egyszerűsítés mögött a fenti kód van. A @foreach magját is tovább tudom egyszerűsíteni, ami ugye fenn egy sima PHP-s kiíratásnak felelt meg:

<li>{{ $task }}</li>

Sokkal-sokkal egyszerűbb lett így a kód. A weboldal frissítgetésével folyamatosan ellenőrizhetem, hogy még mindig jól jelenik-e meg, amit én szerettem volna. Ez a kódsorozat ugye nem csak egyszerűbb, de sokkal könnyebben is olvasható, mint ahonnan elindultunk.

Ha bővítem a web.php-ban a nézetnek átadott $tasks tömböt, akkor az az új elem is rögtön megjelenik a weboldalon egy frissítés után: például adjuk hozzá ezt az új szöveges elemet: 'Task #4' és teszteljük is. Szerencsére működik, úgyhogy örülünk. Egy módon még teszteljük a tömbös adatátadást. Térjünk vissza a web.php-ra és módosítsuk a return utasítást az arraytest útvonalnál:

return view('arraytest')->withTasks($tasks);

A with() segédmetódus lesz itt a segítségünkre, amellyel "hozzáfűzhetjük" a nézethez az adatainkat, fent például a $tasks tömbünket. De megtehetjük ugyanezt egy változóval is, ami talán jobban rávilágít a használatának mikéntjére:

$foobar = 'foobar';
return view('arraytest')->withTasks($tasks)->withFoo($foobar);

A "with"-hez hozzáírt nevet tudjuk a nézetekben felhasználni a kiíratásra. Így tehát a nézetben $foo változót (kis kezdőbetűs) tudjuk kiíratni, aminek az értéke 'foobar' lesz, mivel a paraméterül megkapott változó értéke lesz benne.

Össze is vonhatjuk a "with utáni részeket" a with() segédmetódus segítségével:

return view('arraytest')->with([
  'foo' => $foobar,
  'tasks' => $tasks
]);

A továbbiakban megismerkedünk egy újabb segédmetódussal, amit a Laravel keretrendszerben használhatunk. Ez a request() metódus lesz, amivel felhasználói bemeneteket, adatokat (input-okat) tudunk lekezelni. Hozzunk létre ennek is egy új útvonalat, majd az ehhez kapcsolódó nézetet is ugyanúgy (bár ez egyáltalán nem elvárás) requesttest névvel.

Route::get('requesttest', function () {
  return view('requesttest', [
    'title' => request('title'), // példa: http://127.0.0.1:8000/requesttest?title=asdf
  ]);
});

Itt már a megjegyzésben is jeleztem, hogy hogyan lehet majd használni a felhasználótól kapott bemenetet: az URL megfelelő "felparaméterezésével".

Info: vegyük észre, hogy a felhasználó által definiált paraméterek szintén kulcs-érték párokként jelennek meg az URL-ben. A paraméterlista ? (kérdőjellel) kezdődik, majd utána a kulcs érték párok = (egyenlőségjellel) összerendelve vannak. Ha további kulcs-érték párokat is szeretnénk így megadni, akkor & (és) jellel kell összefűzni a paramétereket. Példa: utvonalnev?kulcs1=ertek1&kulcs2=ertek2&kulcs3=ertek3

Az adatátadás tehát megvan, a requesttest.blade.php-ban pedig egy sima kiíratást végzünk:

<h1>{{ $title }}</h1>

Eredménye pedig így néz ki (az URL title attribútumának átírogatásával és az oldal újratöltésével tudjuk tesztelni a megoldást):

Ez működik, azonban a háttérben több minden történik, mint csak egy PHP-s echo-s kiíratás. Ugyanis, ha például egy javascript-et adunk meg paraméterként mondjuk a foo tömbelemnek:

Route::get('requesttest', function () {
  return view('requesttest', [
    'title' => request('title'), // példa: http://127.0.0.1:8000/requesttest?title=asdf
    'foo' => '<script>alert("foobar");</script>',
  ]);
});

Majd a nézetben kiíratjuk a PHP-s módszerrel:

<p><?= $foo; ?></p>

Frissítsük a weboldalunkat, és vegyük észre, hogy felugrott az alert (figyelmeztetés) ablak, amit fentebb kódoltunk. Ez egy elég nagy biztonsági rés, hiszen a kódbeszúrásos támadások így sikeresen végre tudnának hajtódni, ha ezt a PHP-s kiíratós módszert használnánk.

Viszont  ez "csak" PHP és még egyáltalán nem használtuk ki a Laravel (Blade) nyújtotta lehetőségeket, úgyhogy menjünk tovább ebbe az irányba: írjuk át "Blade-esre":

<p>{{ $foo }}</p>

Ekkor a $foo változó kiíratásakor nem hajtódik végre a script, csak ennek a konkrét szövegnek a kiíratása történik meg. Ez tehát egy biztonsági elem, így védekezik a rendszer a kód beszúrásos támadástípusok ellen.

Ha megnézzük az oldal forrását (jobb egérgomb a weboldalon és az oldal forráskódjának megtekintése), akkor látható, hogy a "kacsacsőr", az idézőjel átalakultak különleges HTML karakterekre, amelyek így már másképp működnek. Ha viszont a nézetben átalakítjuk ilyenre:

<p>{!! $foo !!}</p>

Akkor ezzel jelezzük a rendszernek, hogy egy script érkezik, nyugodtan végrehajthatja és le fog futni az alert-ünk. De ezzel azért nagyon-nagyon legyünk körültekintőek, óvatosak, hogy mikor és hogyan használjuk, mert veszélyes és ha a felhasználótól érkező script-et szeretnénk így feldolgozni, azt inkább sürgősen felejtsük el...

A blogbejegyzés során született kódokat ezen a commit-en tudod nyomon követni (a "felülírt" kódokat a fájlnak megfelelő megjegyzésekbe tettem, de mindegyik kipróbálható a megjegyzések elvételével): Routing 2. rész (commit)