6

我們最近開始改善組織,減少重複建設,除其他事項外,努力使用學說2.2,和Zend框架2的部分。今天,我開始討論實現服務層的想法,以充當我們的控制者和主體實體之間的中介。數據訪問和安全

目前,我國大多數邏輯駐留在控制器中。另外,我們使用動作助手來測試某些權限;然而,在實現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,另一個接受Zend \ Di注入的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; 
    } 
} 
  1. 這是一個合適的模式來使用我們試圖完成?
  2. 它是很好的做法,在服務層內的權限驗證張貼?
  3. 據我所知,鑑於邏輯仍然駐留在所述控制器,使該模型的靈活性在各種情況下(JSON,XML,HTML等)來使用。思考?

我很滿意迄今爲止的工作方式,但如果有人看到我們如何做這件事的任何缺點,請張貼您的想法。

+1

只是我的兩個認證便士。我不相信有一種正確的方式和一種錯誤的方式,但是,我開始將身份驗證放入服務層,但隨後將其移至我的控制器中。我的推理是服務層是我的內部API,我應該使用我的控制器層向全世界公開,因此它應該決定誰可以訪問什麼。另外,如果我想構建任何內部工具/腳本等,我不需要在它們中構建身份驗證來使用我的服務層。 – 2012-04-27 23:40:39

+0

小心不要混淆身份驗證和訪問控制。在將任何域類引入圖片之前,身份驗證可以(應該?)進入模塊。只有你建立了用戶身份。例如:if(!$ authService-> hasIdentity())在您的域服務模型中。 – dualmon 2012-11-10 19:07:58

+1

@JamieSutherland:我不同意。這些服務定義了業務邏輯;控制器是請求和適當業務邏輯之間的橋樑。例如,您只有一個服務來訂購產品,但您可能有多個控制器用於HTTP請求,API請求等等。如果您關心的是ACL請求特定(例如,通過HTTP,您可能期望用戶會話在哪裏,因爲您希望爲API請求提供密鑰),請概括一下您的ACL實現以實現此目的。 – moteutsch 2013-04-21 08:40:51

回答

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

} 

注意,我們只是擴大學說\ 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; 
     } 
    } 
+1

我強烈反對在存儲庫中放置訪問控制代碼。存儲庫不應該知道應用程序的業務邏輯。它應該只涉及檢索數據。業務邏輯適用於更高級的服務和類。 – moteutsch 2013-04-21 08:33:30

+0

我認爲從Doctrine實現中抽象存儲庫也是一個好主意。使用組合(默認的Doctrine存儲庫類)而不是繼承。這與編碼到界面相結合,使您可以輕鬆地在部分或全部存儲庫中交換數據源(我喜歡稱之爲「映射器」,以避免與Doctrine存儲庫混淆)。 – moteutsch 2013-04-21 08:36:20