【问题标题】:Making a databaseDo() function for hooking PDO queries制作用于挂钩 PDO 查询的 databaseDo() 函数
【发布时间】:2012-09-15 13:02:16
【问题描述】:

所以我开始 this tutorial 作为 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 作为参数。这是将变量传递给这样的函数而不使其成为全局函数的正确方法吗?

【问题讨论】:

    标签: php function pdo


    【解决方案1】:

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

    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));
    

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

    【讨论】:

    • 酷。我才刚刚开始研究 OOP 的东西,所以我还没有机会使用它。
    【解决方案2】:

    虽然避免重复 (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 方法是在一个组件中处理数据库连接管理,例如在上下文感知单例类实例中(一种常用方法),以及在另一个组件中处理查询。因此,您的查询功能不一定要因数据库连接处理的变化而改变。你提到的教程有instructions for creating and using such a singleton instance

    【讨论】:

      猜你喜欢
      • 2020-05-02
      • 2011-06-04
      • 2020-06-17
      • 1970-01-01
      • 2013-01-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多