我们最近开始使用 Doctrine 2.2 和 Zend Framework 2 的一部分,以努力改进组织、减少重复等。今天,我开始提出实现服务层的想法,以充当我们的 Controller 和 Doctrine 实体之间的中介。
现在,我们的大部分逻辑都驻留在 Controller 中。此外,我们使用 Action 助手来测试某些权限;然而,在实现 Zend\Di 之后,我想到了一种新方法。我开始创建特定于实体的服务模型,它使用 Zend\Di 注入(inject) EntityManager 实例和当前用户的权限。
Controller 代码如下:
class Project_DeleteController extends Webjawns_Controller_Action
{
public function init()
{
$this->_initJsonContext();
}
public function indexAction()
{
$response = $this->_getAjaxResponse();
$auditId = (int) $this->_getParam('audit_id');
if (!$auditId) {
throw new DomainException('Audit ID required');
}
/* @var $auditService Service\Audit */
$auditService = $this->getDependencyInjector()->get('Service\Audit');
try {
$auditService->delete($auditId);
$response->setStatusSuccess();
} catch (Webjawns\Exception\SecurityException $e) {
$this->_noAuth();
} catch (Webjawns\Exception\Exception $e) {
$response->setStatusFailure($e->getMessage());
}
$response->sendResponse();
}
}
以及我们服务层之一的示例。构造函数采用两个参数——一个采用 EntityManager,另一个采用由 Zend\Di 注入(inject)的 Entity\UserAccess 对象。
namespace Service;
use Webjawns\Service\Doctrine,
Webjawns\Exception;
class Audit extends AbstractService
{
public function delete($auditId)
{
// Only account admins can delete audits
if (\Webjawns_Acl::ROLE_ACCT_ADMIN != $this->getUserAccess()->getAccessRole()) {
throw new Exception\SecurityException('Only account administrators can delete audits');
}
$audit = $this->get($auditId);
if ($audit->getAuditStatus() !== \Entity\Audit::STATUS_IN_PROGRESS) {
throw new Exception\DomainException('Audits cannot be deleted once submitted for review');
}
$em = $this->getEntityManager();
$em->remove($audit);
$em->flush();
}
/**
* @param integer $auditId
* @return \Entity\Audit
*/
public function get($auditId)
{
/* @var $audit \Entity\Audit */
$audit = $this->getEntityManager()->find('Entity\Audit', $auditId);
if (null === $audit) {
throw new Exception\DomainException('Audit not found');
}
if ($audit->getAccount()->getAccountId() != $this->getUserAccess()->getAccount()->getAccountId()) {
throw new Exception\SecurityException('User and audit accounts do not match');
}
return $audit;
}
}
- 对于我们要实现的目标,这是一种合适的模式吗?
- 如发布的那样在服务层内进行权限验证是好的做法吗?
- 据我了解, View 逻辑仍然驻留在 Controller 中,使模型可以灵活地用于各种上下文(JSON、XML、HTML 等)。想法?
我对目前的工作方式很满意,但如果有人发现我们这样做的任何缺点,请发表您的想法。
最佳答案
我喜欢你在这里做的事情,我认为你的关注点分离很好。我们正在尝试更进一步,使用自定义存储库。因此,例如,标准模型/服务方法可能如下所示:
public function findAll($sort = null)
{
if (!$sort) $sort = array('name' => 'asc');
return $this->getEm()->getRepository('Application\Entity\PartType')
->findAll($sort);
}
...我们正在将需要 DQL 的内容添加到存储库中,以将所有 DQL 排除在模型之外,例如:
public function findAllProducts($sort = null)
{
if (!$sort) $sort = array('name' => 'asc');
return $this->getEm()->getRepository('Application\Entity\PartType')
->findAllProducts($sort);
}
对于上述模型,存储库类如下所示:
<?php
namespace Application\Repository;
use Application\Entity\PartType;
use Doctrine\ORM\EntityRepository;
class PartTypeRepository extends EntityRepository
{
public function findAllProducts($order=NULL)
{
return $this->_em->createQuery(
"SELECT p FROM Application\Entity\PartType p
WHERE p.productGroup IS NOT NULL
ORDER BY p.name"
)->getResult();
}
}
请注意,我们只是简单地扩展了 Doctrine\ORM\EntityRepository 这意味着我们不必重新定义所有标准的 Doctrine 存储库方法,但如果需要我们可以覆盖它们,并且我们可以添加我们自己的自定义方法.
所以关于访问控制,它使我们能够通过从存储库访问服务中的业务逻辑,在非常低的级别添加基于身份的约束或其他记录级条件。通过这种方式,服务不知道实现。只要我们严格不把 DQL 放在应用的其他部分,我们就可以对任何通过存储库访问数据库的类实现记录级的业务约束。 (注意应用程序更高级别中的自定义 DQL)。
例子:
public function findAll($order=NULL)
{
// assumes PHP 5.4 for trait to reduce boilerplate locator code
use authService;
if($this->hasIdentity()) {
return $this->_em->createQuery(
"SELECT p FROM Application\Entity\PartType p
JOIN p.assignments a
WHERE a.id = " . $this->getIdentity()->getId()
)->getResult();
} else {
return NULL;
}
}
关于php - 服务层的数据访问和安全(Doctrine & ZF),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10359144/