用于互连服务的ZF3服务DI的有效模式

Jan*_* M. 4 php zend-framework-mvc zend-framework2 zend-framework3 zf3

据我所知,有效模式是:

  • 一个实例化所需服务的FooControllerFactory(FooService)
  • 一个带有构造函数__construct的FooController(FooService $ fooService)
  • Controller获取一些基本数据并从服务中获取结果
  • 服务包含所有必需的业务逻辑这是一个基本服务.最终,此服务将需要其他服务用于各种活动.例如CacheService,SomeOtherDataService.

问题是,包含/注入其他互连服务的有效/适当模式是什么?

我们目前的reallife示例极其简化:

AuctionController

/**
  * get vehicles for specific auction
*/
public function getVehiclesAction ()
{
    $auctionService = $this->getAuctionService(); // via service locator
    $auctionID = (int) $this->params('auction-id');
    $auction = $auctionService->getAuctionVehicle($auctionID);
    return $auction->getVehicles();
}
Run Code Online (Sandbox Code Playgroud)

AuctionService

public function getAuctionVehicles($auctionID) {
    $auction = $this->getAuction($auctionID);
    // verify auction (active, permissions, ...)
    if ($auction) {
        $vehicleService = $this->getVehicleService(); // via service locator
        $vehicleService->getVehicles($params); // $params = some various conditions or array of IDs
    }
    return false;
}
Run Code Online (Sandbox Code Playgroud)

VehicleService

public function getVehicles($params) {
    $cache = $this->getCache(); // via service locator
    $vehicles = $cache->getItem($params);
    if (!$vehicles) {
        $vehicleDB = $this->getVehicleDB(); // via service locator
        $vehicles = $vehicleDB->getVehicles($params);
    }
    return $vehicles;
}
Run Code Online (Sandbox Code Playgroud)

建议的有效模式的示例

AuctionController

public function __construct(AuctionService $auctionService) {
    $this->auctionService = $auctionService;
}

/**
  * get vehicles for specific auction
*/
public function getVehiclesAction ()
{
    $auctionID = (int) $this->params('auction-id');
    $auction = $this->auctionService->getAuctionVehicle($auctionID);
    return $auction->getVehicles();
}
**AuctionService**

public function getAuctionVehicles($auctionID) {
    $auction = $this->getAuction($auctionID); // no problem, local function
    // verify auction (active, permissions, ...)
    if ($auction) {
        $vehicleService = $this->getVehicleService(); // we don't have service locator
        $vehicleService->getVehicles($params); // $params = some various conditions or array of IDs
    }
    return false;
}
Run Code Online (Sandbox Code Playgroud)

VehicleService

public function getVehicles($params) {
    $cache = $this->getCache(); // we don't have service locator, but cache is probably static?
    $vehicles = $cache->getItem($params);
    if (!$vehicles) {
        $vehicleDB = $this->getVehicleDB(); // where and how do we get this service
        $vehicles = $vehicleDB->getVehicles($params);
    }
    return $vehicles;
}
Run Code Online (Sandbox Code Playgroud)

一些说明:

  • 服务仅在某些情况下互连,95%是独立的
    • 拍卖有很多功能,不需要车辆
    • 车辆有VehicleController和VehicleService,只在某些情况下与拍卖有关,它是一个具有其他功能的独立模块
    • 在控制器中注入所有需要的服务将浪费资源,因为在每个操作中都不需要它们(在现实生活中,我们有更多的互连服务,而不仅仅是两个)
  • 在多个服务中编程相同的业务逻辑只是为了避免服务定位器显然是无效的模式而且是不可接受的.

Fge*_*Fge 5

如果控制器需要太多不同的服务,它通常表明控制器有太多的责任.

继续@ AlexP的回答,然后将此服务注入您的控制器.根据您的设置,当创建控制器时,这肯定会导致依赖注入级联.这至少会将创建的服务限制为控制器实际需要的服务(以及那些传递相关的服务).

如果其中一些服务很少需要,并且您担心每次请求都创建它们,那么新的Service Manager现在也支持延迟服务.那些仍然可以作为常规依赖项注入服务/控制器(如上所述),但仅在第一次调用时创建.

从文档的示例中复制它:

$serviceManager = new \Zend\ServiceManager\ServiceManager([
    'factories' => [
        Buzzer::class             => InvokableFactory::class,
    ],
    'lazy_services' => [
         // Mapping services to their class names is required
         // since the ServiceManager is not a declarative DIC.
         'class_map' => [
             Buzzer::class => Buzzer::class,
         ],
    ],
    'delegators' => [
        Buzzer::class => [
            LazyServiceFactory::class,
        ],
    ],
]);
Run Code Online (Sandbox Code Playgroud)

请求服务时,不会立即创建它:

$buzzer = $serviceManager->get(Buzzer::class);
Run Code Online (Sandbox Code Playgroud)

但只有在第一次使用时:

$buzzer->buz();
Run Code Online (Sandbox Code Playgroud)

这样,您可以将多个依赖项注入控制器,并且只会创建实际需要的服务.当然,对于任何依赖项都是如此,例如其他服务所需的服务等.