PHP-модель домена

Я программировал на PHP несколько лет и в прошлом использовал свои собственные методы обработки данных в своих приложениях.

Я создал свой MVC в прошлом и имею разумное понимание ООП в php, но я знаю, что моя реализация требует некоторой серьезной работы.

В прошлом я использовал отношения is-a между моделью и таблицей базы данных. Теперь я знаю, после нескольких исследований, что это не лучший способ продвижения вперед. Насколько я понимаю, я должен создавать модели, которые действительно не заботятся о базовой базе данных (или о том, какой механизм хранения следует использовать), но только заботятся об их действиях и их данных.

Из этого я установил, что могу создавать модели let, например Person, у этого объекта-человека могут быть некоторые Дети (человеческие дети), которые также являются объектами Person, хранящимися в массиве (с помощью методов addPerson и removePerson, принимающих объект Person) ,

Затем я мог бы создать PersonMapper, который мог бы использовать для получения Лица с определенным «идентификатором» или для сохранения Лица.

Затем это может искать данные отношений в таблице поиска и создавать связанные дочерние объекты для запрашиваемого лица (если они есть), а также сохранять данные в таблице поиска в команде сохранения.

Это сейчас подталкивает ограничения к моим знаниям …..

Что, если бы я хотел смоделировать здание с разными уровнями и разными комнатами на этих уровнях? Что, если я захочу разместить некоторые предметы в этих комнатах?

Создать класс для построения, уровня, комнаты и предмета

со следующей структурой.

у здания может быть 1 или много объектов уровня, удерживаемых на уровне массива, может быть 1 или много предметов помещения, хранящихся в комнате с массивом, может иметь 1 или несколько объектов объектов, находящихся в массиве

и mappers для каждого класса с более высокоуровневыми картами, использующими дочерние мапперы для заполнения массивов (по запросу объекта верхнего уровня или ленивой загрузки по запросу)

Это, кажется, тесно соединяет разные объекты, хотя и в одном направлении (т. Е. Пол не должен находиться в здании, но здание может иметь уровни)

Это правильный способ заниматься вещами?

В представлении я хочу показать здание с возможностью выбора уровня, а затем показать уровень с возможностью выбора комнаты и т. Д., Но я также могу показать дерево как структуру элементов в здании и что уровня и комнаты, в которой они находятся.

Я надеюсь в этом есть смысл. Я просто борюсь с концепцией вложенных объектов друг в друга, когда общая концепция oop, кажется, состоит в том, чтобы разделить вещи.

Если кто-то может помочь, это будет действительно полезно.

Предположим, вы организовываете свои объекты так: введите описание изображения здесь

Чтобы инициализировать весь объект здания (с уровнями, комнатами, элементами), вам необходимо предоставить классы уровня db для выполнения задания. Один из способов получения всего, что вам нужно для древовидной структуры здания:

( увеличьте браузер для лучшего просмотра )

увеличить для лучшего обзора

Здание будет инициализироваться соответствующими данными в зависимости от параметров, предоставленных в качестве аргументов для метода initializeById. Этот подход также может работать при инициализации уровней и комнат. (Примечание. Повторное использование этих методов initializeById при инициализации всего здания приведет к большому количеству запросов db, поэтому я использовал небольшой трюк для индексирования результатов и opetator SQL IN)

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

введите описание изображения здесь

И, наконец, некоторый код.

Здание

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

Это сейчас подталкивает ограничения к моим знаниям …..

Структура здания / уровня / комнаты / предмета, которую вы описали, звучит отлично для меня. Дизайн, основанный на домене, – это понимание вашего домена, а затем моделирование концепций как объектов. Если вы можете описать, что хотите, простыми словами, вы уже выполнили свою задачу. Когда вы разрабатываете свой домен, держите все остальное (например, постоянство) вне изображения, и будет намного проще отслеживать все.

Это, похоже, тесно связано с разными объектами, хотя и в одном направлении

В этом нет ничего плохого. Здания в реальном мире имеют полы, комнаты и т. Д., И вы просто моделируете этот факт.

и картографы для каждого класса с более высокоуровневыми картами с использованием дочерних счетчиков

В терминологии DDD эти «mappers» называются «репозиториями». Кроме того, ваш объект Building можно рассматривать как «совокупность», если ему принадлежат все этажи / комнаты / предметы внутри него, и если нет смысла загружать Room самостоятельно, без здания. В этом случае вам понадобится только один BuildingRepository который может загрузить все дерево зданий. Если вы используете любую современную библиотеку ORM, она должна автоматически выполнять всю работу по составлению карт (включая загрузку дочерних объектов).

Если я правильно понимаю ваш вопрос, ваша основная проблема заключается в том, что вы не используете абстрактные классы должным образом. В принципе, у вас должны быть разные классы для каждого вашего здания, уровней, комнат и т. Д. Например, у вас должен быть абстрактный класс Building, абстрактный класс Levels, который расширяется Building и т. Д., Зависит от того, что вы хотите иметь точно, и например, у вас есть дерево building-> level-> room, но это больше похоже на двойной список, потому что каждое здание имеет массив объектов уровня, и каждый уровень имеет родительский объект здания. Вы также должны использовать интерфейсы, так как многие люди игнорируют их, и они помогут вам и вашей команде в будущем.

Что касается моделей зданий более общим способом, лучшим способом сделать это, на мой взгляд, является класс, который реализует те же методы для каждого типа базы данных или другого используемого метода хранилища. Например, у вас есть база данных mongo и база данных mysql, у вас будет класс для каждого из них, и у них будут такие методы, как добавление, удаление, обновление, push и т. Д. Чтобы быть уверенным, что вы не совершаете никаких ошибок, и все будет работайте правильно, лучший способ сделать это – иметь базу данных интерфейса, которая будет хранить методы, и вы не будете использовать метод mongo где-нибудь, где метод mysql не определен. Вы также можете определить абстрактный класс для общих методов, если они есть. Надеюсь, это будет полезно, ура!