Удобная обертка PDO

13 комментариев

Выкладываю на ваш суд свой класс-надстройку над стандартным PDO. Писался и совершенствовался он с каждым проектом, и содержит все мои изменения суммарно.

Ссылка на GitHub (обновлено 26.06.2016)

Данное расширение добавляет следующий функционал:

  • Цепочки вызовов
  • Аналоги функций из pear::db
  • Показать сформированный запрос
  • Функции для подсчета статистики запросов
  • Дополнительные функции
  • Дополнительные опции
  • Другие изменения

Цепочки вызовов

Старый синтаксис:

$request = $pdo->prepare('SELECT data FROM table WHERE param = :placeholder ');
$request->bindValue('placeholder', 'value');
$request->execute();
$data = $request->fetchAll();

Новый синтаксис:

$data = $pdo
    ->prepare('SELECT data FROM table WHERE param = :placeholder ')
    ->bindValue('placeholder', 'value')
    ->execute()
    ->fetchAll();

Вобщем короче, удобнее, нагляднее.

Цепочки поддерживают следующие функции:

PDO->prepare
PDO->query
PDOStatement->bindParam
PDOStatement->bindValue
PDOStatement->bindValueList
PDOStatement->execute

Аналоги функций из pear::db

Представлены следующие функции

public function getAll($statement, $params = array());
public function getRow($statement, $params = array());
public function getColumn($statement, $params = array(), $column_number = null);
public function getOne($statement, $params = array(), $column_name = 0);

Конечно представлены не все функции из pear:DB, но это именно те которых мне не хватало в моих проектах.

Использовать очень просто:

$data = $pdo->getAll('SELECT data FROM table WHERE param = :placeholder ', array('placeholder' => 'value'));

Запросы получаются намного короче и лаконичнее чем использование даже предложенных мною цепочек.

Показать сформированный запрос

При использовании плейсхолдеров отсутствует возможность получить полный запрос (т.е. c заменой плейсхолдеров). Вобщем то это не проблема этого расширения. Это особенность реализации PDO, ведь сама замена плейсхолдеров проводится уже в БД.

$sql = $pdo
    ->prepare('SELECT data FROM table WHERE param = :placeholder ')
    ->bindValue('placeholder', 'value')
    ->getSQL();

Вернет сгенерированный запрос. Пока работает только с именованными плейсхолдерами.

Функции для подсчета статистики запросов

Это уже функционал который не добавляет только ленивый.

Используется для подсчета колличества выполненных запросов и времени затраченного на выполнение запросов.

public function statisticTime();
public function statisticCount();

По названию понятно что какая возвращает.

Дополнительные функции

Тут оформлены дополнительные функции, которые я использую в своих проектах.

Возвращает результат SELECT FOUND_ROWS(), оформлено в одну функцию для более удобного использования (работает только для MySQL):

PDO->calcFoundRows()

Алиас для lastInsertId:

PDO->calcLastInsertId()

Присвоить значения нескольким плейсхолдерам за один вызов функции:

PDOStatement->bindValueList(array $array)

Дополнительные опции

PDOExtended::ATTR_USE_UTF

Включено по умолчанию. Только MySQL. При инициализации подключения установить все настройки кодировок в UTF-8. При подключении выполняются следующие запросы:

$this->exec("SET NAMES utf8 COLLATE utf8_general_ci");
$this->exec("SET CHARACTER SET utf8");
PDOExtended::ATTR_STRICT_MODE

Включено по умолчанию. Только MySQL. При инициализации подключения включить строгий режим. При подключении выполняется следующий запрос:

$this->exec("SET sql_mode='STRICT_TRANS_TABLES,NO_ZERO_DATE,NO_ZERO_IN_DATE'");
PDOExtended::ATTR_TIME_ZONE

Только MySQL. При инициализации подключения установить временную зону сервера. При подключении выполняется следующий запрос:

$this->exec("SET time_zone=?");

Например:

$pdo->setAttribute(PDOExtended::ATTR_TIME_ZONE, '+0:00');
$pdo->setAttribute(PDOExtended::ATTR_TIME_ZONE, null);  //Отключить изменение временной зоны сервера

Другие изменения

Включены PDOExtended::ATTR_STRICT_MODE и PDOExtended::ATTR_USE_UTF. Будем двигать правильные настройки mysql и правильную кодировку в массы =)

По умолчанию включена генерация исключений при возникновении ошибок PDOExtended::ERRMODE_EXCEPTION.

Также изменены две стандартные функции, а точнее добавлены дополнительные параметры к ним:

PDO->exec($statement, $params = array());
PDO->query($statement, $params = array());

Функции делают тоже самое, согласно их документации, но добавлен еще один параметр $params массив который может содержать набор плейсхолдеров.

Пример:

$data = $pdo->
    query(
        'SELECT data FROM table WHERE param = :placeholder ',   
        array(
            'placeholder' => 'value'
        )
    )->
    fetchAll();

Использование

Приинклудьте файл с классом расширения в ваш проект. И при создании объекта PDO используйте имя класса PDOExtended:

$pdo = new PDOExtended([...])

Заключение

Как то так. Понятно что уже существующие проекты никто не будет переводить на этот класс. Но новые — почему бы и нет?

Так например при разработке меня всегда раздражала излишняя многословность PDO из коробки. Здесь она исправлена.

Ссылку на класс см. в начале статьи.

  1. mr.The

    Гуд, но кажется уже давно пора переходить на activerecord\datamapper паттерны. Никакого sql, в подавляющем большинстве случаев, писать вообще не прийдётся. Да и удобнее.

    1. Дмитрий Амиров Автор

      Ну вобщем то ar/dm представляют лишь объекты, и это немного другое.

      Я в своих проектах использую SQL для некоторых запросов и простенькие DataMapper-ы для представления основных объектов. Считаю это разумным компромисом между абстрактностью и простотой приложения. В итоге код достаточно легко поддерживается, и довольно понятен (хотя может это только на мой взгляд).

      Отречься полностью от SQL и фигачить модели для всего только для ООП ради ООП не считаю правильным.

      Ну вобщем как то так =)

        1. Дмитрий Амиров Автор

          И это правильно. Нужно делать так как удобно самому, а не слушать других и уж тем более не делать так только потому что это модно.

          За примером далеко ходить не надо. Иной раз достаточно почитать комменты к статьям на хабре. С комментариями в духе — мол у вас тут реализованно намного менее абстрактно чем во всех остальных фреймворках. Так и хочется спросить в ответ — вы е**нулись? Зачем делать тридцать слоев абстракции, если ты уверен что сделанного достаточно? Нет же, все стараются захреначить архитектуру суперрасширяемого приложения. А самое смешное, что вся эта огромная вложенность абстракций в момент когда потребуется отрефакторить код оказывается бесполезна, так как при разрабтке трудно учесть все, и то что вам надо будет дописать, в 90% случаев не уложится в них.

          Просто одно дело делать так потому что это самому удобно, а второе дело — использовать херову кучу абстракций — ради упрощения будущего рефакторинга. Второе я не одобряю. Да, я не спорю, нужно правильно проектировать архитектуру, но переизбытка быть не должно. Все должно быть в меру)

          Это не в твой адрес. Это мне просто высказаться захотелось, накипело так сказать)

  2. Виктор

    Да с цепочками вызовов намного удобнее, код лаконичнее получается. Спасибо за идею

        1. Дмитрий Амиров Автор

          Понимаю, плох тот програмист который не любит писать свои велоспеды)

  3. proger

    странно как-то работает ваш класс…

    не работает (и не генерит ошибок):

    $db = new PDOExtended('mysql:host=localhost;dbname=baza', 'root', '');
    $res = $db->prepare("INSERT INTO table1  (name_ru)  VALUES (:str) ");
    $res->execute(Array('str' => 'AAA'));
    

    работает :

    $db = new PDO('mysql:host=localhost;dbname=baza', 'root', '');
    $res = $db->prepare("INSERT INTO table1  (name_ru)  VALUES (:str) ");
    $res->execute(Array('str' => 'AAA'));
    

    OpenServer, PHP 5.5, MySQL 5.5

    1. Дмитрий Амиров Автор

      Скорее всего дело в том что мой класс автоматически включает strict mode. Это режим в котором mysql бросает ошибки на любые неправильные данные (вместо округления, обрезания и проч.). По дефолту этот режим в MySQL выключен, что не совсем правильно, и слишком расслабляет программистов, откуда в итоге возникают ошибки.

      Видимо у вас не задано значение по умолчанию для какого-то из столбцов, либо же данные «AAA» для столбца «name_ru» некорректны. Класс не бросает ошибки потому что по дефолту это отключено и в самом оригинальном PDO.

      В общем ваш комментарий наконец сподвигнул меня обновить репозиторий на гитхабе. Возьмите новый код класса оттуда. Там я вынес настройку включения strict mode, а также включил генерацию исключений по умолчанию (наверное это правильнее чем их замалчивать).

      Так например, ваш код теперь должен будет выбросить ошибку с пояснением проблемы при его выполнении. Для отключения строгого режима можете сделать так:

      $db = new PDOExtended('mysql:host=localhost;dbname=baza', 'root', '', array(
      	PDOExtended::ATTR_STRICT_MODE => false,
      ));
      

      Ваш код кстати можно немного сократить:

      $db = new PDOExtended('mysql:host=localhost;dbname=baza', 'root', '');
      $db->exec("INSERT INTO table1  (name_ru)  VALUES (:str)", array('str' => 'AAA'));
      

Добавить комментарий

Прочли запись? Понравилась? Не стесняйтесь, оставьте, пожалуйста, свой комментарий. Мне очень интересно, что вы думаете об этом. Кстати в комментарии вы можете задать мне любой вопрос. Я обязательно отвечу.

Вы можете оставить коментарий анонимно, для этого можно не указывать Имя и email. Все комментарии проходят модерацию, поэтому ваш комментарий появится не сразу.