Először tisztázzuk a REST jelentését, de kétlem, hogy a Wikipedia-nál bővebb és jobb magyarázatot tudnék adni, úgyhogy inkább ide belinkelem a releváns szócikket: https://hu.wikipedia.org/wiki/REST Annyit azért összefoglalóan mindenképpen el szeretnék mondani, hogy ez nem egy protokoll, viszont szorosan kapcsolódik a HTTP protokollhoz, amely ugye a webes szereplőket és kommunikáció részleteit határozza meg. Ha egy ilyen alkalmazás és architektúrája (mint például a Laravel is) megfelel a következő megszorításoknak, akkor szokták RESTful-nak nevezni: (1) kliens-szerver architektúra, (2) állapotmentesség, (3) gyorsítótárazhatóság, (4) réteges felépítés, (5. opcionális) igényelt kód, (6) egységes felület (interface). Aki ennél többet szeretne tudni róla, tényleg olvassa el a wikis szócikket.
A Laravel a RESTful működést az útvonalain, vezérlő metódusain keresztül valósítja meg. De van, amikor egy kép többet mond ezer szónál. Ezért készítettem ezt az összefoglaló táblázatot (és róla a képet), ami a bejegyzésben található.
A leggyakoribb műveletek (CRUD) megvalósításához ezekre a HTTP metódusokra és útvonalakra van szükség. Ezek közül az első kettőt már ismerjük. GET HTTP metódussal képesek voltunk lekérni a '/flights' és a '/flights/{id}' útvonalakat. Mindkét útvonal elérésekor a böngészőben valamilyen erőforrást kértünk le, nevesen a flights adatbázistábla tartalmát az index metódusban teljes egészében, míg a show metódusban csak egy darab sort belőle. A 7-ből tehát 2-t már ismerünk, használtunk is őket, tudjuk, hogy hogyan működnek, ezeket tehát kipipálhatjuk.
A táblázat sorainak színezése nem véletlen! Sárgával (citrom és narancs) jelöltem az adatbázis erőforrások lekérését.
A következő két sorban az adattáblába új sort tudunk létrehozni, vagy ha az MVC tervezési minta szerint szeretném megfogalmazni, akkor a példaként használt Flight Model osztálynak egy új példányát hozom létre. Kezdjük a create-tel, mivel ez egy GET-es útvonal lekérés lesz, aminek hatására a felhasználó böngészőjének átadunk egy olyan üres (nem kitöltött) űrlapot, amelynek segítségével felparaméterezheti az elküldés gomb megnyomásával létrejövő Flight Model osztály új példányát. Ez tehát egy üres űrlapot ad vissza a felhasználó böngészőjének. A store pedig ennek a párja, hiszen amikor kitöltjük az űrlapot és elküldjük ("postázzuk" - POST) a szervernek a bemeneti információkat, akkor ő a store metódus magjában mentjük ki az adatbázisba.
A táblázat sorainál, mivel ezek (create és store) összefüggőek, bekereteztem őket, továbbá világos és sötét zöld háttérszínnel láttam el őket.
A következő két sorban az adattáblában már meglévő sort tudunk frissíteni, vagy ha az MVC tervezési minta szerint szeretném megfogalmazni, akkor a példaként használt Flight Model osztály már meglévő példányának adattagjait tudom szerkeszteni és frissíteni. Kezdjük az edit-tel, hiszen ez vonatkozik a szerkesztésre. A GET-es útvonal meglátogatásával egy űrlapot kap vissza a felhasználó, de már nem üreset, mivel ez az adatsor a flights adattáblában már létezik és most az űrlap mezői ennek a sornak az értékeit kapják meg. Utána ezt a felhasználó átírhatja a mezők értékét, majd amikor végzett a szerkesztéssel akkor a küldés (vagy frissítés) gomb megnyomásával hívódik meg az update vezérlő metódus a PUT-os útvonalon keresztül (lehetőségünk van a PUT helyett PATCH-et is használni, a kettő között nincsen különbség, ugyanúgy működnek). Ebben a metódusban már nem egy új sort szúrok be a flights adattáblába, hanem ugye a meglévő sor mezőit frissítem az érkező esetlegesen új értékekkel.
A tábla sorainál, mivel ezek (edit és update) is összefüggőek, bekereteztem őket, továbbá világos és sötét kék háttérszínnel láttam el őket.
A táblázat legutolsó sorában látszódik a destroy metódus, amelyet a DELETE-es útvonalon keresztül tudunk elérni. Ez annyit csinálni, hogy a paraméterként megkapott adatbázis sort törölni fogja a flights adattáblában.
Fontos még megjegyezni, hogy a HTML űrlap elküldését csak GET vagy POST method attribútum értékkel tudjuk elküldeni, a szabvány ezt engedi meg nekünk, de mivel nekünk szükségünk van PUT/PATCH és esetleg DELETE metódusok szerinti küldésre is, ezért utóbbiak esetén egy kis trükköt alkalmazunk: POST-ként küldjük el az űrlapot, majd az űrlapon belül állítjuk be a PUT, PATCH, DELETE értékeket.
Bővítsük a komplex példánkat csomagokkal!
De nem programcsomagokkal (packages), hanem az utasokhoz rendeljünk csomagokat, poggyászokat (luggage). A példa megvalósítása során létre fogjuk hozni a 7 útvonalat és a Controller osztályban pedig a 7 útvonalhoz tartozó 7 metódust. De kezdjük a Luggage Model osztály létrehozásával (adjunk hozzá mindent IS, a migrációs fájlt, a gyárat és a Controller-t is):
php artisan make:model Luggage -mfcr
Ezek közül a kapcsolók közül (mfcr) már ismerjük az m-et (migrációs fájl), f-et (factory), c-t (Controller), az r pedig azt adja hozzá, hogy a Controller-ben létre fog jönni majd ez a 7 RESTful metódus is, illetve a magjuk nem (természetesen), de a metódusok váza igen.
A migrációs fájlban a csomagoknál (luggage - angolban rendhagyó módon a luggage többesszáma is luggage, erre figyeljünk) plusz mezőben mindössze egy csomagszámot és egy utasazonosítót (külső kulcs, 1-több-es kapcsolat) tároljunk el:
$table->string('number');
$table->foreignId('passenger_id')->constrained()->onUpdate('cascade')->onDelete('cascade');
Mentés után migráljunk: php artisan migrate
Ha pedig kapcsolatokról beszélünk, akkor hozzuk is létre az ennek megfelelő Eloquent kapcsolatot biztosító metódusokat. Kezdjük a Luggage osztállyal:
public function passenger()
{
return $this->belongsTo(Passenger::class);
}
Majd folytassuk a Passenger osztály bővítésével:
public function luggage()
{
return $this->hasMany(Luggage::class);
}
Kérdés: ha tudni szeretnénk, hogy az adott repülőjáraton melyik csomagokat szállítják, akkor szükség volna-e arra, hogy a repülőjárathoz (Flight) is hozzárendeljük a csomagot? Válasz: nincs. Ha valaki nem tudja, hogy miért nincs erre szükség, akkor a bejegyzés végén elmagyarázom.
Ezekután a LuggageFactor defintion metódusának visszatérési értéke így nézhet ki, használva a faker-t:
return [
'number' => $this->faker->lexify('???') . "-" . $this->faker->numerify('###'), // példa: 'ABC-123'
'passenger_id' => Passenger::inRandomOrder()->first()->id,
];
(Utána ne felejtsük el importálni a Passenger osztályt a fájl tetején.) Tinker-ben le is tesztelhetjük a működését: php artisan tinker
Luggage::factory()->make()
Működik, úgyhogy vegyük is fel a gyárat egy annak megfelelő Seeder osztályba:
php artisan make:seeder LuggageSeeder
Az új osztályunk run() metódusába pedig állítsuk be, hogy a gyár mondjuk 50 darab poggyászt hozzon létre "termeléskor".
Luggage::factory()->count(50)->create();
A database/seeders/DatabaseSeeder osztályban pedig a már meglévő Flight és Passenger "gyártás" után illesszük be a poggyászok gyártását is:
$this->call([
FlightSeeder::class,
PassengerSeeder::class,
LuggageSeeder::class
]);
Aki nem emlékezne a Seeder osztályok működtetésére, az itt tájékozódhat erről. Indítsunk is be egy gyártást, hogy legyen jó sok tesztadatunk: php artisan db:seed
Ezután következhet a web.php-ban a 7 új útvonal felvétele (azért, hogy működjön, importáljuk be a fájl tetején a LuggageController osztályt):
Route::get('/luggage', [LuggageController::class, 'index']);
Route::get('/luggage/{luggage}', [LuggageController::class, 'show']);
Route::get('/luggage/create', [LuggageController::class, 'create']);
Route::post('/luggage', [LuggageController::class, 'store']);
Route::get('/luggage/{luggage}/edit', [LuggageController::class, 'edit']);
Route::put('/luggage/{luggage}', [LuggageController::class, 'update']);
Route::delete( '/luggage/{luggage}', [LuggageController::class, 'destroy']);
Kiegészítés: az "id" helyett a "luggage" (Model osztály neve kis kezdőbetűvel, ugye a webcímnél nem lenne értelme nagy kezdőbetűvel írni) van. Ennek meg fogjuk nézni, hogy milyen gyakorlati haszna lesz.
Megjegyzés még az iménti kódsorokhoz: nem kell, de én próbáltam ugyanúgy elszeparálni az összetartozó párokat egy plusz sortöréssel, ahogy a fenti táblázatban csináltam a keretezés és színek szerinti elválasztást.
A terminal segítségével le is kérdezhetjük a már meglévő útvonalainkat: php artisan route:list
Az eredmény itt látható (keressük meg és koncentráljunk most a luggage-hez kapcsolódó új útvonalainkra):
Következhet a Controller szerkesztése. Ha
megnézzük a LuggageController-t, akkor
láthatjuk, hogy a rendszer már automatikusan beleteszi a 7 RESTful
útvonalhoz tartozó 7 Controller action-t, vagyis vezérlő metódust is, ez nekünk így nagyon kényelmes. És itt utalnék vissza az útvonalakra, mármint azokra, ahol {luggage} szerepelt wildcard paraméterként az {id} helyett... ez még könnyebbé teszi a dolgunkat, mert nem kell majd kikeresnünk az adatbázistábla megfelelő sorát (find($id) vagy findOrFail($id) metódussal), hanem egyből a Luggage Model osztály példánya fog a rendelkezésünkre állni a metódus magjában. Nézzük is meg ezt rögtön a gyakorlatban: a LuggageController show metódusának magja legyen ez:
dd($luggage);
Az oldal kiszolgálásának elindítása után pedig nézzük meg ezt a webcímet: http://127.0.0.1:8000/luggage/1
Meg fogjuk kapni az egyes számú poggyász példányát.
Lenyitva az #attributes melletti kis háromszöget, már láthatók is az adatsor mezői és azok értékei, tehát működik az az elgondolás, hogy most már nem kell plusz egy sort írnunk, amivel az $id segítségével rákeresünk az adatsorra, hanem kapásból működik ez a megoldás. Maradjunk ennél a show metódusnál a LuggageController-ben és töröljük ki a dd() metódushívásos sort, helyette inkább térjünk vissza egy nézettel, ami megjeleníti a szép weboldalunkon a poggyász számát és a hozzá tartozó utas nevét.
public function show(Luggage $luggage)
{
// dd($luggage);
return view('luggage.show', [
'number' => $luggage->number,
'passenger' => $luggage->passenger->name,
]);
}
Az index metódust is készítsük el:
return view('luggage.index', [
'luggage' => Luggage::get(),
]);
Ezek működnek. Aki nem hiszi, tesztelje le őket a tinker segítségével. Viszont még nincsenek meg a hozzájuk tartozó nézet fájljaink a resources/views/luggage mappában. Hozzuk előbb létre a show.blade.php -t oda.
Majd következhet ugyanoda az index.blade.php
Már csak egy dolog van hátra: a layout.blade.php -ban bővítsük ki a menüt és tegyük bele a poggyászokat is a menüpontok közé. Ennek megoldását (1 sor) most már mindenkire rábízom. A weboldal eredmény itt látható:
Illetve egy poggyász részletezése:
A következő blogbejegyzésben innen fogjuk folytatni és megnézzük a létrehozó, szerkesztő, törlő útvonalakat és metódusokat. Továbbá ott fogunk majd kódszervezést is csinálni az útvonalainknál, hogy még kényelmesebben létrehozhatók legyenek majd ez a nevezetes 7 útvonal minden erőforráshoz, elöljáróban annyit, hogy mindössze egyetlen sorral helyettesíthető lesz majd a web.php-ban lévő 7 útvonal.
A bejegyzésben feltett kérdésre azért az volt a válasz, hogy nincs szükség a csomagoknak a repülőjárathoz rendelésére, mivel az utasok amúgy is már hozzá vannak rendelve a repülőjáratokhoz egy kapcsolaton keresztül. Így a csomag-repülőjárat kapcsolat létrehozása nélkül is könnyedén lekérdezhető (az utasokon keresztül), hogy milyen csomagok is tartoznak a repülőjárathoz, és fordítva.
A bejegyzéshez tartozó módosítások Github commit-je itt található.