11

저는 몇 년 동안 PHP로 프로그래밍 해 왔으며 과거에는 내 응용 프로그램 내에서 데이터를 처리하기 위해 내 방식을 채택했습니다.PHP 도메인 모델

나는 과거에 내 자신의 MVC를 구축했고 PHP 내에서 OOP에 대한 합리적인 이해를 가지고 있지만 구현에 심각한 문제가 있음을 알고 있습니다.

이전에는 모델과 데이터베이스 테이블 사이에 is-a 관계를 사용했습니다. 나는 이것이 실제로 최선의 방법이 아니라는 것을 약간의 연구를 한 후에 알았다. 필자가 이해하는 한, 기본 데이터베이스 (또는 사용되는 저장 메커니즘이 무엇이든)를 신경 쓰지 않고 자신의 행동과 데이터만을 신경 써야하는 모델을 만들어야합니다.

이 모델을 만들 수 있습니다. 예를 들어 Person 이 사람 객체는 배열에 보유 된 Person 객체 인 일부 Children (인간 자식)을 가질 수 있습니다 (addPerson 및 removePerson 메서드, Person 객체를 받아들임).

그러면 특정 'id'가있는 사람을 얻거나 Person을 저장하는 데 사용할 수있는 PersonMapper를 만들 수 있습니다.

그러면 조회 테이블에서 관계 데이터를 찾아 요청한 Person에 대한 관련 하위 오브젝트를 작성하고 save 명령의 조회 테이블에 데이터를 저장할 수 있습니다.

이 지금 내 지식의 한계를 추진하고있다 .....

내가 그 수준 내에서 서로 다른 수준의 다른 객실과 건물을 모델링하고 싶다면 무엇? 그 방에 물건을 넣고 싶으면 어떡하지?

나는 다음과 같은 구조의 건물, 수준, 룸 및 항목

에 대한 클래스를 만들겠습니까.

건물 1 또는 1을 가질 수 배열 방 유지 인쇄물 (1) 또는 다수의 공간을 가질 수있는 배열 레벨로 유지 많은 레벨 오브젝트 또는 어레이 각각

및 매퍼 개최 많은 항목 객체를 가질 수있다 상위 레벨 객체의 요청시 또는 요청시 지연로드 중 하나를 사용하여 자식 채우기를 사용하는 상위 수준 매퍼가있는 클래스

이것은 한 방향으로도 다른 객체를 단단히 연결하는 것처럼 보입니다 (예 : 건물에있을 필요는 없지만 건물에는 레벨이있을 수 있음)

이것은 올바른 방법인가요?

보기 내에서 레벨을 선택하고 방을 선택하는 옵션으로 레벨을 표시하는 건물을 보여주고 싶습니다.하지만 항목 구조와 같은 트리를 표시 할 수도 있습니다. 건물과 그들이 어느 수준과 방에 있는지에 관해 묻는 것.

나는 이것이 의미가 있기를 바란다. 나는 oop의 일반적인 개념이 사물을 분리하는 것처럼 보이면 서로 내에서 개체를 중첩하는 개념으로 고심하고있다.

누군가가 도움이된다면 정말 유용 할 것입니다.

+0

네 스팅 사운드가 올바르게 들립니다. [프로그래머 사이트] (http://programmers.stackexchange.com)에서 동일한 질문을 시도해보십시오. – Malachi

+0

게시물에 서명하지 마십시오. – Malachi

+0

게시물 서명에 대해 죄송합니다. 향후 서명하지 마십시오. –

답변

0

질문을 올바르게 이해하는 경우 주된 문제는 추상적 인 클래스를 올바르게 사용하지 않는다는 것입니다.기본적으로 건물, 레벨, 룸 등 각각에 대해 서로 다른 클래스를 가져야합니다. 예를 들어, 추상 클래스 Building, Building으로 확장되는 추상 클래스, 정확히 무엇을 갖고 싶은지에 달려 있어야합니다. 나무 건물 -> 레벨 -> 방을 가지고있는 것처럼, 각 건물에는 레벨 객체의 배열이 있고 각 레벨에는 부모가있는 건물 객체가 있기 때문에 이중 연결 목록과 비슷합니다. 또한 많은 사람들이 인터페이스를 무시해야 인터페이스를 사용해야하며, 앞으로 많은 사람들이 귀하와 귀하의 팀을 도울 것입니다.

좀 더 일반적인 방법으로 건물 모델을 작성하는 가장 좋은 방법은 내 생각에 데이터베이스에서 사용하는 각 유형의 데이터베이스 나 다른 저장 방법에 대해 동일한 방법을 구현하는 클래스를 만드는 것입니다. 예를 들어 mongo 데이터베이스와 mysql 데이터베이스를 가지고 있다면 각각에 대한 클래스가 생기고 add, remove, update, push 등과 같은 메소드가 생깁니다. 실수를하지 않으면 모든 것이 자동으로 실행됩니다. 이 작업을 수행하는 가장 좋은 방법은 메서드를 저장하는 인터페이스 데이터베이스를 사용하는 것입니다. 그러면 mysql 메서드가 정의되지 않은 어딘가에서 mongo 메서드를 사용하지 않게 될 것입니다. 공통 메소드에 추상 클래스가있는 경우이를 정의 할 수도 있습니다. 희망이 도움이 될 것입니다, 환호!

+0

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

3

이 지금 내 지식의 한계를 추진하고있다 .....

당신이 나에게 완벽하게 정상적으로 소리를 설명 건물/레벨/방/아이템 구조. 도메인 중심 디자인은 도메인을 이해하고 개념을 객체로 모델링하는 것입니다. 원하는 것을 간단하게 설명 할 수 있다면 이미 작업을 완료 한 것입니다. 도메인을 디자인 할 때는 그 밖의 모든 부분 (예 : 지속성)을 그림 밖으로 유지하면 훨씬 쉽게 추적 할 수 있습니다.

이 그것에 대해 아무 문제가 없다 한 방향으로

불구하고 단단 다른 객체를 보인다. 현실 세계의 건물에는 층, 방 등이 있으며 단순히이 사실을 모델링하는 것입니다. 아이 매퍼 DDD 용어에서

를 사용하여 높은 수준의 매퍼 각 클래스에 대한

및 맵퍼는이 "매퍼"는 "저장소"라고합니다. 또한 Building 개체는 그 안에있는 모든 층/회의실/항목을 소유하고 있고 건물이 없으면 Room을로드하는 것이 의미가없는 경우 "집계"로 간주 될 수 있습니다. 이 경우 전체 건물 트리를로드 할 수있는 BuildingRepository 하나만 있으면됩니다. 최신 ORM 라이브러리를 사용하면 자동으로 모든 매핑 작업을 수행해야합니다 (하위 객체로드 포함).

+0

하나의 작은 수정 인 Aggregate는 바닥과 모든 것을 갖춘 건물입니다. Building 자체는 적어도 일부 컨텍스트에서는 Aggreagate Root (전체 집계의 외관처럼 작동하는 '기본'객체)입니다. 또한 리포지토리는 약간의 매퍼이지만 ORM 의미는 아닙니다. 저장소는 하나 이상의 ORM을 사용할 수 있습니다. Btw, ORM의 엔티티는 도메인 엔티티가 아니며, 지속성 오브젝트입니다. – MikeSW

9

의 당신이 지금처럼 개체를 구성한다고 가정 해 봅시다 : 당신이 그 일을 할 수있는 DB 층 클래스를 제공해야합니다 (레벨, 객실, 항목) 건물 전체 개체를 초기화하기 위해 enter image description here

. 당신이 건물의 트리 뷰에 필요한 가져 오는 모든 것을 한 가지 방법은 다음과 같습니다

zoom for better view

가 가

건물의 적절한 데이터 매퍼에 따라 자신을 초기화합니다 (더보기의 브라우저를 줌) initializeById 메소드의 인수로 제공됩니다.이 접근법은 레벨과 방을 초기화 할 때도 사용할 수 있습니다. (참고 : DB 쿼리를 많이 발생합니다 건물 전체를 초기화 할 때 그 initializeById 방법을 인용, 그래서 나는 약간의 결과를 인덱싱 트릭과 opetator IN SQL 사용) 이제

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

} 

항목