25 Aug

Controller és middleware a két jóbarát


Most, hogy a routingot tisztába tettük, nézzünk egy kicsit bele abba, hogy azokat a route-okat, amiket belőttünk miféle controllerekbe tudjuk belevezetni? Hiszen nem gondolhatjuk komolyan, hogy a kérések kezelésének logikáját a routes.php-ban írjuk meg, nemde? Főleg, hogy az MVC-ben ez lenne a controllerek dolga. Ezek általánosan az app/Http/Controllers mappába kerülnek.Xbox-360-S-Controller

Megjegyzés: Minden controllernek extendelnie kell a Controller osztályt.

Nézzünk egy egyszerű példát:

 
<?php // /app/Http/Controllers/MockController.php követjük a PSR standardot


namespace App\Http\Controllers;

class MockController extends Controller { 

   public function index() { // minden meghívott metódusnak publikusnak kell lennie, hogy a dispatcher elérje azt.

      return view(); // a template fájlokat itt adjuk vissza

   }

}

Na most akkor nézzük, hogy is tudunk hivatkozni erre a controller/action párosra (egy kis ismétlés gyanánt):
Route::get("/", [
    "uses" => "MockController@index",  // itt adhatjuk meg a controller/action párost

    "as" => "home" // itt pedig a route nevét, ami néven később tudunk rá hivatkozni

]);

route("home") // az előző route-hoz tartozó URL-t adja vissza

// blade templateben ugyanez: {{ route("home") }}

Implicit routingparental-advisory-explicit-lyrics-C


Igazából most jövök rá, hogy pár dolog kimaradt a routing szekcióból, de most pótlom őket. Nos, kicsit fárasztó lenne minden egyes metódust lemappelni, nemde? Emiatt laravelben lehetőségünk van arra, hogy csak a controllert adjuk meg a route-hoz, míg a metódusok nevét a Laravel összekapcsolja nekünk.
Route::controller("/posts", "PostController", [ // csak az URL tagot és a controller nevét adjuk meg. 

"getIndex" => "home"] ); // itt is lehetőségünk van az egyes mappelt metódusokhoz nevet rendelni, amit később az URL helperrel használhatunk


// a GET /posts/index url a PostController@getIndex-re mutat

// a POST /posts/test-view a PostController@postTestView-ra mutat. Ez az ún. dash-syntax. A kötőjelet követő betű nagybetűvé lesz alakítva és a kötőjel ki lesz purgálva a névből.



Ha olyan metódust írunk be, amihez tartozó metódus nem létezik, akkor 404-es oldalra leszünk irányítva.

Middleware-ek definiálás a kontrollerben is lehetséges:
public function __construct() {
  $this->middleware("auth"); // a konstruktorban helyezzük mindezt el, és a sorrend számít

  $this->middleware("log", array("only" => "getIndex")); // beállíthatjuk, hogy csak bizonyos metódusokra legyen érvényes

  $this->middleware("mock" array("except" => "getIndex")); // vagy épp azt, hogy melyekre ne

}

Pihentessük a szemünket!

Laravelben lehetőségünk van arra, hogy ún. RESTful controller route-okat definiáljunk.
Route::resource("photo", "PhotoController");

Ahhoz, hogy egy ilyen controllert létrehozzunk, a következőt kell bepötyögni a laravel rootjában állva:
php artisan make:controller PhotoController

Ez legenerálja nekünk a controllert, valamint a hozzá tartozó action-öket, persze üresen. Ezeket az action-öket az alábbiak szerint mappeli nekünk ez a route beállítás:
































Metódus URL Action Route neve
GET /photo index photo.index
GET /photo/create create photo.create
POST /photo store photo.store
GET /photo/{photo} show photo.show
GET /photo/{photo}/edit edit photo.edit
PUT/PATCH /photo/{photo} update photo.update
DELETE /photo/{photo} destroy photo.destroy

Persze itt is lehetőségünk nyílik korlátozni, hogy mely action-öket szeretnénk a route-on meghagyni/vagy épp kizárni:
Route::resource("photo", array("only" => array("index", "show")));


Route::resource('photo', 'PhotoController',
 ['except' => ['create', 'store', 'update', 'destroy']]);

Lehetőségünk van továbbá egymásba ágyazni ezeket a Resource  route-okat. Csupán az ún. dot notation-t kell használni, vagyis pontokkal válasszuk el egymástól őket:
Route::resource("photos.comments","PhotoCommentController");

Az ilyen módon regisztrált route-okat így tudjuk elérni: photos/{photos}/comments/{comments}, és a paramétereket hasonlóképpen kapják meg:
class PhotoCommentController extends Controller {

 /**
 * Show the specified photo comment.
 *
 * @param int $photoId
 * @param int $commentId
 * @return Response
 */
     public function show($photoId, $commentId)
     {
       //

     }
}

A fenti példában látszik, hogy a két szükséges ID-t megkapjuk paraméterben.

Ha más route-okat is szeretnénk definiálni a Resource controller alapértelmezettjei felül akkor azokat a Route::resource definiálása előtt tegyük meg:
Route::get('photos/popular', 'PhotoController@method'); // csináltunk egy újabb endpointot

Route::resource('photos', 'PhotoController'); // valamint az alap resource route-okat is fellőttük

Az előző cikkben már használtunk Dependency Injection-t az egyik metódusunknál, ahol egy model osztályt vártunk paraméterként. Ugyanezt megtehetjük a konstruktorunk esetében is. A controllerek kiszolgálását a laravelben az ún. service container végzi, amit egy későbbi cikkben kitárgyalunk. Ilyen formában Modeleket, Request objektumot és még sok mást átadhatunk paraméterként a konstruktornak vagy épp az adott actionnek.

Middleware


A Laravelben a Middleware-ek szolgálnak egyfajta pre-dispatch hooknak, vagyis mielőtt a controllerhez érne egy adott request, előtte ezeken az ún. Middleware-eken át megszűrjük azt. Ilyen middleware az authentikációért felelős, ami továbbenged, ha be vagyunk jelentkezve, vagy épp redirektál egy loginoldalra. Számos ilyet csomagoltak a keretrendszerrel, amik egyből működésre készek. Ezek az osztályok az app/Http/Middleware könyvtárban csücsülnek.

Na de mégis mibe fáj egy ilyet létrehozni?

Ahogy a legtöbb dologban a laravelnél, két út létezik, az egyik a kényelmes megoldás, ami csupán annyi, hogy kiadjuk a következő parancsot:
php artisan make:middleware [middleware-neve]

a másik pedig olyan, mintha sajtreszelővel ****nánk amikor kézzel belekulákoljuk a könyvtárba az osztályt
<?php
namespace App\Http\Middleware;

class LimitBeer {
    public function handle($request, \Closure $next) { // ezeket a paramétereket kapjuk meg, ezt a sémát követnie kell 

        
        if ($request->input("beer") == 1) { // ha a bejövő paraméterek közül a beer értéke = 1, akkor redirektáljuk a kérést.

            return redirect("egySorAzNemSor");
        }        
        return $next($request); // továbbadjuk a $request objektumot a következő middleware-nek vagy a controllernek

    } 
}

és a Kernel.php-ben is bevezetjük azt, hogy tudjunk rá aliassal hivatkozni:
protected $routeMiddleware = [
    'limit' => \App\Http\Middleware\LimitBeer::class,
];

Ha ez utóbbit nem tesszük meg, akkor a fully qualified classsname-el tudunk rá hivatkozni, úgy is felismeri a rendszer.

A middleware-ek futhatnak a request feldolgozása előtt, vagy éppen utána. Ez csak attól függ, hogy is hoztuk őket létre:
<?php namespace App\Http\Middleware;

use Closure;

class BeforeMiddleware implements Middleware {

    public function handle($request, Closure $next)
    {
        // Perform action


        return $next($request); // az előzőekben már látott formában továbbadjuk a request-et a következő felelősnek

    }
}

A fenti módszer ugyanolyan, mint amit legelőször bemutattam, jön a request, moleszteráljuk egy keveset és továbbadjuk azt. Azonban ha a request feldolgozása után szeretnénk valamit, akkor azt a következő módon tudjuk elvégezni:
<?php namespace App\Http\Middleware;

use Closure;

class AfterMiddleware implements Middleware {

    public function handle($request, Closure $next)
    {
        $response = $next($request); // itt továbbadjuk a következőnek, az megint a következőnek és így tovább, míg végül a controller is megkapja és a végső feldolgozott állapotot kapjuk vissza, így a Response objektum áll rendelkezésünkre.

 
        // Perform action


        return $response; // majd a választ dobjuk tovább

    }
}

Ha a middleware-jeinket szeretnénk MINDEN route esetében használni, akkor lehetőség nyílik ún. globális middleware-ek definiálására is, szintén a Kernel osztályban:
protected $middleware = [ // ez a tömb szolgál a globális middleware-ek részére

    \App\Http\Middleware\LimitBeer::class, // itt nem kell megadni aliast, mert nem hívhatók meg direktben, maguktól futnak le

];

Terminable Middleware


Vannak esetek, mikor a HTTP response kiküldése után szeretnénk még valamit ügyködni. A "session" middleware, ami a laravellel érkezik pont ezt csinálja, a session adatokat írja be a storage-be, miután ki lett küldve a response. Ahhoz, hogy ezt a feladatot ellássuk, ún. "terminable" middlewaret kell létrehozzunk:
<?php 
use Closure;
use Illuminate\Contracts\Routing\TerminableMiddleware;

class StartSession implements TerminableMiddleware {

    public function handle($request, Closure $next)
    {
        return $next($request); // ugyanúgy működik, ahogy a többi before middleware

    }
// itt már implementálnunk kell a terminate metódust is

    public function terminate($request, $response) // megkapjuk a request és a response objektumot egyaránt
    {   
        // Store the session data...

    }

}

Ahogy a példán is látszik, itt már nem elég a handle metódust implementáljuk, a terminate metódust is kell, amiben megkapjuk a request és response objektumokat is, hogy azokkal operáljunk. Ezeket a middleware-eket a globális middleware-ek között kell regisztrálnunk a Kernel osztályunkban.

A controllerekről és middleware-ekről ennyit, legközelebb a request és response osztályoknak nézünk a körmére!

Hozzászólások betöltése
2014-2018 © Letscode.hu. Minden jog fenntartva. Build verzió: 1.2.11