11

我已經使用PHP進行了數年的編程,並且在過去採用了我自己的方法來處理我的應用程序中的數據。PHP域模型

我建立我自己的MVC過去,並有PHP內OOP有一定的瞭解,但我知道我的實現需要一些嚴肅的工作。

在過去,我已經使用了is-a的一個模型與一個數據庫表之間的關係。我現在知道,在做了一些研究之後,這不是真正的最佳前進方向。 據我瞭解,我應該創建模型,並不真正關心底層數據庫(或任何存儲機制將被使用),但只關心他們的行爲和他們的數據。

從這我已經確定,我可以創建模型讓我們說,例如一個人 這個人對象可能有一些兒童(人類的孩子)也是Person對象保存在一個數組(使用addPerson和removePerson方法,接受一個Person對象)。

然後我可以創造我可以用它來得到一個人與一個特定的「身份證」,或保存一個人PersonMapper。

這可能然後查找關係的數據中查找表,並創建已請求的人相關聯的子對象(如果有的話),並同樣在查找表節省save命令的數據。

現在這是挑戰極限,以我的知識.....

如果我想與這些級別中的不同層次和不同的房間建築模型?如果我想在這些房間放置一些物品怎麼辦?

我會創造建設,水平,房間和物品

用以下結構的一類。

建築物可以具有以陣列 水平保持可以具有1或多個室中的陣列 室對象保持1個或許多級對象可以有1個或以陣列保持每個

和映射器的許多項目對象類使用更高級別的映射器使用子映射器來填充數組(可以根據頂層對象的請求或請求的延遲加載)

這似乎將不同的對象緊緊耦合在一個方向上(即,不需要在建築物中,但建築物可以有水平)

這是關於事情的正確方法嗎?

在視圖內,我想顯示一個建築物,可以選擇一個等級,然後用選項選擇一個房間等來顯示等級。但是我也可能想顯示一個樹狀結構的項目建設和什麼級別和房間,他們都在。

我希望這是有道理的。當oop的一般概念似乎是將事物分開時,我只是在相互嵌套對象的概念上掙扎。

如果有人能幫上忙,那真的很有用。

+0

嵌套聽起來沒錯。您可以嘗試在[程序員網站]上詢問同樣的問題(http://programmers.stackexchange.com) – Malachi

+0

請勿在您的帖子上簽名 – Malachi

+0

對簽署帖子感到抱歉,以後不會這樣做。 –

回答

0

如果我明白你的問題的權利,你的主要問題是,你不使用抽象類正常。基本上,對於每個建築物,關卡,房間等,應該有不同的類。例如,您應該有一個抽象類Building,一個由Building等擴展的抽象類Levels取決於您想要的具體內容,以及就像你有一棵樹 - > level-> room,但它更像是一個雙鏈表,因爲每個建築物都有一個層次對象的數組,每個層次都有一個父對象。你也應該使用界面,因爲很多人都會忽視它們,他們將在未來幫助你和你的團隊。

關於以更通用的方式構建模型,在我看來,最好的方法是爲每種類型的數據庫或其他存儲方法實現相同的方法。例如,你有一個mongo數據庫和一個mysql數據庫,你將爲每個數據庫都有一個類,並且它們會有添加,刪除,更新,推送等方法。爲了確保你不會犯任何錯誤,一切都會正確地工作最好的方法是創建一個存儲方法的接口數據庫,並且最終不會在未定義mysql方法的地方使用mongo方法。如果有通用方法,您也可以定義一個抽象類。希望這會有所幫助,歡呼!

+0

http://en.wikipedia.org/wiki/Composition_over_inheritance –

3

現在這是挑戰極限,以我的知識.....

建築/層/間/項目你描述效果不錯我的結構。領域驅動設計就是理解你的領域,然後將這些概念建模爲對象 - 如果你能用簡單的語言描述你想要的東西,那麼你已經完成了你的任務。當你設計你的域名時,將所有其他內容(例如持久化)放在圖片之外,並且跟蹤事情變得更加簡單。

這似乎緊密地結合不同的對象雖然是在一個方向

沒有什麼錯了。真實世界中的建築物確實有地板,房間等,你只是對這個事實建模。

和映射器使用子映射器

在DDD術語與更高級別映射器每個類,這些「映射器」被稱爲「庫」。此外,如果您擁有其中的所有樓層/房間/物品,並且如果沒有建築物自行加載Room,則可能將其視爲「聚合」。在這種情況下,您只需要一個可加載整個建築樹的BuildingRepository。如果你使用任何現代的ORM庫,它應該自動爲你做所有的映射工作(包括加載子對象)。

+0

一個小小的更正,Aggregate是帶有地板和所有的建築物。建築本身至少在某些情況下是Aggreagate Root(「主要」對象,它將像整個聚合體的外觀一樣)。此外,存儲庫有一些映射器,但不在ORM意義上。存儲庫可能使用一個或多個ORM。順便說一下,ORM的實體不是域實體,它們是持久性對象。 – MikeSW

9

比方說,你組織你的對象,像這樣: enter image description here

爲了初始化整個建築物體(與水平,房間,物品),你必須提供DB層的類來完成這項工作。你需要建設的樹視圖獲取一切的一個方法是:

縮放瀏覽器更好的視野

zoom for better view

大廈將自身初始化取決於映射器相應的數據作爲參數提供給initializeById方法。這種方法也可以在初始化關卡和房間時使用。 (注:初始化整個建築將導致大量的數據庫查詢的時候重用那些initializeById方法,所以我用了一點效果索引伎倆和SQL IN opetator)

class RoomMapper implements RoomMapperInterface { 

    public function fetchByLevelIds(array $levelIds) { 
     foreach ($levelIds as $levelId) { 
      $indexedRooms[$levelId] = array(); 
     } 

     //SELECT FROM room WHERE level_id IN (comma separated $levelIds) 
     // ... 
     //$roomsData = fetchAll(); 

     foreach ($roomsData as $roomData) { 
      $indexedRooms[$roomData['level_id']][] = $roomData; 
     } 

     return $indexedRooms; 
    } 

} 

現在讓我們說我們有這個DB模式

enter image description here

最後一些代碼。

建設

class Building implements BuildingInterface { 

    /** 
    * @var int 
    */ 
    private $id; 

    /** 
    * @var string 
    */ 
    private $name; 

    /** 
    * @var LevelInterface[] 
    */ 
    private $levels = array(); 

    private function setData(array $data) { 
     $this->id = $data['id']; 
     $this->name = $data['name']; 
    } 

    public function __construct(array $data = NULL) { 
     if (NULL !== $data) { 
      $this->setData($data); 
     } 
    } 

    public function addLevel(LevelInterface $level) { 
     $this->levels[$level->getId()] = $level; 
    } 

    /** 
    * Initializes building data from the database. 
    * If all mappers are provided all data about levels, rooms and items 
    * will be initialized 
    * 
    * @param BuildingMapperInterface $buildingMapper 
    * @param LevelMapperInterface $levelMapper 
    * @param RoomMapperInterface $roomMapper 
    * @param ItemMapperInterface $itemMapper 
    */ 
    public function initializeById(BuildingMapperInterface $buildingMapper, 
      LevelMapperInterface $levelMapper = NULL, 
      RoomMapperInterface $roomMapper = NULL, 
      ItemMapperInterface $itemMapper = NULL) { 

     $buildingData = $buildingMapper->fetchById($this->id); 

     $this->setData($buildingData); 

     if (NULL !== $levelMapper) { 
      //level mapper provided, fetching bulding levels data 
      $levelsData = $levelMapper->fetchByBuildingId($this->id); 

      //indexing levels by id 
      foreach ($levelsData as $levelData) { 
       $levels[$levelData['id']] = new Level($levelData); 
      } 

      //fetching room data for each level in the building 
      if (NULL !== $roomMapper) { 
       $levelIds = array_keys($levels); 

       if (!empty($levelIds)) { 
        /** 
        * mapper will return an array level rooms 
        * indexed by levelId 
        * array($levelId => array($room1Data, $room2Data, ...)) 
        */ 
        $indexedRooms = $roomMapper->fetchByLevelIds($levelIds); 

        $rooms = array(); 

        foreach ($indexedRooms as $levelId => $levelRooms) { 
         //looping through rooms, key is level id 
         foreach ($levelRooms as $levelRoomData) { 
          $newRoom = new Room($levelRoomData); 

          //parent level easy to find 
          $levels[$levelId]->addRoom($newRoom); 

          //keeping track of all the rooms fetched 
          //for easier association if item mapper provided 
          $rooms[$newRoom->getId()] = $newRoom; 
         } 
        } 

        if (NULL !== $itemMapper) { 
         $roomIds = array_keys($rooms); 
         $indexedItems = $itemMapper->fetchByRoomIds($roomIds); 

         foreach ($indexedItems as $roomId => $roomItems) { 
          foreach ($roomItems as $roomItemData) { 
           $newItem = new Item($roomItemData); 
           $rooms[$roomId]->addItem($newItem); 
          } 
         } 
        } 
       } 
      } 

      $this->levels = $levels; 
     } 
    } 

} 

級別

class Level implements LevelInterface { 

    private $id; 
    private $buildingId; 
    private $number; 

    /** 
    * @var RoomInterface[] 
    */ 
    private $rooms; 

    private function setData(array $data) { 
     $this->id = $data['id']; 
     $this->buildingId = $data['building_id']; 
     $this->number = $data['number']; 
    } 

    public function __construct(array $data = NULL) { 
     if (NULL !== $data) { 
      $this->setData($data); 
     } 
    } 

    public function getId() { 
     return $this->id; 
    } 

    public function addRoom(RoomInterface $room) { 
     $this->rooms[$room->getId()] = $room; 
    } 

} 

class Room implements RoomInterface { 

    private $id; 
    private $levelId; 
    private $number; 

    /** 
    * Items in this room 
    * @var ItemInterface[] 
    */ 
    private $items; 

    private function setData(array $roomData) { 
     $this->id = $roomData['id']; 
     $this->levelId = $roomData['level_id']; 
     $this->number = $roomData['number']; 
    } 

    private function getData() { 
     return array(
      'level_id' => $this->levelId, 
      'number' => $this->number 
     ); 
    } 

    public function __construct(array $data = NULL) { 
     if (NULL !== $data) { 
      $this->setData($data); 
     } 
    } 

    public function getId() { 
     return $this->id; 
    } 

    public function addItem(ItemInterface $item) { 
     $this->items[$item->getId()] = $item; 
    } 

    /** 
    * Saves room in the databse, will do an update if room has an id 
    * @param RoomMapperInterface $roomMapper 
    */ 
    public function save(RoomMapperInterface $roomMapper) { 
     if (NULL === $this->id) { 
      //insert 
      $roomMapper->insert($this->getData()); 
     } else { 
      //update 
      $where['id'] = $this->id; 
      $roomMapper->update($this->getData(), $where); 
     } 
    } 

} 

項目

class Item implements ItemInterface { 

    private $id; 
    private $roomId; 
    private $name; 

    private function setData(array $data) { 
     $this->id = $data['id']; 
     $this->roomId = $data['room_id']; 
     $this->name = $data['name']; 
    } 

    public function __construct(array $data = NULL) { 
     if (NULL !== $data) { 
      $this->setData($data); 
     } 
    } 

    /** 
    * Returns room id (needed for indexing) 
    * @return int 
    */ 
    public function getId() { 
     return $this->id; 
    } 

}