PHP域模型

Cal*_*mer 11 php oop domain-driven-design domain-model object-oriented-database

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

我过去已经构建了自己的MVC,并且在php中对OOP有了合理的理解,但我知道我的实现需要一些认真的工作.

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

从此我已经确定我可以创建模型,例如一个人,这个人对象可以有一些子(人类孩子)也是一个数组中的Person对象(使用addPerson和removePerson方法,接受一个Person对象) .

然后我可以创建一个PersonMapper,我可以使用它来获取具有特定"id"的Person,或者保存Person.

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

这正在推动我的知识极限......

如果我想在这些级别内建造具有不同级别和不同房间的建筑物,该怎么办?如果我想在这些房间放置一些物品怎么办?

我会为建筑,水平,房间和项目创建一个课程

具有以下结构.

建筑物可以有一个或多个水平对象保持在一个数组级别可以有一个或多个房间对象保持在阵列室可以有1个或多个项目对象保持在一个数组

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

这似乎紧紧地耦合了不同的物体,尽管是在一个方向上(即地板不需要在建筑物中,但建筑物可以有水平)

这是正确的做事方式吗?

在视图中我想要显示一个建筑物,可以选择一个水平,然后显示水平,选择一个房间等选项..但我可能还想显示一个像建筑物中的项目结构的树和什么他们所处的水平和空间.

我希望这是有道理的.当oop的一般概念似乎是分开的东西时,我正在努力与彼此嵌套对象的概念斗争.

如果有人可以提供帮助,那将非常有用.

Wel*_*erz 8

假设你像这样整理你的对象: 在此输入图像描述

为了初始化整个建筑对象(包含关卡,房间,项目),您必须提供数据库层类来完成工作.获取建筑物树视图所需的一切方法是:

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

放大以获得更好的视图

构建将使用适当的数据初始化自身,具体取决于作为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;
    }

}
Run Code Online (Sandbox Code Playgroud)

现在让我们说我们有这个数据库架构

在此输入图像描述

最后一些代码.

建造

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

}
Run Code Online (Sandbox Code Playgroud)

水平

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

}
Run Code Online (Sandbox Code Playgroud)

房间

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

}
Run Code Online (Sandbox Code Playgroud)

项目

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

}
Run Code Online (Sandbox Code Playgroud)


小智 0

如果我正确理解你的问题,你的主要问题是你没有正确使用抽象类。基本上,您应该为每个建筑物、楼层、房间等设置不同的类。例如,您应该有一个抽象类 Building,一个由 Building 等扩展的抽象类 Levels,具体取决于您想要的内容,以及就像你有一个树建筑->级别->房间,但它更像是一个双链表,因为每个建筑物都有一个级别对象数组,每个级别都有一个父级建筑对象。您还应该使用界面,因为很多人都会忽略它们,它们将来会对您和您的团队有很大帮助。

关于以更通用的方式构建模型,我认为最好的方法是拥有一个类,为您使用的每种类型的数据库或其他存储方法实现相同的方法。例如,你有一个 mongo 数据库和一个 mysql 数据库,你将为每个数据库都有一个类,它们将具有添加、删除、更新、推送等方法。为了确保你不会犯任何错误,一切都会正常工作最好的方法是拥有一个接口数据库来存储方法,并且您最终不会在未定义 mysql 方法的地方使用 mongo 方法。如果公共方法有的话,您还可以为它们定义一个抽象类。希望这会有所帮助,干杯!