简体   繁体   中英

How to get serviceManager to work on a class zf2

I have some services defined on my module.php that works as intended declared as:

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

I use them to map my db tables to an object. From my main classes of my project I can access them without any problem. Take a look of my project file tree:

在此处输入图片说明

For example, userResource.php extends abstractResource and this function works:

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

Inside ResourceAbstract I have:

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

} 

There as suggested by the Zf2 documentation I need to implement ServiceLocatorAwareInterface in order to use the serviceManager. So far so good. Then I decided to add a new class, call Auth.

This class is not very different from abstractResource, it gets call in loginController like this:

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


}

This is 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)];
    }

}

As you can see it follows the same path that abtractResource do, BUT, in this case when I execute loginController I get an error:

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

And that refers to this line: $user = $this->getServiceLocator()->get('Marketplace\\V1\\Rest\\User\\UserCollection')->findByEmail($email);

Meaning that getServiceLocator is empty. Why I cant get serviceLocator to work on Auth class but I can in abstractResource?

That's because the ServiceLocator is injected through a mechanism called 'setter injection'. For that to happen, something (eg, ServiceManager ) needs to call a setter for a class, in this case setServiceLocator . When you directly instantiate Auth that's not the case. You need to add your class to the service locator, for example as an invokable service.

Eg:

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

or, more appriopriatly as it doesn't use a anonymous function for a factory, put it in your modules config file `modules/Marketplace/config/module.config.php':

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

and the you can get Auth from the service locator in your controller:

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

instead of:

$auth = new Auth;

This way the service locator will construct Auth for you, check what interfaces it implements and when it finds out that it does implement ServiceLocatorAwareInterface then it'll run the setter passing an instance of itself into it. Fun fact: the controller itself gets injected an instance of service locator the same way (it's an ancestor of this class implementing the very same interface). Another fun fact: that behaviour may change in future as discussed here .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM