【问题标题】:Designing a general database interface in PHP用 PHP 设计一个通用的数据库接口
【发布时间】:2010-03-17 18:46:35
【问题描述】:

我正在用 PHP 为我的 web 项目创建一个小型框架,这样我就不必为每个新网站一遍又一遍地做基本工作。创建第二个 CakePHP 或 Codeigniter 不是我的目标,我也不打算使用任何可用的框架来构建我的网站,因为我更喜欢使用我自己创建的东西。

当涉及到核心结构、请求处理等部分时,我在设计和编码框架时没有遇到任何问题,但我在为我的模块设计数据库接口时遇到了困难。

我已经考虑过使用 MVC 模式,但发现这对于我相当小的项目来说有点矫枉过正。

所以我面临的确切问题是我的框架模块(例如,viewCustomers 可能是一个模块)应该如何与数据库交互。

  • 将 SQL 直接混入 PHP 代码是否(仍然)是个好主意? (会是“旧方式”:mysql_query( 'SELECT firstname, lastname(.....))?

  • 我怎样才能抽象出如下查询?

    SELECT firstname, lastname FROM customers WHERE id=X
    

MySQL 的“助手”功能是否会像

$this->db->customers->getBy( 'id', $x );

是个好主意?

我不太确定,因为在处理更复杂的查询(例如上面非常琐碎的查询)时,它们往往变得无用。

  • MVC 中的“模型”模式是我解决这个问题的唯一真正选择吗?

  • 您目前使用什么来解决上述问题?

【问题讨论】:

  • “...因为我更喜欢使用我自己创造的东西”...那么这就是要努力的事情。严重地。您将为您自己、您的同事和您的客户提供很大的帮助。
  • 如果你很幸运,你最终会得到一个有缺陷的、功能低劣的 CakePHP 克隆。世界需要的是另一个 PHP 包装器。对。
  • 当我第一次构建我的 ORM 然后将我的代码移植到它时,我简直不敢相信。我从我的模型中删除了数百行不再需要的代码。现在一切都小了很多。当然,我的 ORM 只有一个文件大小。对于那些浪费的 +500kb 库,我不会说同样的话。
  • @lamas:您可能患有“非此处发明”综合症。我也时常患有的一种综合症:en.wikipedia.org/wiki/Not_Invented_Here
  • 不知何故,如果有人在 SO 上提出一个无法用一个词回答的问题,他可能会得到免费的心理分析(我的意思是这个)。这不像我只使用我自己提出的东西,或者你看到我现在上传我自己的更好的 SO 克隆吗? fireeyedboy:不,我没有患上“非在这里发明”综合症。 我只是不想现在用 2 到 5MB 的 Doctrine ORM 来破坏我的项目

标签: php mysql design-patterns


【解决方案1】:

我相信您只是想从您的模块中访问您的数据库。我会避免直接从代码中使用 mysql_query 。相反,使用抽象的 DB 访问的简单模型会简单明了。

例如,您可以使用以下代码创建像 models/Customers.php 这样的文件:

<?php

class Customers {

    public function getById($id) {
        $sql = "SELECT first_name, last_name FROM customers WHERE id='$id'";
        $res = $DB::getRow($sql);
        return ($res);
    }
}

我假设某种 DB 助手已经被实例化并且可以作为 $DB 使用。 Here 是一个简单的使用 PDO 的。

现在,您应该将其包含在您的模块中并使用以下方式:

<?php

include_once "models/Customers.php";

$customers = new Customers();
$theCustomer = $customers->getById(intval($_REQUEST['cust_id']));


echo "Hello " . $theCustomer['first_name']

干杯。

【讨论】:

    【解决方案2】:

    您是否研究过 http://www.doctrine-project.org/ 或其他 php orm 框架(想到了 zend_db)?

    【讨论】:

    • 你的意思是我应该看看它们是如何制作/工作的还是使用它们?
    • 使用它们。在我看来,再次改造它们是浪费时间。就我个人而言,我真的很喜欢 zend 框架的方式。您编写自己的应用程序/框架,但为特定任务插入其中的一些模块。
    • 问题是这些吸收了多少内存。 PHP 不是为每个请求加载这么多文件和类而构建的。因此,您需要在需要强大的服务器或简单的编码之间进行权衡。
    • Doctrine 和其他人受到“高性能 MySQL”作者的强烈反对 mysqlperformanceblog.com
    • 正如 xeoncross 已经说过的那样 - 它总是归结为生产力/安全性与原始性能......顺便说一句,我没有找到任何文章通常不鼓励在 mysqlperformanceblog.com 上使用 orm 啊,并且apc 以及 docrtine 将所有内容“编译”到一个文件中的方式也有帮助
    【解决方案3】:

    如果您需要速度,请使用原始查询(但您确实应该使用带有prepared queries 的 PDO)。

    如果您想要更多面向对象的东西,您可以(按照您的建议)使用助手进行设计。

    曾经,我设计过类似的东西,其概念如下:

    1. 数据库连接/处理程序类(处理与不同数据库和不同服务器的多连接,例如 MySQL、Oracle 等);
    2. 每个操作一个类(即 SELECT、DELETE 等);
    3. 过滤器类(例如 RangeFilter);

    代码看起来像这样:

    $select = new Select('field1', 'field2', );
    $result = $select->from('myTable')
                     ->addFilter(SQLFilter::RangeFilter, 'field2')
                     ->match(array(1, 3, 5))
                     ->unmatch(array(15, 34))
                     ->fetchAll();
    

    这是一个如何构建它的简单示例。

    您可以进一步实现表关系的自动化处理、字段类型检查(对表使用自省)、表和字段别名支持等。

    这似乎是一项漫长而艰苦的工作,但实际上,制作所有这些功能不会花费您太多时间(约 1 个月)。

    【讨论】:

    • 我已经好几年没接触过PHP了,所以我现在不知道有没有,但是那个时候(2010年!)还没有这样的框架。但我听说 Laravel 有这样的东西:laravel.com/docs/5.0/queries
    • 不错,可能有用
    【解决方案4】:

    三个提示:

    • 使用存储过程(这样您就可以将 php 与 db 分开)
    • 对准备好的语句使用 PDO/MySQLiCALL NEWS_LIST(?, ?)
    • 为您的数据库使用静态类。允许您在任何模块中访问它。

    【讨论】:

    • +1:PDO,PDO,哇哇哇。 PDO,PDO,嘘! (对不起,工作无聊)
    • 为 DB 使用静态类并不比只使用 mysql_query 好多少。全局可变对象(作为静态 DB 类)是不好的风格,因为它们通常是非常高耦合的来源。
    • 高耦合?我严重怀疑这一点。使用存储过程的静态自定义 DB 类将允许您在所有模块之间使用 DB,同时保持更改底层数据库服务(mysql、postgres)或添加缓存(memcache)的能力,而无需修改模块。这会降低耦合度。
    • 我引用:“允许您在任何模块中访问它。”这引入了“任何模块”和 DB 类之间的依赖关系。应该并且可以轻松注入的依赖项。这样,“任何模块”都与 DB 类紧密耦合。如果你想引入一些提到的优化/定制或分片的东西,你最终也会扼杀 DB 类。将所有内容抽象为每个类的驱动程序关系是您至少可以做的。
    【解决方案5】:

    原始 SQL 仍然是我的赢家,我喜欢控制我发送到服务器的内容(例如索引使用、复杂的 JOIN 子句等),因此我通常远离辅助函数。

    您应该使用已经提供了很多功能的 PDO,如果这还不够,您可以扩展它(可能使用您自己的功能,例如在实际查询数据库之前检查 Memcached/APC 上的命中)。您还可以扩展该类以实现您自己的 SQL 函数,例如:

    function getUser($user_id) {
        return $this->query("SELECT * FROM users WHERE id = " . (int) $user_id);
    }
    

    当然,从模型中你应该仍然可以发送:

    $this->db->query("SELECT * FROM users WHERE id = " . (int) $user_id);
    

    并得到相同的结果。这些函数应仅用作快捷方式,扩展类不应包含在框架中,因为它将依赖于站点。

    MVC 模式非常适合这种情况,因为您可以仅将数据库用作驱动程序,然后您的模型可以将数据转换为您需要的数据。创建一个简单的MVC结构并不难,以后会给你带来好处。

    【讨论】:

    • 数据库抽象是个好主意,但是 PDO 只是解决问题的一种方法。 PHP 还附带了开箱即用的 dbx_。然后是 ADOdb、ORM、元数据库....但我同意尝试抽象 SQL 语法并没有什么收获和失去很多。
    【解决方案6】:

    你听起来像我。你见过http://github.com/Xeoncross/micromvchttp://github.com/Xeoncross/database 中的一个文件ORM 吗?挖掘我的代码,我想你会找到你想要的。

    解决方案是使用某些查询的全部原始功能 - 同时仍允许 ORM 和查询构建器(如 codeigniter 的 AR)处理其他事情。

    两个都不错。

    【讨论】:

      【解决方案7】:

      并不是说我知道确切的答案(我也不认为它存在),但我认为我可以在这里分享我所拥有的。我使用自己的数据库“框架”,轻量级(目前约 1000 行)且易于使用。我的主要目标是简化 sql 的使用,而不是对程序员(我:)“隐藏”它。一些例子:

       // row() is 'query' + 'fetch' in one
       $user = $db->row("select * from users where id=25");
      
       // the same, injection safe
       $user = $db->row("select * from users where id=?", $_GET['id']);
      
       // ? placeholders are smart
       $someUsers = $db->rows("select * from users where id IN(?)", array(1, 2, 10));
      
       // ...and even smarter
       $data = array('name' => 'Joe', 'age' => 50);
       $id = 222;
       $db->exec("update users set ?a where id=?", $data, $id);
      
       // 'advanced' fetch functions
       $topNames   = $db->vlist("select name from users order by name limit 10");
       $arrayOfIds = $db->nlist("select id from users where age > 90");
      
       // table() returns a Table Gateway
       $db->table('users')->delete('where id=?', 25);
      
       // yes, this is safe
       $db->table('users')->insert($_POST);
      
       // find() returns a Row Gateway object
       $db->table('users')
           ->find('where name=?', 'Joe')
           ->set('status', 'confirmed')
           ->save();
      

      【讨论】:

      • 当我在您的网站上看到 _array 和其他位时,我成为了您的代码的粉丝。你有机会也发布上述内容吗?
      • 嘿,谢谢。这几乎是在进行中的工作,我认为我不能很快发布任何准备好使用的代码。过时的版本可以在这里找到code.google.com/p/mria/source/browse/trunk/yo/db.inc.php如果您对此有任何问题/建议,请告诉我。
      【解决方案8】:

      了解这一点:数据库交互是一个已解决的问题。

      所以除非你真的想要这样做 a) 体验或 b) 因为你是强迫症并且想知道你将使用的代码的每个字符,那么我会选择现有的解决方案。

      还有很多:PEAR::MDB2、Zend::Db、Creole、Doctrine、Propel,仅举几例。

      【讨论】:

      • 我不会说数据库交互是一个已解决的问题,尤其是通过 ORM 的。这就是为什么所有 ORM 都允许您直接查询数据库,而不是从 SQL 中提供完整的抽象。
      【解决方案9】:

      我刚刚离开“辅助函数”路径,让我烦恼的一件事是,我继续在一个文件中添加函数,该文件随着相同或相似或失效的函数不断增长。我认为行数为 600,在我看来,这对于单个文件来说已经太多了。这并没有让我放弃这个想法,但我会更有条理地进行下一次跋涉。我可能会根据 db 操作(选择、插入等)将 db 函数拆分为多个文件。

      所以我的建议是去尝试“辅助功能”并尽可能有条理。

      另外,我是第一次使用 PDO,非常喜欢它。它不像 mysql() 函数那样低技术,也不像我们可以提到但不会提到的臃肿技术。我将再次使用 PDO。

      【讨论】:

        【解决方案10】:

        关于这个话题似乎有很多不同的意见,由于我还没有在这里找到一个真正令人满意的答案,而且赏金也快结束了,所以我会写一些我在最后几天想出来的东西反复试验:

        我正在使用单例 MySQL 类来处理连接和非常基本的查询以及可能发生的错误。

        /users/show/1(使用 mod_rewrite)这样的单页不使用原始 SQL,而是使用类似于以下示例的某种轻量级 ORM:

        $user = $this->db
                     ->users
                     ->getBy( 'id', $id );
        

        $this-&gt;db 是具有__get( $tableName ) 方法的数据库抽象类的实例。访问未定义的users 属性然后触发它。其余的解释自己;查询由传递给getBy( ) 的参数形成(SQL 转义也由它处理),其结果作为数组返回。

        我还没有完成整个想法,但是向数据库添加新用户可能如下所示:

        $user = $this->db
                     ->users
                     ->new;
        $user->id = 2;
        $user->name = 'Joe';
        $user->save( );
        

        正如我所说,这个概念还没有真正完成,并且可能存在(巨大的)缺陷。然而我认为它可能比普通的 MySQL 更容易编写、更安全和更容易维护。 整个“东西”的其他一些好处是它很小,因此相当快而且非常简单。

        我知道这无法与已经存在的极其强大的 ORM 和框架竞争,但由于上述我的一个 cmets 中提到的某些原因,我仍在创建它。

        【讨论】:

        • 使用像 micromvc 这样的 MVC 将允许 /users/show/1 调用 User::show($id) {} 然后您可以在其中放置 $user = new User($id);
        【解决方案11】:

        如果您确实计划创建一个数据库类,那么考虑将其设为单例可能是一个想法,允许您在不声明/创建它的情况下使用它,因为...

        global $db;
        $db = new db;
        $db->query ('... sql ...');
        

        当你可以做的时候有点多余

        db::query ('... sql ...');
        

        我有一组 SQL 函数,我几乎经常使用这些函数来将过去多行转义的大量 SQL 减少为单个调用,例如:

        get_element ($table, $element, $value, $column='id');
        get_row ($table, $value, $column='id');
        

        因此,如果您只想从 id 为 4 的表“客户”中获取名称:

        $name = db::get_element ('customers', 'name', 4);
        

        还有附带的函数 query_element 和 query_row,您只需将 SQL 字符串传递给它,它就会返回单个元素/行。

        以及插入/更新的功能,例如

        $array = array (
            'name' => 'bob jones',
            'age' => 28
        );
        $insert_id = db::insert_array ('customers', $array);
        
        $customer_details = db::get_row ('customers', $insert_id);
        
        $customer_details['age'] = 30;
        
        db:update_array ('customers, $customer_details);
        

        将创建一个新行,将详细信息拉回,更新年龄,然后将其重新写入数据库。

        在每个表的基础上创建自定义 SQL 访问模块通常是我一直发现的错误 - 最好使用合理的函数一般性地查询数据库。

        如果您确实必须使用任何具有复杂连接的东西,那么最好为其创建函数,例如 getCustomerInfo (),但如果您只是想要一个通用的表值查找,那么制作大量自定义方法只会增加出错的机会在其中之一。另外转义数据非常重要 - 如果您可以尽可能多地使用非 sql 并通过一些核心函数将其汇集,您可以确保所有内容都相当容易地正确转义。

        如果您想查看我的自定义数据库类,请告诉我。

        【讨论】:

        • 如果您需要扩展您的数据库类或从其他地方获取信息,因为您在整个代码中都对类名进行硬编码,所以单例是糟糕、缓慢且特别痛苦的。一个抽象类或接口 + 在需要的地方传递数据库对象将是一个更好的解决方案。
        猜你喜欢
        • 1970-01-01
        • 2014-03-06
        • 1970-01-01
        • 2018-08-10
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多