在PHP中设计通用数据库接口

时间:2010-03-17 18:46:36

标签: php mysql design-patterns

我正在为PHP中的Web项目创建一个小框架,所以我不必为每个新网站反复做基础工作。我的目标不是创建第二个CakePHP或Codeigniter,而且我也不打算用任何可用的框架构建我的网站,因为我更喜欢使用我自己创建的东西。

在核心结构,请求处理等部分的设计和编码方面,我没有遇到任何问题,但我已经陷入了为模块设计数据库接口的困境。

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

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

  • 将SQL直接混合到PHP代码中(仍然)是个好主意吗? (将是“老路”:mysql_query( 'SELECT firstname, lastname(.....))?

  • 我如何抽象如下的查询?

    SELECT firstname, lastname FROM customers WHERE id=X
    

MySQL“helper”函数会像

一样

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

是个好主意吗?

我不太确定,因为在处理更复杂的查询时,它们往往变得毫无用处,例如上面几乎无关紧要的查询。

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

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

11 个答案:

答案 0 :(得分:6)

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

例如,您可以使用以下代码创建类似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']

干杯。

答案 1 :(得分:5)

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

答案 2 :(得分:3)

如果您需要速度,请使用原始查询(但您应该将PDO与prepared queries一起使用)。

如果你想要更多的OOP,你可以 - 如你所知 - 用帮助器设计它。

有一次,我设计了类似的东西,它有以下概念:

  1. 数据库连接/处理程序类(处理到不同数据库和不同服务器的多连接,如MySQL,Oracle等);
  2. 每个动作的类(即SELECT,DELETE等);
  3. 过滤类(例如RangeFilter);
  4. 代码看起来像这样:

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

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

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

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

答案 3 :(得分:2)

三个提示:

  • 使用存储过程(因此您可以将php与数据库分开)
  • 将PDO / MySQLi用于准备好的陈述CALL NEWS_LIST(?, ?)
  • 为您的数据库使用静态类。允许您在任何模块中访问它。

答案 4 :(得分:1)

原始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结构并不难,它会在以后为您带来好处。

答案 5 :(得分:1)

你听起来像我。您是否在http://github.com/Xeoncross/micromvc中看到了http://github.com/Xeoncross/database和一个文件ORM?挖掘我的代码,我想你会找到你想要的东西。

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

两者都很好。

答案 6 :(得分:1)

不是我知道确定的答案(我认为它也不存在),但我想我可以分享我在这里的内容。我使用自己的db'框架',轻量级(当前约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();

答案 7 :(得分:0)

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

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

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

答案 8 :(得分:0)

我刚刚离开了“辅助函数”路径,而且让我感到困惑的一件事就是我继续在一个文件中添加函数,这个文件随着相同或相似或不存在的函数而增长和增长。我认为行数是600,这对于我认为的单个文件来说是很多的。这并没有让我想到这个想法,但我会更有条理地进行下一次艰苦跋涉。我可能会根据db操作将db函数拆分为多个文件(select,insert etc ...)。

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

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

答案 9 :(得分:0)

关于这个话题似乎有很多不同的意见,因为我还没有找到一个真正令人满意的答案而且赏金已经快结束了,我只会写一些我在最后几天出现的内容。试错:

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

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

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

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

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

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

正如我所说,这个概念并没有真正完成,并且可能存在(巨大的)缺陷。但我认为编写,比普通的MySQL更安全,更容易维护。 整个“事物”的其他一些优点是它很小,因此相当快,也很简单。

我知道这不能与已经存在的极其强大的ORM和框架竞争,但我仍然是出于上述一条评论中提到的某些原因而创建它。

答案 10 :(得分:-4)

如果您计划制作数据库类,可能会考虑将其设为单例,允许您在不声明/创建数据库的情况下使用它,因为...

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的表'customers'获取名称,那么:

$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并通过一些核心功能将其汇集起来,那么您可以确保所有内容都能够相当容易地进行转义。

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