【问题标题】:How to create multiple non-shared instances without injecting the ServiceLocator in ZendFramework2?ZendFramework2中如何在不注入ServiceLocator的情况下创建多个非共享实例?
【发布时间】:2017-02-26 21:54:37
【问题描述】:

我有一个在我的 module.config.php 文件中创建非共享服务和控制器工厂的工厂。这部分配置如下所示:

"service_manager" => [
    "factories" => [
        Entity\User::class => Factory\Entity\UserFactory::class
    ],
    "shared" => [
        Entity\User::class => false
    ]
],
"controllers" => [
    "factories" => [
        Controller\UserAdminController::class => Factory\Controller\UserAdminControllerFactory::class
    ]
]

User实体是非共享的,因为我允许管理员同时添加多个用户,所以我需要实例化User实体类的多个实例。

这是我的控制器工厂:

<?php

namespace User\Factory\Controller\Core;

use User\Controller\UserAdminController;
use Interop\Container\ContainerInterface;
use User\Entity\User;
use Zend\ServiceManager\FactoryInterface;
use Zend\ServiceManager\ServiceLocatorInterface;

class UserAdminControllerFactory implements FactoryInterface{

    /**
     * @param ContainerInterface $container
     * @param string $requestedName
     * @param array|null $options
     *
     * @return UserAdminController
     */
    public function __invoke(ContainerInterface $container, $requestedName, array $options = null){
        $serviceLocator = $container->getServiceLocator();

        return new UserAdminController($serviceLocator->get(User::class));
    }

    /**
     * @param  ServiceLocatorInterface $serviceLocator
     *
     * @return UserAdminController
     */
    public function createService(ServiceLocatorInterface $serviceLocator){
        return $this($serviceLocator, UserAdminController::class);
    }

}

目前,它仅在控制器的构造函数中注入 User 实体类的单个实例。但是,根据管理员想要执行的操作,我需要不确定数量的新实例。

一种可能的解决方案是每次通过在控制器中调用$this-&gt;getServiceLocator()-&gt;get(User::class) 来传递整个服务定位器并创建新实例,但这被认为是一种反模式。

这里的问题是如何在控制器中创建用户的新实例,使用 UserFactory 类?

EDIT1:添加用户工厂:

<?php

namespace User\Factory\Entity\Core;

use User\Entity\User;
use Interop\Container\ContainerInterface;
use Zend\ServiceManager\FactoryInterface;
use Zend\ServiceManager\ServiceLocatorInterface;

class UserFactory implements FactoryInterface{

    /**
     * @param ContainerInterface $container
     * @param string $requestedName
     * @param array|null $options
     *
     * @return User
     */
    public function __invoke(ContainerInterface $container, $requestedName, array $options = null){
        $urlHelper = $container->get("ViewHelperManager")->get("url");

        return new User($urlHelper);
    }

    /**
     * @param  ServiceLocatorInterface $serviceLocator
     *
     * @return User
     */
    public function createService(ServiceLocatorInterface $serviceLocator){
        return $this($serviceLocator, User::class);
    }

}

【问题讨论】:

  • 也许你在看stackoverflow.com/questions/25447442/… 我不确定,但为什么不将工厂注入控制器并创建用户实体
  • 可以分享一下UserFactory的内容吗?您是否有充分的理由为您的域实体编写工厂?无论如何,我会尝试为此类任务编写一个控制器插件。
  • @Roman 因为我的工厂需要服务定位器,如果我注入工厂,我也必须注入服务定位器,这是一种反模式。因此,我不能这样做。有什么想法吗?
  • @edigu 我不认为创建控制器插件是一个好主意,因为它会在控制器中引入更多的依赖关系并且更具体 - 在每个控制器中,因为控制器插件在应用程序范围内可用.我将添加 UserFactory,但这适用于每个具有依赖关系的对象。例如,查询数据库的表单或必须重复多次(以具有多个实例)的字段集。
  • @edigu 我已经添加了UserFactory,你可以看看。

标签: php dependency-injection zend-framework2 factory service-locator


【解决方案1】:

最好的方法是将用户实体的新实例注入控制器。控制器工厂应如下所示:

<?php

namespace User\Factory\Controller\Core;

use User\Controller\UserAdminController;
use Interop\Container\ContainerInterface;
use User\Entity\User;
use Zend\ServiceManager\FactoryInterface;
use Zend\ServiceManager\ServiceLocatorInterface;

class UserAdminControllerFactory implements FactoryInterface{

    /**
     * @param ContainerInterface $container
     * @param string $requestedName
     * @param array|null $options
     *
     * @return UserAdminController
     */
    public function __invoke(ContainerInterface $container, $requestedName, array $options = null){
        $serviceLocator = $container->getServiceLocator();

        return new UserAdminController($serviceLocator->get(User::class));
    }

    /**
     * @param  ServiceLocatorInterface $serviceLocator
     *
     * @return UserAdminController
     */
    public function createService(ServiceLocatorInterface $serviceLocator){
        return $this($serviceLocator, UserAdminController::class);
    }

}

在控制器内部,当需要一个新的用户实例时,可以通过调用 PHP 的 clone 函数简单地创建它。以下是控制器中的示例操作:

<?php

namespace User\Controller\Core;

use User\Entity\User;
use Zend\View\Model\ViewModel;
use Zend\Mvc\Controller\AbstractActionController;

class UserAdminController extends AbstractActionController {

    /**
     * @var User
     */
    public $user;

    /**
     * @param User $user
     */
    public function __construct(User $user){
        $this->user = $user;
    }
    /**
     * @return ViewModel
     */
    public function indexAction(){
        if($this->getRequest()->isPost()){
            $usersData = $this->params()->fromPost("usersData");
            foreach($usersData as $userData){
                $newUser = clone $this->user;
                $newUser->fromArray($userData);
                $newUser->save();
            }
        }

        return ViewModel();
    }

}

将 UserFactory 注入控制器是不是一种选择,因为工厂使用服务定位器来创建 User 类的新实例。如果工厂被注入到控制器,服务定位器也应该被注入,但是这被认为是一种反模式,此外,它在 ZendFramework3 中已被弃用并被认为是一种不好的做法。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-03-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多