PHP领域模型

标签 php oop domain-driven-design domain-model object-oriented-database

我已经使用 PHP 编程多年,并且在过去采用了我自己的方法来处理我的应用程序中的数据。

我过去构建了自己的 MVC,对 php 中的 OOP 有一定的了解,但我知道我的实现需要一些认真的工作。

过去我在模型和数据库表之间使用 is-a 关系。经过一些研究,我现在知道这并不是最好的前进方式。 据我所知,我应该创建不真正关心底层数据库(或要使用的任何存储机制)但只关心它们的行为和数据的模型。

由此我确定我可以创建模型,例如一个人 this person 对象可能有一些 Children(人类 child ),它们也是 Person 对象,保存在数组中(使用 addPerson 和 removePerson 方法,接受 Person 对象)。

然后我可以创建一个 PersonMapper,我可以用它来获取具有特定“id”的 Person,或保存一个 Person。

然后这可以在查找表中查找关系数据并为已请求的人(如果有的话)创建关联的子对象,同样在保存命令中将数据保存在查找表中。

这正在插入我的知识极限......

如果我想为具有不同楼层和这些楼层中的不同房间的建筑物建模怎么办?如果我想在这些房间里放置一些元素怎么办?

我会为建筑、关卡、房间和元素创建一个类吗

具有以下结构。

建筑物可以有 1 个或多个级别对象保存在一个数组中 级别可以有 1 个或多个房间对象保存在数组中 房间可以有一个或多个项目对象保存在一个数组中

每个类的映射器和更高级别的映射器使用子映射器来填充数组(根据顶级对象的请求或延迟加载请求)

这似乎将不同的对象紧密地耦合在一起,尽管是在一个方向上(即楼层不需要在建筑物中,但建筑物可以有层次)

这是处理事情的正确方法吗?

在 View 中,我想显示一个带有选择级别的选项的建筑物,然后显示带有选择房间等选项的级别。但我可能还想显示树状项目结构建筑物以及他们所在的楼层和房间。

我希望这是有道理的。当 oop 的一般概念似乎是将事物分开时,我只是在努力解决相互嵌套对象的概念。

如果有人能提供帮助,那将非常有用。

最佳答案

假设您像这样组织对象: enter image description here

为了初始化整个建筑对象(包括层、房间、项目),您必须提供数据库层类来完成这项工作。获取建筑物 TreeView 所需的一切的一种方法是:

(缩放浏览器以获得更好的 View )

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

}

现在假设我们有这个数据库模式

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

}

关于PHP领域模型,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12373189/

相关文章:

java - 如何使用instanceof根据子类类型对子类做不同的事情?

android - 为什么 LiveData 有一个单独的 MutableLiveData 子类?

c++ - 从构造函数传递给成员函数时,私有(private)成员变量为空

domain-driven-design - 打破应用到有界上下文

design-patterns - 使用 CQRS 在单个命令处理程序中保留多个内容

PHP 三元运算符不适用于此代码

php - 将删除的记录移动到另一个表

php - Display flex 导致文本被拆分

php - 如何根据变量的值对列表进行排序? (php)

c# - 访问者模式可以带额外的参数吗