Prejsť na obsah

Dependency Injection a middleware

Táto kapitola popisuje dependency injection (DI) a middleware vo frameworku DotApp PHP Framework 2.0. Kontroléry, middleware a trasy patria do modulu. Kontrolér voláte reťazcom "Module:Controller@method!" (koncové ! vypne DI pre danú metódu). Modulový middleware pripojíte pomocou ->before("#Module:AuthGate@check!").

1. Dependency Injection a middleware v DotApp

Kontajner DI žije na inštancii DotApp (bind, singleton, resolve). Middleware je buď hook before/after na trase, pomenovaný pipeline Middleware::register, alebo trieda modulu v app/modules/{Module}/Middleware/.

1.1. Čo je Dependency Injection a middleware?

Dependency Injection (DI) vkladá registrované služby do callbackov namiesto toho, aby ste ich konštruovali ručne. V DotApp je kontajnerom trieda DotApp.

Middleware beží pred alebo po obsluhe trasy. Triedu middleware modulu pripojíte cez ->before("#Module:Class@method!"). Pomenované pipelines používajú Middleware::register spolu s Middleware::use()->group().

1.2. Kontajner DI

Tri metódy na jadre, ktoré dostávate ako initialize($dotApp):

1.2.1. bind(string $key, callable $resolver)

Zaregistruje factory. Každé volanie resolve vytvorí novú inštanciu.


$dotApp->bind('logger', function () {
    return new Logger();
});
$logger = $dotApp->resolve('logger');
$logger2 = $dotApp->resolve('logger'); // different instance
        
1.2.2. singleton(string $key, callable $resolver)

Vytvorí sa raz a potom sa znovu používa. Framework už registruje DotApp::class ako singleton.


$dotApp->singleton('cache', function () {
    return new CacheService();
});
$cache1 = $dotApp->resolve('cache');
$cache2 = $dotApp->resolve('cache'); // same instance
        
1.2.3. resolve(string $key)

Vráti naviazanú hodnotu. Ak je kľúč neznámy, vyhodí výnimku.


$cache = $dotApp->resolve('cache');
        
1.2.4. Ako sa DI používa v praxi

Služby registrujte v initialize($dotApp) modulu. Metódy kontrolérov používané z trás sú public static a prijímajú $request. Koncové ! na reťazci volateľného handlera vypne DI pre danú metódu, takže handler dostane iba $request.


public function initialize($dotApp) {
    $dotApp->singleton('cache', function () {
        return new CacheService();
    });
}
        

1.3. Volanie kontrolérov

Všetky nasledujúce zápisy patria do app/modules/{Module}/module.init.php vnútri initialize($dotApp).

1.3.1. Reťazec modulového kontroléra

Syntax: "Module:Controller@method!". Živé demo Users: Users:Login@page!.


Router::get('/documentation/examples/run/users/login', "Users:Login@page!", Router::STATIC_ROUTE);
        
1.3.2. Koncové !

Znak ! vypne DI pre danú metódu. Používajte ho takmer pri každom handleri trasy.


Router::post('/documentation/examples/run/users/login', "Users:Login@save!", Router::STATIC_ROUTE);
        
1.3.3. Closures

Prvým argumentom je $request.


Router::get('/status', function ($request) {
    return "Current method: " . $request->getMethod();
}, Router::STATIC_ROUTE);
        
1.3.4. DotApp::call

Volanie modulového kontroléra alebo reťazca middleware z PHP:


DotApp::call("Users:Login@page!", $request);
        

1.4. Middleware

1.4.1. Modulový middleware na trase

Triedu umiestnite do app/modules/Users/Middleware/AuthGate.php a pripojte ju cez before. Handlerom trasy ostáva metóda kontroléra.


Router::get('/documentation/examples/run/users/app', "Users:Login@app!", Router::STATIC_ROUTE)
    ->before("#Users:AuthGate@check!");
        

namespace Dotsystems\App\Modules\Users\Middleware;

use Dotsystems\App\Parts\Auth;
use Dotsystems\App\Parts\Config;
use Dotsystems\App\Parts\Response;

class AuthGate extends \Dotsystems\App\Parts\ModuleMiddleware
{
    public static function check($request, array $rights = [])
    {
        if (!Auth::isLogged()) {
            $p = rtrim((string) Config::module('Users', 'prefix'), '/');
            return Response::redirect($p . '/login', 302);
        }
        if (!empty($rights) && !Auth::can($rights)) {
            return new Response(403, 'Forbidden');
        }
    }
}
        
1.4.2. Pomenovaný middleware pipeline

Zaregistrujte cez Middleware::register a potom obalte trasy cez Middleware::use()->group(). Callback dostane ($request, $next) a musí zavolať $next($request).


use Dotsystems\App\Parts\Middleware;
use Dotsystems\App\Parts\Response;

Middleware::register('is_admin', function ($request, $next) {
    if (!\Dotsystems\App\Parts\Auth::can('Users.admin')) {
        return new Response(403, 'Forbidden');
    }
    return $next($request);
});

Middleware::use('is_admin')->group(function () {
    Router::get('/admin/users', 'Users:Login@app!');
});
        
1.4.3. Globálny before hook

Closure použitá ako Router::before musí prijať $request. Vrátenie Response trasu predčasne ukončí.


Router::before(['POST'], ['/documentation/examples/run/users/*'], function ($request) {
    // e.g. extra CRC or logging
});
        
1.4.4. Reťazec trasy middleware()

Na reťazci trasy je Router::middleware() aliasom before(). Pomenované pipelines sa registrujú cez Middleware::register a uplatňujú cez Middleware::use()->group(). Druhý argument Router::get (a ostatných metód HTTP slovies) je handler trasy: reťazec kontroléra alebo callable.

1.5. Praktické príklady

1. Registrácia singletonu v initialize($dotApp):


$dotApp->singleton('cache', function () {
    return new CacheService();
});
$cache1 = $dotApp->resolve('cache');
$cache2 = $dotApp->resolve('cache');
echo ($cache1 === $cache2) ? "Same instance" : "Different instances";
        

2. Chránená stránka:


Router::get('/documentation/examples/run/users/app', "Users:Login@app!", Router::STATIC_ROUTE)
    ->before("#Users:AuthGate@check!");
        

3. Bind plus closure, ktorá číta požiadavku:


$dotApp->bind('logger', function () {
    return new Logger();
});
Router::get('/log', function ($request) use ($dotApp) {
    $logger = $dotApp->resolve('logger');
    return "Method: " . $request->getMethod();
}, Router::STATIC_ROUTE);
        

1.6. Poznámky

  • Singleton vs bind: singleton pre zdieľané služby, bind pre novú inštanciu pri každom volaní.
  • Kontroléry: public static, prvý argument $request, reťazec trasy 'Module:Ctrl@method!'.
  • Middleware: trieda modulu + ->before('#Module:Class@method!'), alebo Middleware::register + use()->group().
  • Pomenované pipelines: registrácia cez Middleware::register, potom obalenie trás v Middleware::use('name')->group(). Na reťazci trasy je ->middleware() aliasom ->before(), nie registrátor.