5

私はこのクラスを PDO で動作するように作成し、SQL クエリを「より簡単に」、心配する必要がないようにしました。

ここに私の考えがあります

  • クラス DB が PDO を拡張するようにすべきでしょうか?
  • クエリ メソッドが大きすぎませんか? 呼び出されるプライベートメソッドに分割する必要があります..これは疎結合として知られているものですか?
  • SELECT クエリを検出する私の方法は、それ自身の利益のためにはあまりにも醜いですか?
  • 他に明らかな問題は何ですか?私は少しずつ学習しているので、多くの潜在的な問題を見落としていた可能性があると確信しています。

ありがとうございました

`

 class Db
 {
    private static $_instance = NULL;


    private function __construct() {

        // can not call me
    }

    private function __clone() {

        // no!
    }

    public static function getInstance() {

        if (!self::$_instance)
        {

            try {

                self::$_instance = new PDO('mysql:host=' . CONFIG_MYSQL_SERVER . ';dbname=' . CONFIG_MYSQL_DATABASE, CONFIG_MYSQL_USERNAME, CONFIG_MYSQL_PASSWORD);;
                self::$_instance-> setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

            } catch(PDOException $e) {

                trigger_error($e->getMessage());

            }

        }

        return self::$_instance;


    }



    public static function query($query /*string*/, $bindings = NULL)
    {

        $queryPortion = substr($query,0, 6);

        try {

            if ($bindings) {

                    $prepared = self::getInstance()->prepare($query);

                    foreach($bindings as $binding=>$data) { // defaults to string

                        if (!is_array($data)) {
                            $prepared->bindParam($binding, $data); 

                        } else {

                            switch(count($data)) {

                                case 1:
                                    $prepared->bindParam($binding, $data['value']);
                                    break;                          

                                case 2:
                                    $prepared->bindParam($binding, $data['value'], $data['dataType']);
                                    break;

                                case 3:
                                    $prepared->bindParam($binding, $data['value'], $data['dataType'], (int)$data['length']);
                                    break;                          

                                default:
                                    trigger_error('An error has occured with the prepared statement bindings.');
                                    return false;
                                    break;
                            }
                        }

                    }

                    $prepared->execute();

                    return $prepared->fetchAll(PDO::FETCH_ASSOC);


            } else if (String::match($queryPortion, 'select')) { // if this is a select query

                $rows = self::getInstance()->query($query);

                return $rows->fetchAll(PDO::FETCH_ASSOC);

            } else {

                return self::getInstance()->exec($query);

            }


        } 
        catch(PDOException $e)
        {
            trigger_error($e->getMessage());
        }


    }

    public static function getLastInsertId()
    {
        try {
            self::getInstance()->lastInsertId();
          }
        catch(PDOException $e)
        {
            trigger_error($e->getMessage());
        }

    }

    public static function disconnect()
    {
        // kill PDO object
        self::$_instance = NULL;

    }
 }
4

5 に答える 5

5

それは悪くはなく、小さなアプリケーションに役立つかもしれないと言われていますが、ほとんどの場合、別の抽象化に対する非常に薄い抽象化です。それは他の多くの機能をもたらしていません。

とりわけ、考慮したいことがあります。

  • これは PHP5 コードであるため、必要に応じて代わりに例外を使用し、例外がより広く普及するまで使用してください。trigger_errorset_exception_handler
  • シングルトンを使用していますが、必ずしも悪いことではありませんが、この場合、たとえば、1 つのデータベースへの 1 つの接続しか処理できないという欠点があります。
  • ストアド プロシージャを使用しているかどうかはわかりませんが、ストアド プロシージャもメソッドを介して結果セットを返す場合があります。query()
  • 行末に2 つのセミコロン ( ;;) がありますnew PDO

そうは言っても、あなたのクエリメソッドが大きすぎるとは思いません。現時点では、他の場所から呼び出すことができるものはあまりありません。ただし、別の関数から呼び出すことができる 2 つまたは 3 つの行が表示されたら、すぐに分割してください。これはDRYの良い方法です。

于 2009-03-06T04:33:25.073 に答える
2

これが私が使用したものです(Zzz_Configへの参照を$ GLOBALS ['db_conf']などに置き換えるだけです):

/**
 * Extended PDO with databse connection (instance) storage by name.
 */
class Zzz_Db extends PDO
{
    /**
     * Named connection instances.
     *
     * @var array
     */
    static private $_instances;

    /**
     * Retrieves (or instantiates) a connection by name.
     *
     * @param  string $name  Connection name (config item key).
     * @return Zzz_Db        Named connection.
     */
    static public function getInstance($name = null)
    {
        $name = $name === null ? 'db' : "db.$name";
        if (!isset(self::$_instances[$name])) {
            if (!$config = Zzz_Config::get($name)) {
                throw new RuntimeException("No such database config item: $name");
            }
            if (!isset($config['dsn'])) {
                if (!isset($config['database'])) {
                    throw new RuntimeException('Invalid db config');
                }
                $config['dsn'] = sprintf('%s:host=%s;dbname=%s',
                    isset($config['adapter']) ? $config['adapter'] : 'mysql',
                    isset($config['host']) ? $config['host'] : 'localhost',
                    $config['database']);
            }

            $db = self::$_instances[$name] = new self(
                $config['dsn'],
                isset($config['username']) ? $config['username'] : null,
                isset($config['password']) ? $config['password'] : null);
            $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
            //$db->setAttribute(PDO::ATTR_STATEMENT_CLASS, 'Zzz_Db_Statement');

            if ($db->getAttribute(PDO::ATTR_DRIVER_NAME) == 'mysql') {
                $db->setAttribute(PDO::ATTR_EMULATE_PREPARES, true);
                $db->exec('SET CHARACTER SET utf8');
            }
        }

        return self::$_instances[$name];
    }
}

使用法:

$db = Zzz_Db::getInstance(); // or Zzz_Db::getInstance('some_named_db')
$stmt = $db->prepare('SELECT ...

目標は、db 構成を *.ini ファイル (非コーダーが編集可能) に保持することです。

于 2009-03-06T05:10:10.867 に答える
2

はいといいえ。

これは、単純で手早くダーティなアプリケーションに適したコードです。

より複雑な構造化アプリケーションでこれを使用すると、問題が発生します。エラー処理は、実行しているSQLによって異なります。

また、重大なエラーは「999 行の問題」タイプのエラーとして表示されます。999 はスーパー デュパー ルーチンにあり、特定の sql リクエストまでトレースするのは困難です。

とは言っても、私は小さなプロジェクトで常にこの種のことを自分で行っています。

于 2009-03-06T04:11:01.680 に答える
1

私は別の方法で PDO を拡張し、prepare()/の周りに一連のラッパー関数を追加するクラスを作成しましたexecute()。これは、組み込み関数よりもはるかに優れています (ただし、これは少し主観的ですが...)。

もう 1 つ:本当に古いバージョンの mysql (<=4.0) を使用していない限り、に設定PDO::ATTR_EMULATE_PREPARESする必要があります。falseこれtrueは大きな頭痛の種であり、あいまいな方法で物事が壊れる原因になります...これが、そもそも巨大なラッパーを持っている理由だと思いますbindParam()

于 2009-03-09T03:21:17.403 に答える
0

あなたの質問に答えるために、それが良いコードであるかどうか、あなた自身に尋ねてください:
PDOを直接使用することと比較した私のコードの付加価値は何ですか?

良い答えが見つかったら、コードを使用してください。そうでなければ、私はPDOに固執するでしょう。

また、それ自体で動作し、PDOをサポートするZendFrameworkのDBクラスの実装を検討してみてください。

于 2009-03-06T18:16:33.847 に答える