覆盖从 Slim 4 中的路由组注入 class?

Overriding injected class from routing group in Slim 4?

我有一个 Slim4 应用程序,由多个模块组成,分别位于不同的路由组中,如下所示:

$app->group('/app', function(RouteCollectorProxy  $app) {
   /*blah blah*/
})->add(MyMiddleWare::class);

$app->group('/api', function(RouteCollectorProxy  $app) {
   /*blah blah*/
})->add(MyMiddleware::class);

$app->group('/admin', function(RouteCollectorProxy  $app) {
   /*blah blah*/
})->add(MyMiddleware::class);

MyMiddleware 收到一个 Interface

class MyMiddleware
{
    public function __construct(IMyInterface $myServiceImplementingInterface) { /*blah blah*/ }
}

当我们设置容器时,我们告诉它要注入哪个 class 所以 PHP-DI 知道要用哪个 class 构造中间件:

/* bootstraping */
$containerBuilder = new ContainerBuilder();
$containerBuilder->addDefinitions(__DIR__ . '/container.php');
$container = $containerBuilder->build();

/*container.php*/
return [
    IMyInterface::class => function (ContainerInterface $container) {
        return new MyServiceImplementingInterface();
    },
];

我的主要问题是:

是否可以基于路由组以某种方式覆盖 IMyInterface::class 容器设置的实现?所以我可以有类似的东西:

主容器设置:

/*container.php*/
return [
    IMyInterface::class => function (ContainerInterface $container) {
        return new MyServiceImplementingInterface();
    },
];

特定路由组容器设置:

/*container.admin.php*/
return [
    IMyInterface::class => function (ContainerInterface $container) {
        return new AnotherServiceImplementingInterface();
    },
];

我建议为不同的组使用 MyMiddleware class 的两个不同对象,每个对象都使用 IMyInterface 的适当实现来构建。你可以告诉 PHP-DI 用你想要的参数调用构造函数。

这里我在容器中创建了两个MyMiddleware实例,一个名为AdminMiddleware,另一个名为ApiMiddleware。使用 DI\create()->constructor() 方法,我将 DI 配置为在构建这两个对象时注入 IMyInterface 的不同实现:

<?php

use DI\ContainerBuilder;
use Slim\Factory\AppFactory;

// this is the path of autoload.php relative to my index.php file
// change it according to your directory structure
require __DIR__ . '/../vendor/autoload.php';

interface IMyInterface {
    public function sampleMethod();
}

class MyServiceImplementingInterface implements IMyInterface {
    public function sampleMethod() {
        return 'This implementation is supposed to be used for API endpoint middleware';
    }
}

class AnotherServiceImplementingInterface implements IMyInterface {
    public function sampleMethod() {
        return 'This implementation is supposed to be used for Admin middleware';
    }
}

class MyMiddleware
{
    private $service;
    public function __construct(IMyInterface $myServiceImplementingInterface) { 
        $this->service = $myServiceImplementingInterface;
    }
    public function __invoke($request, $handler)
    {
        $response = $handler->handle($request);
        $response->getBody()->write($this->service->sampleMethod());
        return $response;
    }
}

$containerBuilder = new ContainerBuilder();
$containerBuilder->addDefinitions([
    'AdminMiddleware' => DI\create(MyMiddleware::class)->constructor(DI\get(AnotherServiceImplementingInterface::class)),
    'ApiMiddleware' => DI\create(MyMiddleware::class)->constructor(DI\get(MyServiceImplementingInterface::class))
]);

$container = $containerBuilder->build();

AppFactory::setContainer($container);
$app = AppFactory::create();

$app->group('/admin', function($app) {
    $app->get('/dashboard', function($request, $response, $args){
        return $response;
    });
})->add($container->get('AdminMiddleware'));
$app->group('/api', function($app) {
    $app->get('/endpoint', function($request, $response, $args){
        return $response;
    });
})->add($container->get('ApiMiddleware'));

$app->run();