2013-03-07 74 views
3

我已经为我的模块编写了自定义服务。此服务提供了应验证给定令牌的公共静态函数。Zend Framework中的服务中的实体管理器2

现在我想实现另一个公共静态函数,它检查一个Doctrine-Entity是否存在。对于这种情况,我需要服务中的对象管理器或服务定位器。

class ApiService 
{ 
    const KEY_LENGTH = 10; 
    const USE_NUMBERS = true; 
    const USE_CHARS = true; 

    public static function isValid($apiKey) { 
     $isValid = false; 
     # more code tbd 
     $isValid = self::exists($apiKey); 
     return $isValid; 
    } 

    public static function exists($apiKey) { 
    # Insert Object-Manager here 

     $validator = new \DoctrineModule\Validator\ObjectExists(array(
      'object_repository' => $objectManager->getRepository('Application\Entity\User'), 
      'fields' => array('email') 
     )); 
    } 
} 
  1. 它是“最佳实践”实现我作为公共静态,并呼吁他们为静态方法?

  2. 将对象管理器注入到我的doesEntityExist()函数中的最佳做法是什么?

回答

15

最好的方法是从这里完全删除你的类的静态方法。 ZF2使得通过名字获取服务变得非常简单,所以你不应该为这种用例真正需要静态方法。

首先,清理您的服务:

namespace MyApp\Service; 

use Doctrine\Common\Persistence\ObjectRepository; 
use DoctrineModule\Validator\ObjectExists; 

class ApiService 
{ 
    // ... 

    protected $validator; 

    public function __construct(ObjectRepository $objectRepository) 
    { 
     $this->validator = new \DoctrineModule\Validator\ObjectExists(array(
      'object_repository' => $objectRepository, 
      'fields'   => array('email') 
     )); 
    } 

    public function exists($apiKey) 
    { 
     return $this->validator->isValid($apiKey); 
    } 

    // ... 
} 

现在定义一个工厂它:

namespace MyApp\ServiceFactory; 

use MyApp\Service\ApiService; 
use Zend\ServiceManager\FactoryInterface; 
use Zend\ServiceManager\ServiceLocatorInterface; 

class ApiServiceFactory implements FactoryInterface 
{ 
    public function createService(ServiceLocatorInterface $serviceLocator) 
    { 
     $entityManager = $serviceLocator->get('Doctrine\ORM\EntityManager'); 
     $repository = $entityManager->getRepository('Application\Entity\User'); 

     return new ApiService($repository); 
    } 
} 

服务名称,然后映射到工厂(通常在你的模块) :

namespace MyApp; 

use Zend\ModuleManager\Feature\ConfigProviderInterface; 

class Module implements ConfigProviderInterface 
{ 
    public function getConfig() 
    { 
     return array(
      'service_manager' => array(
       'factories' => array(
        'MyApp\Service\ApiService' 
         => 'MyApp\ServiceFactory\ApiServiceFactory', 
       ), 
      ), 
     ); 
    } 
} 

注意:您可能只想简单地使用闭包而不是定义单独的工厂类,但在不使用服务时使工厂类可以提高性能。此外,在配置中使用闭包意味着您无法缓存合并的配置,因此请考虑使用此处提供的方法。

这里没有工厂类的实例(再次,可以考虑使用这种方法如上所述):

namespace MyApp; 

use Zend\ModuleManager\Feature\ServiceProviderInterface; 

class Module implements ServiceProviderInterface 
{ 
    public function getServiceConfig() 
    { 
     return array(
      'factories' => array(
       'MyApp\Service\ApiService' => function ($sl) { 
        $entityManager = $serviceLocator->get('Doctrine\ORM\EntityManager'); 
        $repository = $entityManager->getRepository('Application\Entity\User'); 

        return new MyApp\Service\ApiService($repository); 
       }, 
      ), 
     ); 
    } 
} 

现在你可以在你的控制器使用服务:

class MyController extends AbstractActionController 
{ 
    // ... 

    public function apiAction() 
    { 
     $apiService = $this->getServiceLocator()->get('MyApp\Service\ApiService'); 

     if (! $apiService->isValid($this->params('api-key')) { 
      throw new InvalidApiKeyException($this->params('api-key')); 
     } 

     // ... 
    } 

    // ... 
} 

您也可以检索它在任何你有服务经理的地方:

$validator = $serviceLocator->get('MyApp\Service\ApiService'); 

作为额外的建议请考虑简化您的服务。由于isValid已经是你的验证的方法,你可以简单地返回验证本身(在此使用简单的方法关闭):

namespace MyApp; 

use Zend\ModuleManager\Feature\ServiceProviderInterface; 
use DoctrineModule\Validator\ObjectExists; 

class Module implements ServiceProviderInterface 
{ 
    public function getServiceConfig() 
    { 
     return array(
      'factories' => array(
       'MyApp\Validator\ApiKeyValidator' => function ($sl) { 

        $entityManager = $serviceLocator->get('Doctrine\ORM\EntityManager'); 
        $repository = $entityManager->getRepository('Application\Entity\User'); 
        new ObjectExists(array(
         'object_repository' => $objectRepository, 
         'fields'   => array('email') 
        )); 
       }, 
      ), 
     ); 
    } 
} 
+0

嗨@Ocramius,谢谢你的帮助。 – Roman 2013-03-07 10:41:57

+0

@Ocramius,我喜欢ServiceFactory实现。但是,我们怎样才能以这种方式将$ entityManager用于其他事情呢?我想使用Hydrator,但我无法确定在哪里/如何。我不认为它的好做法是让控制器再次获得$ entitymanager。 – SimonV 2013-03-10 16:27:16

+0

@SimonV,正如我在http://marco-pivetta.com/doctrine-orm-zf2-tutorial/#/38/11中解释的那样,在控制器中使用持久层可能不是一个好主意。考虑将水合物本身定义为服务,然后将水合物传递给你自己的服务(例如)'MyFooService :: save(Foo $ foo)' – Ocramius 2013-03-10 16:37:45

0

就个人而言,我会做该服务的“服务”,并把它放在ServiceManager。另外我会考虑重构代码。现在你依赖于ObjectExists验证器,而验证器又依赖于和实体库,这取决于实体管理器。在服务外部组装验证器并从工厂注入验证器会更简单。那样的话,如果你需要使用不同的验证器,你只需要一个不同的验证器。

class ApiService 
{ 
    protected $validator; 

    public function isValid($apiKey) 
    { 
     // other code 
     $isValid = $this->exists($apiKey); 
    } 

    public function exists($apiKey) 
    { 
     return $this->getValidator()->isValid($apiKey); 
    } 

    public function setValidator(\Zend\Validator\AbstractValidator $validator) 
    { 
     $this->validator = $validator; 
     return $this; 
    } 

    public function getValidator() 
    { 
     return $this->validator; 
    } 
} 

在Module.php创建服务作为工厂方法,或者更好的是作为一个工厂类,但剩下的作为练习你:)

public function getServiceConfig() 
{ 
    return array(
     'factories' => array(
      'ApiService' => function($sm) { 
       $em = $sm->get('Doctrine\ORM\EntityManager'); 
       $repo = $em->getRepository('Application\Entity\User'); 
       $validator = new \DoctrineModule\Validator\ObjectExists($repo, 
        array('fields' => array('email'))); 
       $service = new ApiService(); 
       $service->setValidator($validator); 
       return $service; 
      }, 
     ), 
    ); 
} 

现在,如果你需要一个不同EntityManager,一个不同的实体仓库,甚至是一个完全不同的验证器,你只需要改变上面的几行,而不必深入你的服务代码。

+0

哇,听起来不错。明天我会试一试。感谢那。 – Roman 2013-03-07 21:57:31