11

He estado programando en PHP durante varios años y en el pasado adopté métodos propios para manejar datos dentro de mis aplicaciones.PHP Domain Model

He construido mi propio MVC en el pasado y tengo una comprensión razonable de OOP en php, pero sé que mi implementación necesita un trabajo serio.

En el pasado he usado una relación is-a entre un modelo y una tabla de base de datos. Ahora sé, después de hacer algunas investigaciones, que este no es realmente el mejor camino a seguir. Por lo que yo entiendo, debería crear modelos que realmente no se preocupen por la base de datos subyacente (o el mecanismo de almacenamiento que se vaya a utilizar), pero solo se preocupan por sus acciones y sus datos.

De esto he establecido que puedo crear modelos de digamos, por ejemplo, una persona y este objeto persona podría tener algunos niños (niños humanos) que también son objetos Person en una matriz (con métodos addPerson y removePerson, aceptando un objeto Persona).

Pude crear una PersonMapper que podría usar para obtener una Persona con una 'id' específica, o para salvar a una Persona.

Esto podría entonces buscar los datos de relación en una tabla de búsqueda y crear los objetos secundarios asociados para la persona que se ha solicitado (si hay alguno) y guardar los datos en la tabla de búsqueda en el comando guardar.

Esto está empujando los límites de mi conocimiento .....

Lo que si quería modelar un edificio con diferentes niveles y diferentes habitaciones dentro de esos niveles? ¿Qué sucede si quiero colocar algunos artículos en esas habitaciones?

¿Me cree una clase para la construcción, nivel, cuarto y elemento

con la estructura siguiente.

edificio puede tener 1 o muchos objetos nivel celebradas en un nivel de matriz puede tener 1 o muchos habitación objetos retenidos en una sala de array pueden tener 1 o muchos objetos de elemento mantenidos en una matriz

y mapeadores para cada clase con mapeadores de nivel superior que utilizan mapeadores hijo para rellenar las matrices (ya sea a pedido del objeto de nivel superior o de carga diferida a petición)

Esto parece acoplar estrechamente los diferentes objetos aunque en una dirección (es decir, un piso no necesita estar en un edificio pero un edificio puede tener niveles)

¿Es esta la manera correcta de hacer las cosas?

Dentro de la vista, quiero mostrar un edificio con la opción de seleccionar un nivel y luego mostrar el nivel con una opción para seleccionar una habitación, etc. pero también me gustaría mostrar una estructura de elementos en árbol el edificio y en qué nivel y habitación se encuentran.

Espero que esto tenga sentido. Simplemente estoy luchando con el concepto de anidar objetos uno dentro del otro cuando el concepto general de oop parece ser separar cosas.

Si alguien puede ayudar, sería realmente útil.

+0

the Nesting sounds right. Puede intentar hacer la misma pregunta en el [Sitio de programadores] (http://programmers.stackexchange.com) – Malachi

+0

No firme sus publicaciones por favor – Malachi

+0

Lo siento por la firma de publicaciones, no lo hará en el futuro. –

Respuesta

0

Si entiendo correctamente su pregunta, su problema principal es que no está utilizando correctamente las clases abstractas.Básicamente debe tener diferentes clases para cada uno de sus edificios, niveles, habitaciones, etc. Por ejemplo, debe tener un edificio de clase abstracta, una clase abstracta. Los niveles que se extienden por Edificio, etc. dependen de lo que quiere tener exactamente, y así tienes un tree building-> level-> room, pero es más como una lista de doble enlace porque cada edificio tiene una matriz de objetos de nivel y cada nivel tiene parent un objeto de construcción. También debe utilizar interfaces ya que muchas personas los ignoran y le ayudarán mucho a usted y a su equipo en el futuro.

En cuanto a la construcción de modelos de una manera más genérica, la mejor manera de hacerlo es, en mi opinión, tener una clase que implemente los mismos métodos para cada tipo de base de datos u otro método de tienda que utilice. Por ejemplo, tienes una base de datos mongo y una base de datos mysql, tendrás una clase para cada uno de ellos y tendrán métodos como agregar, eliminar, actualizar, presionar, etc. Para asegurarte de que no cometes ningún error y todo lo hará Trabajar adecuadamente. La mejor forma de hacerlo es tener una base de datos de interfaz que almacene los métodos y no terminará usando un método mongo en algún lugar donde el método mysql no está definido. También puede definir una clase abstracta para los métodos comunes si tienen alguno. Espero que esto sea útil, ¡salud!

+0

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

3

Esto está empujando los límites de mi conocimiento .....

El nivel de sala estructura del edificio///artículo que usted describe suena perfectamente bien para mí. El diseño impulsado por dominio se trata de entender su dominio y luego modelar los conceptos como objetos: si puede describir lo que quiere con palabras simples, ya ha realizado su tarea. Cuando diseñe su dominio, mantenga todo lo demás (como la persistencia) fuera de la imagen y será mucho más simple hacer un seguimiento de las cosas.

Esto parece fuertemente acoplar los diferentes objetos aunque en una dirección

No hay nada malo en eso. Los edificios en el mundo real tienen pisos, habitaciones, etc. y simplemente estás modelando este hecho.

y mapeadores para cada clase con mapeadores nivel más alto utilizando los mapeadores niño

En la terminología DDD, estos "mapeadores" se denominan "repositorios". Además, su objeto Building podría considerarse un "agregado" si posee todos los pisos/habitaciones/elementos dentro de él y si no tiene sentido cargar un Room por sí mismo sin el edificio. En ese caso, solo necesitaría un BuildingRepository que pueda cargar todo el árbol de construcción. Si usa cualquier biblioteca moderna de ORM, debería hacer todo el trabajo de mapeo automáticamente (incluso cargar objetos secundarios).

+0

Una pequeña corrección, el Agregado es el edificio con el piso y todo eso. El Edificio en sí mismo es, al menos para algunos contextos, la Raíz Aggreagate (el objeto 'principal' que actuará como una fachada para todo el agregado). Además, los repositorios son un poco de mapeadores, pero no en el sentido ORM. Un repositorio puede usar uno o más ORM. Por cierto, las entidades de ORM NO son las entidades de dominio, son objetos de persistencia. – MikeSW

9

Digamos a organizar sus objetos de esta manera: enter image description here

Para inicializar el objeto entero edificio (con niveles, habitaciones, objetos) que tiene que ofrecer clases de capa de db para hacer el trabajo. Una forma de ir a buscar todo lo necesario para la vista de árbol del edificio es:

(acercar el navegador para ver mejor)

zoom for better view

edificio se inicializará con los datos apropiados en función de los creadores de mapas proporcionado como argumentos para inicializar el método ById.Este enfoque también puede funcionar al inicializar niveles y habitaciones. (Nota: La reutilización de estos métodos initializeById al inicializar todo el edificio dará lugar a una gran cantidad de consultas de base de datos, por lo que se utiliza un pequeño truco resultados de indexación y SQL EN 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; 
    } 

} 

Ahora vamos a decir que tenemos este esquema db

enter image description here

Y finalmente algunos códigos.

La construcción de

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

} 

Nivel

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

} 

habitación

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

} 

Artículo

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

}