4

我正在编写一个新的 ZF2 应用程序。我注意到 ZF3 已弃用“从任何地方”调用服务的 ServiceLocator 使用模式。我想为 ZF3 编写代码。

我能够将我的控制器设置为在构造函数时调用所有依赖项。但这意味着Doctrine在我需要它之前预先加载 ie 对象。

问题

如何设置它以便仅在我需要时才加载它?(延迟加载)。我知道 ZF3 将加载移至控制器构造,这使得如何加载即时加载内容并不明显。

旧代码

class CommissionRepository
{

    protected $em;

    function getRepository()
    {
        //Initialize Doctrine ONLY when getRepository is called
        //it is not always called, and Doctrine is not always set up
        if (! $this->em)
            $this->em = $this->serviceLocator->get('doctrine');
        return $this->em;
    }
}

重构 ServiceLocator 模式后的当前代码

class CommissionRepository
{

    protected $em;

    function getRepository()
    {
        return $this->em;
    }

    function setRepository($em)
    {
        $this->em = $em;
    }

    function useRepository($id)
    {
        return $this->em->find($id);
    }
}


class CommissionControllerFactory implements FactoryInterface
{

    public function createService(ServiceLocatorInterface $serviceLocator)
    {
        $parentLocator = $controllerManager->getServiceLocator();

        // set up repository
        $repository = new CommissionRepository();
        $repository->setRepository($parentLocator->get('doctrine'));

        // set up controller
        $controller = new CommissionController($repository);
        $controller->setRepository();

        return $controller;
    }
}

class CommissionController extends AbstractActionController
{

    protected $repository;

    public function setRepository(CommissionRepository $repository)
    {
        $this->repository = $repository;
    }

    public function indexAction()
    {
         //$this->repository already contains Doctrine but it should not
         //I want it to be initialized upon use.  How?
         //Recall that it has been set up during Repository construction time
         //and I cannot call it from "anywhere" any more in ZF3
         //is there a lazy loading solution to this?
         $this->repository->useRepository();
    }
4

1 回答 1

5

如果您没有任何有效/强有力的理由来实例化自定义实体存储库,您应该更喜欢Doctrine\ORM\EntityRepository在您的存储库中扩展CommissionRepository. 例如;

use Doctrine\ORM\EntityRepository;

class CommissionRepository extends EntityRepository
{
    // No need to think about $em here. It will be automatically
    // injected by doctrine when you call getRepository().
    // 
    function fetchCommissionById($id)
    {
        // You can easily get the object manager directly (_em) or
        // using getEntityManager() accessor method in a repository
        return $this->_em->find($id);
    }
}

通过这种方式,当您调用该方法时,实体管理器将在构建时自动注入到存储库中$em->getRepository('App\Entity\Commission')

我假设Commission您的应用程序的命名空间中已经有一个实体Entity

<?php
namespace App\Entity;

use Doctrine\ORM\Mapping as ORM;

/**
 * @ORM\Entity(repositoryClass="App\Repo\CommissionRepository")
 * @ORM\Table
 */
class Commission
{
}

然后,您可以简化工厂中存储库的注入过程,例如:

// ZF2 Way
class CommissionControllerFactory implements FactoryInterface
{

    public function createService(ServiceLocatorInterface $services)
    {
        $em = $services->getServiceLocator()->get('doctrine');
        $repository = $em->getRepository('App\Entity\Commission');

        return new CommissionController($repository);
    }
}

更新- 随着 Service Manager V3 的发布,FactoryInterface 已移至Zend\ServiceManager\Factory命名空间 (1),工厂实际上是可调用的 (2) 并适用于任何容器互操作兼容的 DIC (3) 更新的工厂如下所示:

// ZF3 Way
use Zend\ServiceManager\Factory\FactoryInterface;
use Interop\Container\ContainerInterface;
use Doctrine\ORM\EntityManager;

class CommissionControllerFactory implements FactoryInterface
{

    public function __invoke(ContainerInterface $dic, $name, array $options = null) {
        $em = $dic->get(EntityManager::class);
        $repository = $em->getRepository('App\Entity\Commission');

        return new CommissionController($repository);
    }
}

对于这个问题;正如 marcosh 所说,Lazy Services是一种在需要时立即创建服务的方法。ZF3 发布时将使用 zend-servicemanager 3.0 组件。(目前 zend-expressive 使用它)从servicemanager v3开始,您可以通过在服务配置中定义lazy_services委托人来创建一些代理服务:

'factories' => [],
'invokables' => [],
'delegators' => [
    FooService::class => [
        FooServiceDelegatorFactory::class,
    ], 
],
'lazy_services' => [
    // map of service names and their relative class names - this
    // is required since the service manager cannot know the
    // class name of defined services up front
    'class_map' => [
        // 'foo' => 'MyApplication\Foo',
    ],

    // directory where proxy classes will be written - default to system_get_tmp_dir()
    'proxies_target_dir' => null,

    // namespace of the generated proxies, default to "ProxyManagerGeneratedProxy"
    'proxies_namespace' => null,

    // whether the generated proxy classes should be written to disk or generated on-the-fly
    'write_proxy_files' => false,
];

此外,从服务管理器 v3 开始,工厂ContainerInterface兼容。为了向前兼容,您可能希望将__invoke()createService()方法都保留在您的工厂中,以便顺利迁移。

最后,您的ZF3 兼容工厂可能如下所示:

class CommissionControllerFactory implements FactoryInterface
{
    public function __invoke(ContainerInterface $container, $name, array $options = null)
    {
        $em = $container->get('doctrine');
        $repository = $em->getRepository('App\Entity\Commission');

        return new CommissionController($repository);
    }

    public function createService(ServiceLocatorInterface $container, $name = null, $requestedName = null)
    {
        return $this($container, $requestedName, []);
    }
}

希望能帮助到你。

于 2016-06-08T22:57:43.340 回答