gpt4 book ai didi

php - 组成与继承。我的数据库交互库应该使用什么?

转载 作者:行者123 更新时间:2023-12-02 15:39:21 25 4
gpt4 key购买 nike

考虑用PHP编写的数据库交互模块,其中包含用于与数据库交互的类。我尚未开始编写类(class)代码,因此无法提供代码段。

每个数据库表将有一个类,如下所述。

用户-与用户表进行交互的类。该类包含诸如createUser,updateUser等功能。

位置-与locations表进行交互的类。该类包含诸如searchLocation,createLocation,updateLocation等功能。

另外,我正在考虑创建另一个类,如下所示:

DatabaseHelper :一个类,该类将具有表示与数据库的连接的成员。此类将包含用于执行SQL查询的较低级方法,例如executeQuery(query,parameters),executeUpdate(query,parameters)等。

在这一点上,我有两个选择可以在其他类中使用DatabaseHelper类:-

  • User和Locations类将扩展DatabaseHelper类,以便他们可以使用DatabaseHelper中继承的executeQuery和executeUpdate方法。在这种情况下,DatabaseHelper将确保在任何给定时间只有一个与数据库的连接实例。
  • 将通过创建用户和位置实例的容器类将DatabaseHelper类注入(inject)到用户和位置类中。在这种情况下,容器将确保在任何给定时间应用程序中只有一个DatabaseHelper实例。

  • 这是我很快想到的两种方法。我想知道采用哪种方法。这两种方法都可能不够好,在这种情况下,我想了解实现数据库交互模块可以使用的任何其他方法。

    编辑:

    请注意,Container类将包含类型为DatabaseHelper的静态成员。它将包含一个私有(private)的静态getDatabaseHelper()函数,该函数将返回一个现有的DatabaseHelper实例,或者如果不存在则创建一个新的DatabaseHelper实例,在这种情况下,它将填充DatabaseHelper中的连接对象。容器还将包含名为makeUser和makeLocation的静态方法,这些方法分别将DatabaseHelper注入(inject)到User和Locations中。

    阅读一些答案后,我意识到最初的问题已基本解决。但是仍然有疑问,在我接受如下的最终答案之前,需要澄清一下。

    当我有多个数据库要连接而不是一个数据库时该怎么办。 DatabaseHelper类如何将其合并,容器如何在User和Location对象中注入(inject)适当的数据库依赖关系?

    最佳答案

    让我们从上到下回答您的问题,看看我能在您所说的内容中添加些什么。

    There will be one class per database table as explained below.

    User - A class for interacting with the user table. The class contains functions such as createUser, updateUser, etc.

    Locations - A class for interacting with the locations table. The class contains functions >such as searchLocation, createLocation, updateLocation, etc.


    本质上,您必须在这里进行选择。您描述的方法称为 active record模式。对象本身知道如何以及在何处存储对象。对于与数据库交互以创建/读取/更新/删除的简单对象,此模式非常有用。
    如果数据库操作变得更加广泛且难以理解,那么使用数据映射器(例如 this implementation)通常是一个不错的选择。这是处理所有数据库交互的第二个对象,而对象本身(例如,用户或位置)仅处理特定于该对象的操作(例如,登录名或goToLocation)。如果您想机会存储对象,则只需创建一个新的数据映射器。您的对象甚至都不知道实现中有什么变化。这将强制执行 encapsulationseperation of concerns
    还有其他选择,但是这两种是实现数据库交互的最常用的方法。

    In addition, I am thinking of creating another class as follows: -

    DatabaseHelper : A class that will have a static member that represents the connection to the database. This class will contain the lower level methods for executing SQL queries such as executeQuery(query,parameters), executeUpdate(query,parameters) and so on.


    您在此处描述的内容听起来像 singleton。通常,这并不是一个很好的设计选择。您真的,真的确定永远不会再有第二个数据库吗?可能不是,所以您不应将自己局限于仅允许一个数据库连接的实现。您可以使用一些允许连接,断开连接,执行查询等方法的方法来更好地创建Database对象,而不是使用静态成员来创建DatabaseHelper。这样,如果您需要第二个连接,就可以重用它。

    At this point, I have two options to use the DatabaseHelper class in other classes : -

    1. The User and Locations class will extend the DatabaseHelper class so that they can use the inherited executeQuery and executeUpdate methods in DatabaseHelper. In this case, DatabaseHelper will ensure that there is only one instance of the connection to the database at any given time.
    2. The DatabaseHelper class will be injected in the User and Locations class through a Container class that will make User and Location instances. In this case, the Container will make sure that there is only one instance of DatabaseHelper in the application at any given time.

    These are the two approaches that quickly come to my mind. I want to know which approach to go with. It is possible that both these approaches are not good enough, in which case, I want to know any other approach that I can go with to implement the database interaction module.


    第一种选择实际上并不可行。如果阅读 description of inheritance,您将看到继承通常用于创建现有对象的子类型。用户不是DatabaseHelper的子类型,也不是位置。 MysqlDatabase将是数据库的子类型,或者Admin将是用户的子类型。我建议不要使用此选项,因为它没有遵循面向对象编程的最佳实践。
    第二种选择更好。如果选择使用 Activity 记录方法,则实际上应该将数据库注入(inject)到User和Location对象中。当然,这应该由处理所有此类交互的第三个对象来完成。您可能需要看一下 dependency injectioninversion of control
    否则,如果选择数据映射器方法,则应将数据库注入(inject)到数据映射器中。这样,仍然可以使用多个数据库,同时分离您所有的问题。
    有关 Activity 记录模式和数据映射器模式的更多信息,我建议您获取Martin Fowler的 Patterns of Enterprise Application Architecture书。它充满了这种类型的模式,还有更多!
    我希望这会有所帮助(并且很抱歉,如果其中有一些非常糟糕的英语句子,我不是母语人士!)。
    ==编辑==
    使用数据映射器模式的 Activity 记录模式还有助于测试您的代码(如Aurel所说)。如果将所有代码分开做一件事情,那么检查它是否确实在做一件事情会更容易。通过使用 PHPUnit(或其他测试框架)来检查您的代码是否正常运行,您可以确定每个代码单元中都没有错误。如果您混合考虑(例如,当您选择选项中的选项1时),这将变得非常困难。事情变得很困惑,您很快就会得到一大堆 spaghetti code
    ==编辑2 ==
    Activity 记录模式的一个示例(这是很懒惰的,不是真正 Activity 的):
    class Controller {
    public function main() {
    $database = new Database('host', 'username', 'password');
    $database->selectDatabase('database');

    $user = new User($database);
    $user->name = 'Test';

    $user->insert();

    $otherUser = new User($database, 5);
    $otherUser->delete();
    }
    }

    class Database {
    protected $connection = null;

    public function __construct($host, $username, $password) {
    // Connect to database and set $this->connection
    }

    public function selectDatabase($database) {
    // Set the database on the current connection
    }

    public function execute($query) {
    // Execute the given query
    }
    }

    class User {
    protected $database = null;

    protected $id = 0;
    protected $name = '';

    // Add database on creation and get the user with the given id
    public function __construct($database, $id = 0) {
    $this->database = $database;

    if ($id != 0) {
    $this->load($id);
    }
    }

    // Get the user with the given ID
    public function load($id) {
    $sql = 'SELECT * FROM users WHERE id = ' . $this->database->escape($id);
    $result = $this->database->execute($sql);

    $this->id = $result['id'];
    $this->name = $result['name'];
    }

    // Insert this user into the database
    public function insert() {
    $sql = 'INSERT INTO users (name) VALUES ("' . $this->database->escape($this->name) . '")';
    $this->database->execute($sql);
    }

    // Update this user
    public function update() {
    $sql = 'UPDATE users SET name = "' . $this->database->escape($this->name) . '" WHERE id = ' . $this->database->escape($this->id);
    $this->database->execute($sql);
    }

    // Delete this user
    public function delete() {
    $sql = 'DELETE FROM users WHERE id = ' . $this->database->escape($this->id);
    $this->database->execute($sql);
    }

    // Other method of this user
    public function login() {}
    public function logout() {}
    }
    还有一个数据映射器模式的示例:
    class Controller {
    public function main() {
    $database = new Database('host', 'username', 'password');
    $database->selectDatabase('database');

    $userMapper = new UserMapper($database);

    $user = $userMapper->get(0);
    $user->name = 'Test';
    $userMapper->insert($user);

    $otherUser = UserMapper(5);
    $userMapper->delete($otherUser);
    }
    }

    class Database {
    protected $connection = null;

    public function __construct($host, $username, $password) {
    // Connect to database and set $this->connection
    }

    public function selectDatabase($database) {
    // Set the database on the current connection
    }

    public function execute($query) {
    // Execute the given query
    }
    }

    class UserMapper {
    protected $database = null;

    // Add database on creation
    public function __construct($database) {
    $this->database = $database;
    }

    // Get the user with the given ID
    public function get($id) {
    $user = new User();

    if ($id != 0) {
    $sql = 'SELECT * FROM users WHERE id = ' . $this->database->escape($id);
    $result = $this->database->execute($sql);

    $user->id = $result['id'];
    $user->name = $result['name'];
    }

    return $user;
    }

    // Insert the given user
    public function insert($user) {
    $sql = 'INSERT INTO users (name) VALUES ("' . $this->database->escape($user->name) . '")';
    $this->database->execute($sql);
    }

    // Update the given user
    public function update($user) {
    $sql = 'UPDATE users SET name = "' . $this->database->escape($user->name) . '" WHERE id = ' . $this->database->escape($user->id);
    $this->database->execute($sql);
    }

    // Delete the given user
    public function delete($user) {
    $sql = 'DELETE FROM users WHERE id = ' . $this->database->escape($user->id);
    $this->database->execute($sql);
    }
    }

    class User {
    public $id = 0;
    public $name = '';

    // Other method of this user
    public function login() {}
    public function logout() {}
    }
    ==编辑3:由bot编辑后==

    Note that the Container class will contain a static member of type DatabaseHelper. It will contain a private static getDatabaseHelper() function that will return an existing DatabaseHelper instance or create a new DatabaseHelper instance if one does not exists in which case, it will populate the connection object in DatabaseHelper. The Container will also contain static methods called makeUser and makeLocation that will inject the DatabaseHelper into User and Locations respectively.

    After reading a few answers, I realize that the initial question has almost been answered. But there is still a doubt that needs to be clarified before I can accept the final answer which is as follows.

    What to do when I have multiple databases to connect to rather than a single database. How does the DatabaseHelper class incorporate this and how does the container inject appropriate database dependencies in the User and Location objects?


    我认为不需要任何静态属性,容器也不需要makeLocation方法的那些makeUser。让我们假设您有应用程序的某个入口点,在其中您将创建一个类,该类将控制应用程序中的所有流程。您似乎将其称为容器,而我更喜欢将其称为 Controller 。毕竟,它可以控制应用程序中发生的事情。
    $controller = new Controller();
    Controller 将必须知道它必须加载哪个数据库,以及是否有一个或多个数据库。例如,一个数据库包含用户数据,另一个数据库包含位置数据。如果给定了 Activity 记录User(上方)和类似的Location类,则 Controller 可能如下所示:
    class Controller {
    protected $databases = array();

    public function __construct() {
    $this->database['first_db'] = new Database('first_host', 'first_username', 'first_password');
    $this->database['first_db']->selectDatabase('first_database');

    $this->database['second_db'] = new Database('second_host', 'second_username', 'second_password');
    $this->database['second_db']->selectDatabase('second_database');
    }

    public function showUserAndLocation() {
    $user = new User($this->databases['first_database'], 3);
    $location = $user->getLocation($this->databases['second_database']);

    echo 'User ' . $user->name . ' is at location ' . $location->name;
    }

    public function showLocation() {
    $location = new Location($this->database['second_database'], 5);

    echo 'The location ' . $location->name . ' is ' . $location->description;
    }
    }
    将所有 echo 移至View类或其他东西可能会很好。如果您有多个 Controller 类,那么拥有一个可以创建所有数据库并将其推送到 Controller 中的不同入口点可能会有所收获。例如,您可以将其称为前端 Controller 或入口 Controller 。
    这是否会回答您提出的问题?

    关于php - 组成与继承。我的数据库交互库应该使用什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10992857/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com