如何使用ZF3设置延迟加载(任何地方都没有ServiceLocator模式) [英] How can I set up Lazy Loading with ZF3 (no ServiceLocator pattern from anywhere)

查看:89
本文介绍了如何使用ZF3设置延迟加载(任何地方都没有ServiceLocator模式)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在编写一个新的ZF2应用程序.我注意到ZF3不推荐使用从任何地方"调用服务的ServiceLocator使用模式.我想为ZF3编写代码.

I am writing a new ZF2 app. I have noticed that ServiceLocator usage pattern of calling services "from anywhere" has been deprecated from ZF3. I want to write code in mind for ZF3.

我能够设置我的Controller以便在构造函数时调用所有依赖项.但这意味着我需要先加载Doctrine对象.

I was able to set up my Controller to call all dependencies at constructor time. But that means loading i.e. Doctrine object upfront before I need it.

问题

如何设置它以便仅在需要时立即加载? (延迟加载).我了解到ZF3将加载工作转移到Controller构造上,这使得如何及时加载某些东西变得不明显.

How do I set it up so that it is only loaded when I need it immediately? (lazy-loaded). I understand that ZF3 moves loading to Controller construction, which makes it not apparent as to how to load something Just-In-Time.

旧代码

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();
    }

推荐答案

如果您没有任何有效/强烈的理由来实例化自定义实体存储库,则您最好在存储库中扩展Doctrine\ORM\EntityRepository,例如CommissionRepository .例如;

If you don't have any valid/strong reason to instantiate a custom entity repository, you should prefer extending of Doctrine\ORM\EntityRepository in your repositories like CommissionRepository. For example;

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')方法时,实体管理器将自动注入到构建中的存储库中.

By this way, entity manager will be automatically injected to the repository on construction when you call the $em->getRepository('App\Entity\Commission') method.

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

I assume that you already have a Commission entity in your app's Entity namespace:

<?php
namespace App\Entity;

use Doctrine\ORM\Mapping as ORM;

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

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

Then you can simplify the injecting process of the repository in your factory something like:

// 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),并且可以与任何

UPDATE - With the release of Service Manager V3, FactoryInterface has been moved to Zend\ServiceManager\Factory namespace (1), factories are literally invokables (2) and works with any container-interop compatible DIC (3) Updated factory would be like below:

// 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所说的那样, 懒惰服务 是在需要时立即创建服务的方法. ZF3发行时将使用zend-servicemanager 3.0组件. (当前zend-expressive使用它)自 servicemanager v3 您可以通过定义创建一些代理服务服务配置中的lazy_services 代理人:

For the question; as of marcosh's said, Lazy Services are way to go to create services when need it immediately. ZF3 will use the zend-servicemanager 3.0 component when released. (Currently zend-expressive uses it) As of servicemanager v3 you can create some proxied services by defining lazy_services and delegators in your service configuration:

'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()方法,以实现平稳迁移.

Also, starting with service manager v3 factories are compatible with the ContainerInterface. For the forward-compatibility, you may want to keep both __invoke() and createService() methods in your factories for a smooth migration.

最后,您的 ZF3兼容工厂可能看起来像:

In the end, your ZF3 compatible factory may look like:

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, []);
    }
}

希望有帮助.

这篇关于如何使用ZF3设置延迟加载(任何地方都没有ServiceLocator模式)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆