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!'), aleboMiddleware::register+use()->group(). - Pomenované pipelines: registrácia cez
Middleware::register, potom obalenie trás vMiddleware::use('name')->group(). Na reťazci trasy je->middleware()aliasom->before(), nie registrátor.