PHP领域模型(PHP Domain Model)

2019-07-31 12:13发布

我在PHP编程了好几年,在过去通过我自己的方法,我的应用程序中处理数据。

我建立我自己的MVC过去,并有PHP内OOP有一定的了解,但我知道我的实现需要一些严肃的工作。

在过去,我已经使用的是-A模型和数据库表之间的关系。 我现在才知道做一些研究,这是不是真的取得进展的最佳方式了。 据我了解,我应该创建一个并不真正关心底层数据库模型(或任何存储机制被使用),但只关心他们的行动和他们的数据。

从这个我已经确定,我可以创建模型可以说,例如一个人的这个人的对象可能有一些儿童(人类小孩)这也是一个数组举行Person对象(与addPerson的和removePerson方法,接受一个Person对象) 。

然后,我可以创造我可以用它来得到一个人与一个特定的“身份证”,或保存一个人PersonMapper。

那么这可能查找的关系数据的查找表,并创建已请求人相关的子对象(如果有的话),并同样在查找表节省save命令的数据。

这是现在推限制我的知识.....

如果我想与这些级别中的不同层次和不同的房间建筑模型? 如果我想放在这些房间的一些项目?

我会给建设,水平,房间和物品类

具有以下结构。

建筑物可具有以阵列水平保持1个或许多级对象可以具有以阵列室保持1个或许多室对象可以有以阵列保持1个或许多项目对象

和用于与使用子映射器来填充阵列更高级别映射器每个类映射器(无论是在顶层对象或请求延迟加载的请求)

这似乎紧密地结合不同的对象虽然是在一个方向(即地板并不需要在一个建筑,但建筑能有水平)

这是去了解事情的正确方法?

在视图里,我想表现出建筑的选项来选择一个级别,然后显示水平的选项来选择一个房间等等。但我也可能想表明,像在建筑项目的结构,什么树水平和房间他们英寸

我希望这是有道理的。 我只是在彼此嵌套对象的概念挣扎时,OOP的基本概念似乎是不同的东西。

如果有人能够帮助这将是非常有用的。

Answer 1:

比方说,你组织你的对象,像这样:

为了初始化整个建筑物体(与水平,房间,物品),你必须提供DB层的类来完成这项工作。 获取你需要的建筑树视图是一切的一种方式:

缩放,以更好的视野浏览器

大厦将根据作为参数传递给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模式

最后一些代码。

建造

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

}


Answer 2:

这是现在推限制我的知识.....

建筑/层/间/项目结构,你描述的效果不错给我。 领域驱动设计是所有关于了解你的域,然后建模概念对象 - 如果你能描述你想要的东西简单的话,你已经完成你的任务。 当您在设计领域,保持一切(如持久性)出来的图片,它会变得更加简单明了的事情。

这似乎紧密地结合不同的对象虽然是在一个方向

没有什么错了。 在现实世界中的建筑确实有楼层,房间等,以及你只是模拟了这一事实。

并利用孩子映射器与更高级别映射器每个类映射器

在DDD术语,这些“映射器”被称为“库”。 此外,您的Building对象可能被认为是一个“聚合”,如果它拥有所有的楼层/间/内它的项目,如果它没有意义加载Room本身没有建设。 在这种情况下,你只需要一个BuildingRepository可以加载整个建筑树。 如果你使用任何现代ORM库,它会自动为你做所有的绘图工作(包括加载子对象)。



Answer 3:

如果我理解你的问题的权利,你的主要问题是,你不使用抽象类正常。 基本上你应该有不同的类别为您的每一个建筑,水平,客房等。例如,你应该有一个抽象类建筑,由建筑扩展性等抽象类水平,取决于你想拥有什么,并且就像你有一棵树building-> LEVEL->的房间,但它更像是一个双链表,因为每个建筑都有级别对象的数组,每个级别都有一个父建筑物对象。 您还应该使用接口,很多人忽略了他们,他们会帮助你和你的团队在未来很多。

对于一个更通用的方法建立模型做在我看来,最好的办法是有一个实现每种类型的数据库,或者使用其他存储方法的相同方法的类。 例如你有一个蒙戈数据库和MySQL数据库,你将有一个类为每个这些的时候,就会有像添加,删除,更新,推送等要确保你不做任何错误,一切都会方法正常工作,做到这一点,最好的办法是有将存储方法的接口数据库,并使用一个蒙戈方法的地方在那里没有定义MySQL的方法,你就不会结束。 您也可以定义一个抽象类的常用方法,如果他们有任何。 希望这会有所帮助,干杯!



文章来源: PHP Domain Model