【问题标题】:Is this a good PHP OOP structure for a Database Implementation?这是用于数据库实现的良好 PHP OOP 结构吗?
【发布时间】:2013-06-02 17:52:37
【问题描述】:

我有一系列课程:

abstract class Database extends PDO {}
abstract class OracleDatabase extends Database {}
abstract class MySQLDatabase extends Database {}
abstract class MSSQLDatabase extends Database {}

然后,当我想要一个数据库连接实例时,我创建一个新类,该类扩展 OracleDatabaseMySQLDatabaseMSSQLDatabase,具体取决于数据库所在的位置...例如

class MyAppDatabase extends OracleDatabase {}

首先,这是一个好的设计吗?我读过最好使用接口而不是扩展抽象类,但我不确定如何在不重复代码的情况下做到这一点。

我确实意识到 PDO 的全部意义在于摆脱特定于 DB 的代码,但是对于诸如 DB 对象括号之类的事情,我仍然需要不同的功能(例如,在 Oracle 中,您使用双引号;在 MySQL 中,您使用反引号)、数据类型检测(user_tab_columns vs INFORMATION_SCHEMA)等

而且 - 如果我想创建一个名为 Updater 的类,它允许客户端为特定表创建一组多个“更新” - 对 SET 和 WHERE 子句使用相同的字段,但使用不同的值怎么办?我认为我无法从 Database 类继承,所以我是否可以将其设为“拥有”数据库对象的独立类?

class Updater {

    private $db;

    private $table;

    private $setFields;

    private $whereFields;

    private $updates;

    function __construct($db, $table, $setFields, $whereFields) {
        if (!($db instanceof Database)) {
            if (is_scalar($db)) {
                $type = gettype($db);
            } else {
                $type = get_class($db);
            }
            throw new Exception("Expected Database object; " . $type . " found.");
        }

        $this->db = $db;

        // ensure $table is a table in the database
        // ensure $setFields and $whereFields are columns in the table

        $this->table = $table;
        $this->setFields = $setFields;
        $this->whereFields = $whereFields;
        $this->updates = array();
    }


    function addUpdate($setValues, $whereValues)  {

        // ensure $setValues is an array and has the same cardinality as
        //     $this->setFields

        // ensure $whereValues is an array and has the same cardinality as
        //     $this->whereFields

        array_push($this->updates,
            array(
                'setValues'=>$setValues,
                'whereValues' => $whereValues
            )
        );
    }

    function doUpdate() {   // without error handling

        $escTable = $this->db->bracket($table);

        $setTemplate = array();

        foreach ($this->setFields as $setField) {
            $escField = $this->db->bracket($setField);
            $colonField = makeColonField($setField);   // :fieldName
            $setting = "$escField = $colonField";
            array_push($setTemplate, $setting);
        }

        $csvSetTemplate = implode(", ", $setTemplate);

        $whereTemplate = array();

        foreach ($this->whereFields as $whereField) {
            $escField = $this->db->bracket($whereField);
            $colonField = makeColonField($setField);   // :fieldName
            $setting = "$escField = $colonField";
            array_push($whereTemplate, $setting);
        }

        $andedWhereTemplates = implode(" AND ", $whereTemplate);

        $sql = "UPDATE $escTable SET $csvSetTemplate WHERE $andedWhereTemplates";

        $sth = $this->db->prepare($sql);

        foreach ($this->updates as $update) {

            $setValues   = $update['setValues'];
            $whereValues = $update['whereValues'];

            $params = array();
            for ($i=0; $i<count($setValues); $i++) {
                $setField = $this->setFields[$i];
                $setValue = $setValues[$i];
                $colonField = makeColonField($setField);
                $params[$colonField] = $setValue;
            }

            for ($i=0; $i<count($whereValues); $i++) {
                $whereField = $this->whereFields[$i];
                $whereValue = $whereValues[$i];
                $colonField = makeColonField($whereField);
                $params[$colonField] = $whereValue;
            }

            $sth->execute($params);
        }
    }
}

这是一个好的解决方案吗?

【问题讨论】:

  • 您真的需要同时使用所有这些实例吗?
  • 有必要将Oracle/MySQL/MSSQLDatabase声明为抽象吗?似乎它们可以是具体的,而不是声明一个新类来声明每个类的实例。
  • @YourCommonSense,不确定。我希望能够灵活地切换 DB 实现而不影响应用程序代码。
  • @Pudge601,我想它们可能是具体的。
  • PDO 不已经提供这种抽象级别了吗?

标签: php database oop pdo abstract


【解决方案1】:

首先,我建议在这种情况下抽象类更好,因为不同的RDBMS之间通常有很多共同点,你可以写一些通用的方法来更清楚地解决问题。

还有另一种方法:

class Database extends PDO {
    private $engine; //this is a DBEngine
}

interface DBEngine  
{
}

class MySQLEngine implements DBEngine
{

}

class MSSQLEngine implements DBEngine
{

}

...

在这种情况下,您可以使用接口,因为数据库中实现了常用的方法,而每个引擎只实现了RDBMS之间行为不同的方法。

这称为适配器模式,您可以在 CodeIgniter 的源代码中阅读一些相关信息:http://ellislab.com/codeigniter

而对于第二个问题,你可以在类Database中实现update方法。

换句话说,让“更新”类数据库的公共成员。

【讨论】:

  • 刚刚查看了codeigniter。看起来很棒。我可能会在我的下一个项目中使用它。 :)
【解决方案2】:

我更倾向于有一个数据库包装类,然后注入一个“提供者”(提供者是 MySQL、Oracle 或其他数据库引擎),然后通过你的包装类与您的数据库交互。

您将有一个引导文件来设置这些,然后将您新创建的数据库对象实例传递给您的应用程序。

这称为依赖注入。理论上,您可以轻松地将数据库引擎切换到另一个引擎,甚至可以注入测试数据库并使用相同的应用程序。

一个快速的、即兴的例子:

<?php
class Database
{    
    private $database;

    public function setProvider(DatabaseProvider $database)
    {
        $this->database = $database;
    }

    public function select($table, $fields = array(), $conditions = array(), $order = array())
    {
        $this->database->select($table, $fields, $conditions, $order);
    }

    public function insert($table, $values)
    {
        $this->database->insert($table, $values);
    }

    public function delete($table, $conditions = array())
    {
        $this->database->delete($table, $conditions);
    }
}

然后是一个示例提供者:

<?php
class MySQL implements DatabaseProvider
{
    public function __construct($config)
    {
        // create PDO instance with settings in $config
        $this->connection = new PDO();
    }

    public function select($table, $fields = array(), $conditions = array(), $order = array())
    {
        // build SELECT statement based on table, and other parameters passed
        // return result set
    }

    // and so on...
}

您的MySQL 实例将在您的引导文件中创建,您还可以在其中传递您的连接设置。

显然以上不是一个完整的示例,但应该足以让您开始充实自己的解决方案。

奖金

目录结构示例,深受 Symfony 2 启发。

/app
    /config
        database.xml
/src
    /VendorName
        /AppName
            /Database
                /Provider
                    DatabaseInterface.php
                    MSSQL.php
                    MySQL.php
                    Oracle.php
            Database.php
        Application.php
        Bootstrap.php

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2022-10-21
    • 2010-12-14
    • 2017-01-09
    • 1970-01-01
    • 1970-01-01
    • 2020-11-07
    • 1970-01-01
    • 2018-01-27
    相关资源
    最近更新 更多