2013-03-07 76 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