在PHP OOP中处理数据库访问的正确方法

Com*_*fox 2 php database oop orm pdo

我需要一些PHP的数据库访问帮助.我正试图以OOP的方式做事,但我不确定我是否正朝着正确的方向前进.

假设我有一个Person类,例如:

class Person {
    private $id;
    private $firstname;
    private $lastname;
    // maybe some more member variables

    function __construct($id = NULL) {
        if(isset($id)) {
            $this->id = $id;
            $this->retrieve();
        }
    }

    // getters, setters and other member functions

    private function retrieve() {
        global $db;

        // Retrieve user from database
        $stmt = $db->prepare("SELECT firstname, lastname FROM users WHERE id = :id");
        $stmt->bindParam(":id", $this->id, PDO::PARAM_INT);
        $stmt->execute();
        $result = $stmt->fetch();

        $this->firstname = $result['firstname'];
        $this->lastname = $result['lastname'];
    }

    function insert() {
        global $db;

        // Insert object into database, or update if exists
        $stmt = $db->prepare("REPLACE INTO users (id, firstname, lastname) VALUES (:id, :firstname, :lastname)");
        $stmt->bindParam(":id", $this->id, PDO::PARAM_INT);
        $stmt->bindParam(":firstname", $this->firstname, PDO::PARAM_STR);
        $stmt->bindParam(":lastname", $this->lastname, PDO::PARAM_STR);
        $stmt->execute();
    }
}
Run Code Online (Sandbox Code Playgroud)

请注意,这只是我刚才编写的用于描述我的问题的示例,而不是我在应用程序中使用的实际代码.

现在我的第一个问题是:这是处理数据库交互的正确方法吗?我认为这是一个很好的方法,因为你可以实例化一个对象,操纵它,然后再次插入/更新它.

换句话说:在实例化/使用类的代码中,是否更好地处理类内部的数据库交互(如我的示例中)或外部的数据库交互?

我的第二个问题是关于更新可能修改或未修改的一大堆行.让我们说类Person有一个成员变量$ pets [],这是一个包含人拥有的所有宠物的数组.宠物存储在数据库中的单独表中,如下所示:

+---------+-------------+---------+
|  Field  |    Type     |   Key   |
+---------+-------------+---------+
| pet_id  | int(11)     | PRI     |
| user_id | int(11)     | MUL     |
| name    | varchar(25) |         |
+---------+-------------+---------+
Run Code Online (Sandbox Code Playgroud)

假设我在Person对象中修改了一些宠物.也许我添加或删除了一些宠物,也许我只更新了一些宠物的名字.

在这种情况下,更新整个人的最佳方式是什么,包括他们的宠物?假设一个人有50只宠物,即使其中只有一只宠物发生变化,我是否只更新它们?

我希望这很清楚;)

编辑:

更重要的是,我如何同时处理删除/插入?我目前的方法是在"编辑页面"上,我检索某个人(包括他们的宠物)并以表格显示/打印它们供用户编辑.然后,用户可以编辑宠物,添加新宠物或删除一些宠物.当用户单击"应用"按钮时,表单将POST回到PHP脚本.

我可以想到将这些更改更新到数据库中的唯一方法是删除数据库中当前列出的所有宠物,然后插入新的宠物集.但是有一些问题:首先,pet表中的所有行都被删除并重新插入每次编辑,其次,自动增量id每次都会因此而大幅跳跃.

我觉得我做错了什么.是不是让用户同时删除/添加宠物和修改现有宠物(我应该单独处理这些动作)吗?

hel*_*ert 5

您要完成的任务是称为对象关系映射(将对象映射到关系数据库中的表,反之亦然).已经写了整本书,但我会尽量简短介绍一下.

现在我的第一个问题是:这是处理数据库交互的正确方法吗?我认为这是一个很好的方法,因为你可以实例化一个对象,操纵它,然后再次插入/更新它.

这是一种有效的方法.但是,一般来说,您应该尝试遵守"关注点分离".在我看来,建模一个域实体(在你的情况下像一个人)并将这个对象存储在数据库中/加载是两个(可以说是三个)不同的问题,应该在不同的类中实现(再次,个人意见!).它使您的类的单元测试非常困难,并增加了很多复杂性.

关于ORM,随着时间的推移出现了几种设计模式.最突出的是:

  • Active Record基本上是您在问题中已经提出的方法; 它将数据和数据访问逻辑紧密地耦合在一个对象中.在我看来,这不是最好的方法,因为它违反了关注点,但可能最容易实现.

  • 网关映射器:尝试创建一个单独的类访问您的个人表(有点像PersonGateway这样的话,你的.Person类只包含数据和各种行为,你的PersonGateway各种插入/更新/删除方法的.映射器在其他hand可能是一个将通用数据库结果对象(例如PDO查询返回的行)转换为Person对象的类(在这种情况下,Person类不需要知道这样的mapper类的存在).

在这种情况下,更新整个人的最佳方式是什么,包括他们的宠物?假设一个人有50只宠物,即使其中只有一只宠物发生变化,我是否只更新它们?

再次,几种可能性.在任何情况下,您都应该将我们的Pet映射为单独的类.

  • 将宠物放在单独的表中,并为此类实现自己的数据访问逻辑(例如,使用上面提到的模式之一).然后,您可以随意单独更新,插入或删除它们.您可以跟踪父对象(人员)中添加/删除的宠物,然后在保留父对象时级联更新操作.

  • 嵌入宠物你的个人表内集合.根据此集合的平均大小以及您可能想要查询它们的方式,这可能不是一个好主意.

如果您的项目变得复杂,您可能还想看看为您解决这些问题的ORM框架(例如Doctrine ORM).