Entity Manager in Service in Zend Framework 2

前端 未结 2 1425
孤城傲影
孤城傲影 2021-02-03 16:06

I have written a custom service for my module. This service provides public static functions which should validate a given token.

Now i want to implement another public

相关标签:
2条回答
  • 2021-02-03 16:41

    Personally, I'd make the service a 'service' and put it in the ServiceManager. In addition I'd consider refactoring the code. Right now you have a dependency on the ObjectExists validator, which in turn depends on and entity repository, and that depends on the entity manager. It would be much simpler to compose the validator outside the service and inject it from a factory. That way, if you ever need to use a different validator, you just hand it a different one.

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

    In Module.php create the service as a factory method, or better still as a factory class, but that's left as an exercise for you :)

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

    Now if you need a different EntityManager, a different Entity repository, or even a whole different validator you only need to change a couple of lines above rather than having to delve into your services code.

    0 讨论(0)
  • 2021-02-03 16:58

    The best approach would be to completely remove the static methods from your class here. ZF2 makes it really easy to fetch services by their name, so you shouldn't really need static methods for such a use case.

    First of all, clean up your service:

    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);
        }
    
        // ...
    }
    

    Now define a factory for it:

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

    Then map the service name to the factory (usually in your module):

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

    NOTE: you may want to simply use a closure instead of defining a separate factory class, but having factory classes gives you a small performance boost when you're not using the service. Also, using a closure in configuration means you cannot cache the merged configuration, so consider using the method suggested here.

    Here's an example without the factory class (again, consider using the approach explained above):

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

    Now you can use the service in your controllers:

    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'));
            }
    
            // ...
        }
    
        // ...
    }
    

    You can also retrieve it wherever you have the service manager:

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

    As an additional suggestion, consider simplifying your service. Since isValid is already a method of your validator, you could simply return the validator itself (hereby using the closure method for simplicity):

    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 讨论(0)
提交回复
热议问题