5

所以我开始本教程作为 PHP PDO 的介绍。到目前为止,我只使用基本mysql_*类型查询。

我注意到,在整个教程中,connect -> do action -> disconnect模式是重复的,只有do action部分发生了变化。

在现实世界的环境中,通过创建一个可以传递查询的函数来消除重复是一个好主意吗?

例如:

处理查询的函数:

<?php
function databaseDo($action) {
    $db_hostname = 'localhost';
    $db_username = 'root';
    $db_password = 'root';

    try {
        // Establish DB connection
        $dbh = new PDO("mysql:host=$hostname;dbname=mysql", 
                $db_username, $db_password);
        echo 'Connected to database';

        // Do something
        $action($dbh);        // <- here goes whatever action we wish to perform

        // Close connection
        $dbh = null;
    } 
    catch(PDOException $e) {
        echo $e->getMessage();
    }
?>

然后,假设我想执行 PDO 教程的第一个示例中的操作,我会这样设置:

<?php
// Define action
$insert = function($dbh) {
    $query = "INSERT INTO animals(animal_type, animal_name)
                VALUES ('kiwi', 'troy')";
    $exec = $dbh->exec($query);
    echo $exec; 
};

// Perform action
databaseDo($insert);
?>

$dbh 的范围

我使用 $dbh 作为参数。这是将变量传递给这样的函数而不使其成为全局变量的正确方法吗?

4

2 回答 2

4

是的,这是一个好主意 一个建议是制作一个使用单例模式的数据库帮助器类。像

abstract class DB
   {

    protected static $instance;

    protected $db;

    protected static $host = 'host';
    protected static $user = 'user';
    protected static $pass = 'pass';
    protected static $database;

    public static function getInstance()
    {
        if (!isset(self::$instance)) self::$instance = new static();

        return self::$instance;
    }

    public static function doStatement($statement, array $parameters)
    {

         $handler = self::sql()->prepare($statement);
         $handler->closeCursor();
         $handler->execute($parameters);
         return $handler;
    }
    protected function __construct()
    {
        $this->db = new PDO(sprintf('mysql:host=%s;dbname=%s', static::$host, static::$database), static::$user, static::$pass);
        $this->db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
        $this->db->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_OBJ);

    }

    public static function db()
    {
        return static::getInstance()->db;
    }
}

class Main extends DB
{
    protected static $database = 'db';
}

使用它

$db = Main::getInstance();
$results = $db::doStatement('SELECT * FROM table WHERE id = :id', array(':id' => 5));

现在这只是非常基本的,还需要添加更多(异常处理、更多/更好的辅助方法等),但我在许多项目中都使用过类似的东西。

于 2012-09-15T03:18:02.867 回答
1

虽然避免重复 (DRY) 是一个始终应考虑到您的编码决策的原则,但它应该在不违反另一个重要原则的情况下实现,即关注点分离(SoC,另请参见SRP)。您的示例 databaseDo($action) 具有双重用途:它 (1) 实例化数据库连接和 (2) 它执行查询。

现在,你可能会说,‘是的!这正是我想要的!一块石头杀死两只鸟!',你这样说的理由是可以理解的。但是,混合职责可能会出现问题,因为当您必须更改处理一项职责的方式时,您可能还必须更改处理另一项职责的方式。

想象一下,在某个时候,您需要支持两个数据库连接,而不仅仅是一个。假设两个数据库之一支持另一个不支持的表上的事务。您的 databaseDo() 函数首先必须协商连接到哪个数据库,然后,为了安全地执行“do”操作,需要进行一些事务支持测试。它看起来像这样:

$context = 'production'; // but it could equally be 'development'

function databaseDo($action) {
  $db_hostname = ($context == 'production') ? 'http://remotehost.com' : 'localhost';
  $db_username = ($context == 'production') ? 'apache' : 'root';
  $pass = ($context == 'production') ? 'productionpassword' : 'developmentpassword';

  try {
    $dbh = new PDO("mysql:host=$db_hostname;dbname=mysql", $db_username, $db_password);
    echo 'Connected to database';

    if($context == 'production') {
      // ... some complicated logic to determine whether the production db 
      // will support your query, then execute it if so, exit if not ... 
    }

    if($context == 'development') {
      // ... some more complicated logic for testing and querying the 
      // development db ...
    }

    $dbh = null;
  } catch(PDOException $e) {
    echo $e->getMessage();
  }
}

处理一个职责的额外要求会增加处理第二个职责的复杂性,并且这个功能将变得越来越难以维护。

在这种情况下,更好的 DRY 方法是在一个组件中处理数据库连接管理,例如在上下文感知单例类实例中(一种常用方法),以及在另一个组件中处理查询。因此,您的查询功能不一定要因数据库连接处理的变化而改变。您提到的教程包含创建和使用此类单例实例的说明

于 2012-09-15T04:39:02.407 回答