如何让 serviceManager 在 class zf2 上工作
How to get serviceManager to work on a class zf2
我在我的 module.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);
},
),
);
}
我使用它们将我的数据库表映射到一个对象。从我项目的主要 classes,我可以毫无问题地访问它们。看看我的项目文件树:
例如,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。到目前为止,一切都很好。然后我决定添加一个新的class,调用Auth.
这个 class 与 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;
}
}
这是授权:
<?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 相同的路径,但是,在这种情况下,当我执行 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 class 上工作,但我可以在 abstractResource 上工作?
那是因为 ServiceLocator
是通过一种叫做 'setter injection' 的机制注入的。为此,某些东西(例如 ServiceManager
)需要为 class 调用 setter,在本例中为 setServiceLocator
。当您直接实例化 Auth
时,情况并非如此。您需要将 class 添加到服务定位器,例如作为可调用服务。
例如:
public function getServiceConfig()
{
return array(
'invokables' => array(
'Auth' => '\Marketplace\V1\Functions\Auth',
),
);
}
或者,更恰当地,因为它不为工厂使用匿名函数,将其放入模块配置文件 `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 将自身的一个实例传递给它。有趣的事实:控制器本身以相同的方式注入服务定位器的实例(它是 this class implementing the very same interface). Another fun fact: that behaviour may change in future as discussed here.
的祖先
我在我的 module.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);
},
),
);
}
我使用它们将我的数据库表映射到一个对象。从我项目的主要 classes,我可以毫无问题地访问它们。看看我的项目文件树:
例如,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。到目前为止,一切都很好。然后我决定添加一个新的class,调用Auth.
这个 class 与 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;
}
}
这是授权:
<?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 相同的路径,但是,在这种情况下,当我执行 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 class 上工作,但我可以在 abstractResource 上工作?
那是因为 ServiceLocator
是通过一种叫做 'setter injection' 的机制注入的。为此,某些东西(例如 ServiceManager
)需要为 class 调用 setter,在本例中为 setServiceLocator
。当您直接实例化 Auth
时,情况并非如此。您需要将 class 添加到服务定位器,例如作为可调用服务。
例如:
public function getServiceConfig()
{
return array(
'invokables' => array(
'Auth' => '\Marketplace\V1\Functions\Auth',
),
);
}
或者,更恰当地,因为它不为工厂使用匿名函数,将其放入模块配置文件 `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 将自身的一个实例传递给它。有趣的事实:控制器本身以相同的方式注入服务定位器的实例(它是 this class implementing the very same interface). Another fun fact: that behaviour may change in future as discussed here.