Поиск сервисов в Zend Framework 2

В контроллере я создаю и использую свою модель, поэтому

public function getAlbumTable()
{
    if (!$this->albumTable) {
        $sm = $this->getServiceLocator();
        $this->albumTable = $sm->get('Album\Model\AlbumTable');
    }
    return $this->albumTable;
}

Как мне использовать этот глобальный Service Locator в другом месте моего проекта, например, в другой модели, а не только в любом контроллере?

Конфигурация подключения к БД определяется в файле: my_project/config/autoload/global.php

Спасибо.


person Eremite    schedule 07.10.2012    source источник


Ответы (3)


Zend MVC внедрит экземпляр ServiceLocator в класс, реализующий Zend\ServiceManager\ServiceLocatorAwareInterface. Простая реализация таблицы модели выглядит следующим образом:

<?php
use Zend\Db\TableGateway\AbstractTableGateway;
use Zend\ServiceManager\ServiceLocatorAwareInterface;
use Zend\ServiceManager\ServiceLocatorInterface;

class UserTable extends AbstractTableGateway implements ServiceLocatorAwareInterface {
  protected $serviceLocator;

  public function setServiceLocator(ServiceLocatorInterface $serviceLocator) {
    $this->serviceLocator = $serviceLocator;
  }

  public function getServiceLocator() {
    return $this->serviceLocator;
  }

  // now instance of Service Locator is ready to use
  public function someMethod() {
    $table = $this->serviceLocator->get('Album\Model\AlbumTable');
    //...
  }
}
person Elvan    schedule 07.10.2012
comment
Что ж, спасибо, это действительно работает! А как быть с классами, отличными от class extends AbstractTableGateway, например, с классами форм (extends Zend\Form), классами элементов (extends Zend\Form\Element)? Как использовать ServiceLocator там? Или использовать его там плохая идея?.. - person Eremite; 07.10.2012
comment
Если класс создается с помощью ServiceManager, который реализует осведомленный интерфейс, то ServiceManager внедряется после создания класса. Для Zend\Form это может создать проблему, так как большая часть настройки элементов обычно выполняется в конструкторе (SM вводится после этого момента). Об этом есть сообщение в блоге - michaelgallego.fr/blog/?p=205 - person DrBeza; 08.10.2012
comment
Я понял суть вашего метода. Непонятно только, как заставить ServiceManager автоматически вызывать метод setServiceManager при создании экземпляра формы, расширяющей класс Form, который вы описываете в своем блоге. Вы пишете о необходимости использования массива 'invokables' для ServiceManager, не могли бы вы подробно рассказать, как это сделать. Спасибо. - person Eremite; 08.10.2012

Решенный. Так. Для решения задачи классы моделей должны реализовать интерфейс ServiceLocatorAwareInterface. Таким образом, внедрение ServiceManager произойдет в вашей модели автоматически. См. предыдущий пример.

Для форм и других объектов вашего приложения подходит метод, предложенный здесь http://michaelgallego.fr/blog/?p=205 Вы можете создать форму базового класса, расширяющую BaseForm и реализующую ServiceManagerAwareInterface, от которой вы будете наследовать свои формы в приложении.

namespace Common\Form;

use Zend\Form\Form as BaseForm;
use Zend\ServiceManager\ServiceManager;
use Zend\ServiceManager\ServiceManagerAwareInterface;

class Form extends BaseForm implements ServiceManagerAwareInterface
{
    /**
     * @var ServiceManager
     */
    protected $serviceManager;

    /**
     * Init the form
     */
    public function init()
    {
    }

    /**
     * @param ServiceManager $serviceManager
     * @return Form
     */
    public function setServiceManager(ServiceManager $serviceManager)
    {
        $this->serviceManager = $serviceManager;

        // Call the init function of the form once the service manager is set
        $this->init();

        return $this;
    }
}

Чтобы закачка объекта ServiceManager была автоматически в файле module.config.php в секции service_manager нужно написать

'invokables' => array(
    'Album\Form\AlbumForm' => 'Album\Form\AlbumForm',
),

Затем в вашем контроллере вы можете создать форму, чтобы

$form = $this->getServiceLocator()->get('Album\Form\AlbumForm');

Форма будет содержать объект ServiceManager, который позволит использовать другие зависимости.

Спасибо всем за вашу помощь.

person Eremite    schedule 09.10.2012

getServicelocator() делает ошибку. Так что нужен альтернативный способ. И в расширениях AbstractTableGateway или ServiceLocatorAwareInterface есть ошибки.

Фабричная реализация поможет Контроллеру получить объекты.

*Код образца пользователя будет аналогичен альбому.

1) фабричный класс ( RegisterControllerFactory.php) * скопированная функция createUser в контроллере

namespace Users\Controller\Factory;

use Users\Controller\RegisterController;

use Zend\ServiceManager\FactoryInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
use Zend\ServiceManager\Exception\ServiceNotCreatedException;


class RegisterControllerFactory   
{

    public function __invoke($serviceLocator)
    {  
    $sm = $serviceLocator;
    $dbAdapter = $sm->get('Zend\Db\Adapter\Adapter');

    $resultSetPrototype = new \Zend\Db\ResultSet\ResultSet();
    $resultSetPrototype->setArrayObjectPrototype(new \Users\Model\User);
    $tableGateway       = new \Zend\Db\TableGateway\TableGateway('user' /* table name  */, $dbAdapter, null, $resultSetPrototype);

    $user = new \Users\Model\User();  
    $userTable = new \Users\Model\UserTable($tableGateway);

    $controller = new RegisterController($userTable, $serviceLocator );
    return $controller;

   }
} 

2) контроллер( RegisterController ) пространство имен Users\Controller;

use Zend\Mvc\Controller\AbstractActionController;
use Zend\View\Model\ViewModel;

use Users\Form\RegisterForm;
use Users\Form\RegisterFilter;

use Users\Model\User;
use Users\Model\UserTable;

use Zend\ServiceManager\ServiceLocatorInterface;

class RegisterController extends AbstractActionController
{
    protected $userTable;
    protected $serviceManager;

    public function __construct(UserTable $userTable, ServiceLocatorInterface     $serviceManager)
{
    $this->userTable = $userTable;
    $this->serviceManager = $serviceManager;
}

public function indexAction()
{
    $form = new RegisterForm();
    $viewModel  = new ViewModel(array('form' => $form)); 
    return $viewModel; 
}

public function processAction()
{
    if (!$this->request->isPost()) {
        return $this->redirect()->toRoute(NULL , array( 
                    'controller' => 'register', 
                    'action' =>  'index' 
                ));
    }

    $post = $this->request->getPost();

    $form = new RegisterForm();
    $inputFilter = new RegisterFilter();
    $form->setInputFilter($inputFilter);

    $form->setData($post);
    if (!$form->isValid()) {
        $model = new ViewModel(array(
            'error' => true,
            'form'  => $form,
        ));
        $model->setTemplate('users/register/index');
        return $model;
    }

    // Create user
    $this->createUser($form->getData());

    return $this->redirect()->toRoute(NULL , array( 
                    'controller' => 'register', 
                    'action' =>  'confirm' 
                ));
}

public function confirmAction()
{
    $viewModel  = new ViewModel(); 
    return $viewModel; 
}

protected function createUser(array $data)
{  /*able to delete or modify */
    $sm = $this->serviceManager;
    $dbAdapter = $sm->get('Zend\Db\Adapter\Adapter');

    $resultSetPrototype = new \Zend\Db\ResultSet\ResultSet();
    $resultSetPrototype->setArrayObjectPrototype(new \Users\Model\User);
    $tableGateway       = new \Zend\Db\TableGateway\TableGateway('user' /* table name  */, $dbAdapter, null, $resultSetPrototype);

    $user = new User();
    $user->exchangeArray($data);

    $userTable = new UserTable($tableGateway);
    $userTable->saveUser($user);

    return true;
    }
}

3) модуль.config.php

return array(   
    'controllers' => array(
    'invokables' => array(
        'Users\Controller\Index' => 'Users\Controller\IndexController', 
        'Users\Controller\login' => 'Users\Controller\LoginController', 
        //delete 'Users\Controller\Register'
    ),
    'factories' => array(
            'Users\Controller\Register' =>     'Users\Controller\Factory\RegisterControllerFactory',
   ),       
),
person Marcus    schedule 02.08.2016