Как заставить serviceManager работать на классе zf2

0

У меня есть некоторые сервисы, определенные на моем модуле.php, который работает как предполагалось как:

public function getServiceConfig()
{
    return array(
        'factories' => array(
            'Marketplace\V1\Rest\Service\ServiceCollection' =>  function($sm) {
                    $tableGateway = $sm->get('ServiceCollectionGateway');
                    $table = new ServiceCollection($tableGateway);
                    return $table;
                },
            'ServiceCollectionGateway' => function ($sm) {
                    $dbAdapter = $sm->get('PdoAdapter');
                    $resultSetPrototype = new ResultSet();
                    $resultSetPrototype->setArrayObjectPrototype(new ServiceEntity());
                    return new TableGateway('service', $dbAdapter, null, $resultSetPrototype);
                },
            'Marketplace\V1\Rest\User\UserCollection' =>  function($sm) {
                    $tableGateway = $sm->get('UserCollectionGateway');
                    $table = new UserCollection($tableGateway);
                    return $table;
                },
            'UserCollectionGateway' => function ($sm) {
                    $dbAdapter = $sm->get('PdoAdapter');
                    $resultSetPrototype = new ResultSet();
                    $resultSetPrototype->setArrayObjectPrototype(new UserEntity());
                    return new TableGateway('user', $dbAdapter, null, $resultSetPrototype);
                },
        ),
    );
}

Я использую их для сопоставления своих таблиц db с объектом. Из моих основных классов моего проекта я могу получить к ним доступ без каких-либо проблем. Посмотрите мое дерево файлов проекта:

Изображение 174551

Например, userResource.php расширяет abstractResource, и эта функция работает:

public function fetch($id)
{
    $result = $this->getUserCollection()->findOne(['id'=>$id]);
    return $result;
}

Внутри ResourceAbstract У меня есть:

<?php

namespace Marketplace\V1\Abstracts;

use ZF\Rest\AbstractResourceListener;
use Zend\ServiceManager\ServiceLocatorAwareInterface;
use Zend\ServiceManager\ServiceLocatorInterface;

class ResourceAbstract extends AbstractResourceListener implements ServiceLocatorAwareInterface {

    protected $serviceLocator;


    public function getServiceCollection() {
        $sm = $this->getServiceLocator();
        return $sm->get('Marketplace\V1\Rest\Service\ServiceCollection');
    }

    public function getUserCollection() {
        $sm = $this->getServiceLocator();
        return $sm->get('Marketplace\V1\Rest\User\UserCollection');
    }

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

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

} 

Там, как было предложено в документации Zf2, мне нужно реализовать ServiceLocatorAwareInterface для использования serviceManager. Все идет нормально. Затем я решил добавить новый класс, назовите Auth.

Этот класс не очень отличается от abstractResource, он получает вызов в loginController следующим образом:

<?php
namespace Marketplace\V1\Rpc\Login;

use Zend\Mvc\Controller\AbstractActionController;
use Marketplace\V1\Functions\Auth;

class LoginController extends AbstractActionController
{
    public function loginAction()
    {
        $auth = new Auth();
        $data = $this->params()->fromPost();
        var_dump($auth->checkPassword($data['email'], $data['password']));

        die;
    }


}

Это Auth:

<?php

namespace Marketplace\V1\Functions;


use Zend\ServiceManager\ServiceLocatorAwareInterface;
use Zend\ServiceManager\ServiceLocatorInterface;

class Auth implements ServiceLocatorAwareInterface {

    protected $serviceLocator;

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

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

    public function checkPassword($email, $rawPassword) {

        $user = $this->getServiceLocator()->get('Marketplace\V1\Rest\User\UserCollection')->findByEmail($email);
        if($user)
            return false;

        $result = $this->genPassword($rawPassword, $user->salt);

        if($result['password'] === $user->password)
            return true;
        else
            return false;

    }

    public function genPassword($rawPassword, $salt = null) {
        if(!$salt)
            $salt = mcrypt_create_iv(22, MCRYPT_DEV_URANDOM);
        $options = [
            'cost' => 11,
            'salt' => $salt,
        ];
        return ['password' => password_hash($rawPassword, PASSWORD_BCRYPT, $options), 'salt' => bin2hex($salt)];
    }

}

Как вы можете видеть, он следует тому же пути, что abtractResource, BUT, в этом случае, когда я выполняю loginController, я получаю сообщение об ошибке:

Fatal error</b>:  Call to a member function get() on null in C:\WT-NMP\WWW\MarketPlaceApi\module\Marketplace\src\Marketplace\V1\Functions\Auth.php on line 25

И это относится к этой строке: $user = $this->getServiceLocator()->get('Marketplace\V1\Rest\User\UserCollection')->findByEmail($email);

Это означает, что getServiceLocator пуст. Почему я не могу заставить serviceLocator работать в классе Auth, но я могу в abstractResource?

Теги:
zend-framework2
apigility

1 ответ

2
Лучший ответ

Это потому, что ServiceLocator вводится через механизм, называемый "инъекция установщика". Чтобы это произошло, что-то (например, ServiceManager) нужно вызвать setter для класса, в данном случае setServiceLocator. Когда вы напрямую создаете Auth это не так. Вам нужно добавить свой класс в локатор службы, например, в качестве invokable-сервиса.

Например:

public function getServiceConfig()
{
    return array(
        'invokables' => array(
            'Auth' => '\Marketplace\V1\Functions\Auth',
        ),
    );
}

или, более appriopriatly, так как он не использует анонимную функцию для фабрики, поместите ее в свой файл конфигурации модуля "modules/Marketplace/config/module.config.php":

// ... 
'service_manager' => array(
    'invokables' => array(
        'Auth' => '\Marketplace\V1\Functions\Auth',
    ),
),

и вы можете получить Auth из локатора службы в вашем контроллере:

$auth = $this->getServiceLocator()->get('Auth');

вместо:

$auth = new Auth;

Таким образом, локатор службы будет создавать Auth для вас, проверить, какие интерфейсы он реализует, и когда он узнает, что он реализует ServiceLocatorAwareInterface тогда он запустит setter, передав ему экземпляр. Удовлетворительный факт: сам контроллер получает инжектированный экземпляр локатора сервисов таким же образом (он является предком этого класса, реализующим тот же интерфейс). Еще один забавный факт: это поведение может измениться в будущем, как описано здесь.

  • 0
    Можете ли вы показать мне, как с примером?
  • 0
    Я расширил ответ. Я думаю, что мы только что построили здесь небоскреб.
Показать ещё 4 комментария

Ещё вопросы

Сообщество Overcoder
Наверх
Меню