Zend framework2 zf2:如何在mapper中获取服务管理器

Zend framework2 zf2:如何在mapper中获取服务管理器,zend-framework2,Zend Framework2,在Zend Framework 1中,我有几个映射器,它们从父映射器类继承了setDbTable和getDbTable 现在在ZF2 a中,我面临一个问题,即我需要模型中的服务管理器,而我不知道如何获得它: class Mapper { protected $tableGateway; protected $module = 'application'; public function setTableGateway($table

在Zend Framework 1中,我有几个映射器,它们从父映射器类继承了setDbTable和getDbTable

现在在ZF2 a中,我面临一个问题,即我需要模型中的服务管理器,而我不知道如何获得它:

    class Mapper
    {
        protected $tableGateway;
        protected $module = 'application';

        public function setTableGateway($table)
            {
                if (is_string($table)) {
                    $class = $this->module . '\Model\DbTable\\' . ucfirst($table);
                    $sm = $this->getServiceLocator(); <= Fatal error: Call to undefined method Mapper::getServiceLocator()
                    $tableGateway = (class_exists($class)) ? $sm->get($class) : $sm->get(new TableGateway($table));
                } else {
                    $tableGateway = $table;
                }

                if (!$tableGateway instanceof Zend\Db\TableGateway\AbstractTableGateway) {
                    throw new \Exception('Invalid table data gateway provided');
                }
                $this->tableGateway = $tableGateway;
                return $this;
            }

        // more code
给出一个致命错误:

Call to undefined method Application\Model\Mapper\Doc::getServiceLocator()

如何在我的模型中获得服务经理?还是我没有按ZF2的方式做事?我知道如何在我的控制器中获取服务管理器,并将tableGateway传递给映射器,但这对我来说似乎是代码的大量重复。

注意:正如Jurian Sluiman和andy124在他们的评论中指出的,永远不要在特定于域的模型/对象中注入服务管理器和依赖服务管理器,这不是一个好的实践,因为这会使特定于领域的对象变得僵硬,并影响可移植性。下面的解决方案更具体地针对所提出的问题

$model = $this->getServiceLocator()->get('MyModel'); 我按照以下步骤在我的类、模型等中获取sevicelocator(服务管理器)
1.创建实现ServiceMangerAwareInterface的类
2.在Module.php中的serviceconfig中定义条目 像这样

public function getServiceConfig(){ return array( 'factories' => array( /* Models */ 'MyModule\MyModel' => function($sm) { return new Models\MyModel($sm); }, /* Entities */ 'MyModule\MyEntity1' => function($sm) { return new Entity\MyEntity1($sm); }, 'MyModule\MyEntity2' => function($sm) { return new Entity\MyEntity2($sm); }, . . . ), ); ` 公共函数getServiceConfig(){ 返回数组( “工厂”=>数组( /*模型*/ “MyModule\MyModel”=>函数($sm){ 返回新模型\MyModel($sm); }, /*实体*/ 'MyModule\MyEntity1'=>函数($sm){ 返回新实体\MyEntity1($sm); }, 'MyModule\MyEntity2'=>函数($sm){ 返回新实体\MyEntity2($sm); }, . . . ), ); `
  • 使用服务管理器访问您的模型或类,如下所示(例如控制器下) $model = $this->getServiceLocator()->get('MyModule\MyModel'); $model = $this->getServiceLocator()->get('MyModel'); $model=$this->getServiceLocator()->get('MyModule\MyModel');
  • 您还可以在module.config.php中进行服务管理器配置 'service_manager' => array( 'factories' => array( 'MyModel' => 'MyModule\Models\MyModel', 'MyEntity' => 'MyModule\Entity\MyEntity', ), ), “服务管理器”=>阵列( “工厂”=>数组( “MyModel”=>“MyModule\Models\MyModel”, “MyEntity”=>“MyModule\Entity\MyEntity”, ), ), 4.使用服务经理访问您的模型或类,如下所示(例如控制器下) $model = $this->getServiceLocator()->get('MyModule\MyModel'); $model = $this->getServiceLocator()->get('MyModel'); $model=$this->getServiceLocator()->get('MyModel');
    首先,我认为您的意思是希望从mapper类而不是模型访问服务管理器。我将避免使用后者。有关更多详细信息,请参阅我对Raj答案的评论

    第二,有很多方法可以做到这一点。在这个回答中,我将给你们一个例子,说明一种方法,并仅提及另一种方法

    查看(向下滚动一点),它声明将默认初始值设定项添加为默认值。此初始值设定项检查从服务管理器检索的对象是否实现了
    Zend\ServiceManager\ServiceLocatorAwareInterface
    。如果实现了,则服务管理器将被注入到对象中。因此,如果只实现映射器类中的接口。您可以使用抽象基类来避免为每个映射器类重写此接口。可能类似于以下内容

    基本映射器类:

    namespace User\Mapper;
    
    use Zend\ServiceManager\ServiceLocatorAwareInterface;
    use Zend\ServiceManager\ServiceLocatorInterface;
    
    class AbstractMapper implements ServiceLocatorAwareInterface {
        protected $service_manager;
    
        public function setServiceLocator(ServiceLocatorInterface $serviceLocator)
        {
            $this->service_manager = $serviceLocator;
        }
    
        public function getServiceLocator()
        {
            return $this->service_manager;
        }
    }
    
    namespace User\Mapper;
    
    use User\Mapper\AbstractMapper;
    
    class UserMapper extends AbstractMapper {
        public function doSomething() {
            $sm = $this->getServiceLocator();
            $sm->get('something');
        }
    }
    
    映射器类:

    namespace User\Mapper;
    
    use Zend\ServiceManager\ServiceLocatorAwareInterface;
    use Zend\ServiceManager\ServiceLocatorInterface;
    
    class AbstractMapper implements ServiceLocatorAwareInterface {
        protected $service_manager;
    
        public function setServiceLocator(ServiceLocatorInterface $serviceLocator)
        {
            $this->service_manager = $serviceLocator;
        }
    
        public function getServiceLocator()
        {
            return $this->service_manager;
        }
    }
    
    namespace User\Mapper;
    
    use User\Mapper\AbstractMapper;
    
    class UserMapper extends AbstractMapper {
        public function doSomething() {
            $sm = $this->getServiceLocator();
            $sm->get('something');
        }
    }
    
    由于服务管理器是在运行初始值设定项时注入的,因此应该从服务管理器中检索映射器。如果您不想/必须向映射器类中注入任何内容,则可调用的就足够了。它们可以添加到
    invokables
    键下,该键嵌套在
    service\u manager
    键下
    >module_name/config/module.config.php
    。或者,它可以在module类“
    getServiceConfig
    方法中配置。但是,您的映射器类现在或将来很可能会有一些依赖项,因此您可能希望使用工厂来代替。这样,您可以让服务管理器注入一个表gat进入mapper类的途径

    'service_manager' => array( 'factories' => array( 'MyModel' => 'MyModule\Models\MyModel', 'MyEntity' => 'MyModule\Entity\MyEntity', ), ),
    // Remember to include the appropriate namespaces
    return array(
        'factories' => array(
            'User\Mapper\UserMapper' => function($service_manager) {
                $table_gateway = $service_manager->get('User\Model\DbTable\UserGateway');
                return new UserMapper($table_gateway);
            },
        ),
    );
    
    可以在模块的
    module.php
    文件中的
    getServiceConfig
    方法中添加上述内容,或者在
    service\u manager
    中的
    factories
    键中添加
    module\u name/config/module.config.php
    。您仍然需要添加一个创建数据库网关的工厂;以上只是一个例子例如

    'service_manager' => array( 'factories' => array( 'MyModel' => 'MyModule\Models\MyModel', 'MyEntity' => 'MyModule\Entity\MyEntity', ), ),
    这就是我要做的。当然,可以简单地在mapper类中为服务管理器提供一个getter和setter方法,并从控制器访问这些方法(控制器有一个
    getServiceLocator
    方法)然后像那样注入。不过,我自己不会采用这种方法。

    在模型中注入服务管理器通常是一个非常糟糕的主意。尝试在类中注入依赖项,而不是依赖于要注入的服务容器。这是“依赖项注入”与“服务容器”Jurian Sluiman:这对我来说是一个困难的讨论,因为我最近才了解依赖注入,但我想我已经了解了基本的想法。下面Andy的答案如何(我接受了,因为它是有效的)?在您看来,这也是一种反模式吗?您可以好好阅读本文:(对我来说,Anthony Ferrara是一位非常受尊敬的开发人员)。这并不是真的那么黑/白,但关于同一件事,这里提出了一个类似的问题:您的答案中有一些错误:1.如果您有自己的工厂,并且已经在其中注入了SM,则不需要实现
    ServiceManagerWareInterface
    。2.一般来说,在域模型中注入SM的想法是非常复杂的在你的
    config.php
    (你可能指的是
    module.config.php
    )中,你使用模型作为一个可调用的服务,但你使用工厂服务。由于这些原因,我投你反对票,但你可以在 'service_manager' => array( 'factories' => array( 'MyModel' => 'MyModule\Models\MyModel', 'MyEntity' => 'MyModule\Entity\MyEntity', ), ), $model = $this->getServiceLocator()->get('MyModel');