Является ли эта обертка для PDO «хорошим кодом»? Есть ли потенциальные проблемы? - PullRequest
5 голосов
/ 06 марта 2009

Я создал этот класс для работы с 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;

    }
 }

Ответы [ 5 ]

5 голосов
/ 06 марта 2009

Это не плохо, и, как уже было сказано, это может помочь для небольших приложений, хотя это в основном очень тонкая абстракция для другой абстракции. Это не приносит много других функциональных возможностей.

Что-то, что вы могли бы рассмотреть, среди прочего:

  • Поскольку это код PHP5, используйте исключения вместо trigger_error и set_exception_handler, если это необходимо, до тех пор, пока исключения не станут более распространенными, но, безусловно, более чистыми и более перспективными.
  • Вы используете синглтон, это не обязательно плохо, но в этом случае, например, одним недостатком будет то, что вы сможете обрабатывать только одно соединение с одной базой данных.
  • Я не знаю, используете ли вы хранимые процедуры, но хранимая процедура может возвращать набор результатов через метод query().
  • У вас есть две точки с запятой (;;) в конце вашей строки new PDO.

При этом, я не думаю, что ваш метод запроса слишком велик, и на данный момент мало что можно было бы вспомнить из других источников. Хотя, как только вы увидите две или три строки, которые можно вызвать из другой функции, разбейте ее. Это хороший способ СУХОЙ .

2 голосов
/ 06 марта 2009

Вот что я использовал (просто замените ссылки на 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 ...

Цель состоит в том, чтобы сохранить конфигурацию БД в файле * .ini (редактируемом не кодером).

2 голосов
/ 06 марта 2009

Да и нет.

Это хороший код для простого быстрого и грязного приложения.

Проблема возникает, когда вы используете это в более сложном структурированном приложении. Где обработка ошибок будет зависеть от того, какой sql вы выполняете.

Также любые серьезные ошибки будут отображаться как ошибки типа «проблема в строке 999» где 999 в вашей супер пупер рутине и вам будет трудно отследить его к конкретному запросу sql.

Сказав, что я сам все время так делаю на небольших проектах.

1 голос
/ 09 марта 2009

Я пошел другим путем и создал класс, который расширяет PDO набором функций-оболочек около prepare() / execute(), и это намного лучше, чем встроенные функции (хотя это немного субъективно ...).

Еще одна вещь: вы должны установить PDO::ATTR_EMULATE_PREPARES на false, если вы не используете действительно старую версию mysql (<= 4.0). По умолчанию он равен <code>true, что вызывает сильную головную боль и приводит к непонятным последствиям ... что, я полагаю, является причиной того, что у вас появилась огромная оболочка вокруг bindParam().

0 голосов
/ 06 марта 2009

Чтобы ответить на ваш вопрос, если это хороший код или нет, спросите себя:
Какова дополнительная ценность моего кода по сравнению с непосредственным использованием PDO?

Если вы найдете хороший ответ, используйте свой код. Если нет, я бы придерживался PDO.

Также попробуйте рассмотреть возможность реализации класса DB Zend Framework , который работает сам по себе и поддерживает PDO.

...