2009-02-05 58 views
2

更新: 09/02/2009 - 修訂後的問題,提供了更好的示例,增加了賞金。封裝通用邏輯(域驅動設計,最佳實踐)


嗨,
我建設使用的數據庫和實體(域對象)之間的數據映射器模式的PHP應用程序。我的問題是:

封裝通常執行的任務的最佳方法是什麼?

例如,一個常見任務是從站點映射器中檢索一個或多個站點實體,以及從頁面映射器中檢索它們的關聯(主頁)實體。目前,我是這樣做的:

$siteMapper = new Site_Mapper(); 
$site = $siteMapper->findByid(1); 

$pageMapper = new Page_Mapper(); 
$site->addPage($pageMapper->findHome($site->getId())); 

現在,這是一個相當簡單的例子,但它能夠在現實中更復雜,因爲每個網站也有一個相關聯的場所,並在頁面實際上有多個版本(雖然爲了這個任務的目的,我只對最近的一個感興趣)。

我需要在我的應用程序中的多個位置執行此操作(獲取站點和相關主頁,區域設置等),並且我不能想到封裝此任務的最佳方式/位置,以至於我不必在各處重複。理想情況下,我想有這樣的事情結束了:

$someObject = new SomeClass(); 
$site = $someObject->someMethod(1); // or 
$sites = $someObject->someOtherMethod(); 

其中所得的網站實體已經有了相關的實體創建,並準備使用。

保存這些對象時發生同樣的問題。說我有一個網站的實體和相關主頁實體,他們已經都被修改,我必須做這樣的事情:

$siteMapper->save($site); 
$pageMapper->save($site->getHomePage()); 

同樣的,瑣碎的,但這個例子被簡化。代碼的重複仍然適用。

在我的腦海裏是有意義的有某種中央對象的,可以照顧:

  • 檢索網站(或網站)和所有nessessary相關實體
  • 創建新的新網站的實體相關實體
  • 以一個網站(或網站)並保存它和所有相關實體(如果他們已經改變)

所以,回到我的問題,我應該爲此對象?

  • 現有的mapper對象?
  • 東西基於存儲庫模式?*
  • 基於東西彭定康工作的單位嗎?*
  • 別的東西嗎?

*我不完全理解其中任意一個,因爲你可能已經猜到了。

有沒有一種標準的方法來解決這個問題,有人可以提供他們如何實現它的簡短描述?我不是在尋找任何人來提供一個完整的工作實現,只是理論。

感謝,
傑克

回答

2

使用儲存庫/服務模式,你的存儲庫的類將提供爲每個實體的一個簡單CRUD接口,那麼服務類將是執行像安裝實體的依賴關係的附加邏輯的附加層。然後,您的其他應用只能使用服務。你舉的例子可能是這樣的:

$site = $siteService->getSiteById(1); // or 
$sites = $siteService->getAllSites(); 

然後SiteService類中你會有這樣的事情:

function getSiteById($id) { 
    $site = $siteRepository->getSiteById($id); 
    foreach ($pageRepository->getPagesBySiteId($site->id) as $page) 
    { 
    $site->pages[] = $page; 
    } 
    return $site; 
} 

我不知道PHP是很好,所以請原諒,如果有語法錯誤的東西。

+0

好的,這是有道理的,需要閱讀服務模式。但是,(我已經遇到了這個問題),存儲庫和映射器之間有什麼區別。我的映射器已經提供了一個CRUD界面,爲什麼附加的倉庫層位於頂層? – 2009-02-09 20:07:26

+0

另外,您是否應該爲每種類型的實體提供服務,或者只是您想要做某些事情的服務? – 2009-02-09 20:08:48

0

我可能會通過提取共同任務某處一個輔助方法開始,然後等着看的設計要求是什麼。感覺現在講得還爲時過早。

你會說這個方法是什麼?該名稱通常暗示該方法所屬的位置。

+0

嗯,非常好的問題:-),我想我需要給這個更多的想法。 – 2009-02-06 09:59:17

1

[編輯:該行試圖解決的事實,這是經常容易編寫自定義代碼來直接與情況處理比它要儘量合身的問題轉化爲模式]

模式在概念上很好,但它們並不總是「映射」。經過多年高端PHP開發,我們已經以一種非常直接的方式處理這些問題。試想一下:

文件:Site.php

class Site 
{ 
    public static function Select($ID) 
    { 
     //Ensure current user has access to ID 
     //Lookup and return data 
    } 

    public static function Insert($aData) 
    { 
     //Validate $aData 
     //In the event of errors, raise a ValidationError($ErrorList) 

     //Do whatever it is you are doing 

     //Return new ID 
    } 

    public static function Update($ID, $aData) 
    { 
     //Validate $aData 
     //In the event of errors, raise a ValidationError($ErrorList) 

     //Update necessary fields 
    } 

然後,爲了把它(從任何地方),只需運行:

$aData = Site::Select(123); 

Site::Update(123, array('FirstName' => 'New First Name')); 

$ID = Site::Insert(array(...)) 

有一件事要記住關於面向對象編程和PHP ... PHP不會在請求之間保持「狀態」,所以創建一個對象實例只是爲了讓它立即銷燬通常沒有意義。

+0

這並沒有真正解決處理多個相關實體(創建新的,創建現有的和保存)的問題。 – 2009-02-09 19:02:01

0
class Page { 

    public $id, $title, $url; 

    public function __construct($id=false) { 
    $this->id = $id; 
    } 

    public function save() { 
    // ... 
    } 

} 

class Site { 

    public $id = ''; 
    public $pages = array(); 

    function __construct($id) { 
    $this->id = $id; 
    foreach ($this->getPages() as $page_id) { 
     $this->pages[] = new Page($page_id); 
    } 
    } 

    private function getPages() { 
    // ... 
    } 

    public function addPage($url) { 
    $page = ($this->pages[] = new Page()); 
    $page->url = $url; 
    return $page; 
    } 

    public function save() { 
    foreach ($this->pages as $page) { 
     $page->save(); 
    } 
    // .. 
    } 

} 

$site = new Site($id); 
$page = $site->addPage('/'); 
$page->title = 'Home'; 
$site->save(); 
+1

雖然這是一個優雅的解決方案,但根據數據映射器模式,這不適用於我的設置,因爲我的實體不知道映射器,所以它們沒有保存方法。將實體解析回映射器的保存方法以保存到數據庫。 – 2009-02-09 19:00:09

0

使您的Site對象爲Aggregate Root來封裝複雜關聯並確保一致性。

然後創建一個SiteRepository,負責檢索網站聚合並填充其子(包括所有網頁)。

您不需要單獨的PageRepository(假設您不會將頁面設置爲單獨的聚合根目錄),並且您的SiteRepository應該有責任檢索Page對象(在您的情況下,通過使用現有的Mappers) 。

所以:

$siteRepository = new SiteRepository($myDbConfig); 
$site = $siteRepository->findById(1); // will have Page children attached 

然後是findById方法將負責也發現本網站的所有頁面的孩子。這與CodeMonkey1給出的答案有相似的結構,但是我相信通過使用Aggregate和Repository模式,您將獲得更多的收益,而不是爲此任務創建特定的服務。網站聚合的任何其他檢索/查詢/更新,包括其任何子對象,都將通過相同的SiteRepository完成。

編輯:Here's a short DDD Guide來幫助你的術語,但我真的建議閱讀Evans如果你想要整個圖片。