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) クエリを実行します。

さて、あなたは「はい!」と言うかもしれません。それがまさに私が欲しいものです!一石二鳥だ!」と言うあなたの理由も理解できます。ただし、一方の責任の処理方法を変更する必要がある場合、もう一方の責任の処理方法も変更する必要がある可能性が高いため、責任を混在させると問題が生じる可能性があります。

将来のある時点で、1 つではなく 2 つのデータベース接続をサポートする必要があると想像してください。2 つのデータベースのうちの 1 つがテーブルのトランザクションをサポートし、もう 1 つがサポートしていないとします。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();
  }
}

1 つの責任を処理するための要件が​​追加されると、2 番目の責任の処理が複雑になり、この機能の維持がますます難しくなります。

このシナリオでの DRY へのより良いアプローチは、データベース接続管理を 1 つのコンポーネント (コンテキスト認識シングルトン クラス インスタンス内など) で処理し (一般的なアプローチ)、クエリ処理を別のコンポーネントで処理することです。したがって、データベース接続処理の変更により、クエリ関数を必ずしも変更する必要はありません。参照したチュートリアルには、そのようなシングルトン インスタンスを作成して使用するための手順が記載されています。

于 2012-09-15T04:39:02.407 に答える