我们最近开始使用Doctrine 2.2和部分Zend Framework 2来努力改善组织,减少重复等。今天,我开始提出实现服务层的想法,作为我们的控制器和Doctrine实体之间的中介。
现在,我们的大多数逻辑都驻留在控制器中。另外,我们使用动作助手来测试某些权限;然而,在实施Zend \ Di后我想出了一种新的方法。我开始创建特定于实体的服务模型,它使用Zend \ Di注入EntityManager实例,以及当前用户的权限。
控制器代码如下:
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,另一个是Entity \ UserAccess对象 - 由Zend \ Di注入。
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;
}
}
我对目前的工作方式感到满意,但如果有人发现我们如何做到这一点,请发表您的想法。
答案 0 :(得分:1)
我喜欢你在这里所做的事情,我认为你关注的分离是好的。我们正在尝试使用自定义存储库更进一步。因此,例如,标准模型/服务方法可能如下所示:
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;
}
}