Абстрактный доступ к БД с помощью ADODB

Автор: content Понедельник, Апрель 9th, 2012 Нет комментариев

Рубрика: Язык PHP

Подробное описание абстрактного класса баз данных ADODB. Установка, примеры использования, особенности, ADODB & PEAR.

Установка, примеры использования, особенности, ADODB & PEAR.

1. Пару слов об ADODB

Для начала, скажу что статья рассчитана на программистов, имеющих опыт работы с СУБД, а не на начинающих пхпешников. Я предполагаю, что вы знакомы с PHP, ОПП, SQL и имеете опыт разработки web-приложений.

ADODB — это абстрактный класс доступа к базам данных, написанный на PHP.

Поясню на примере:

Предположим вы написали скрипт под mysql. И тут заказчик говорит Вам, что хостинг меняется и там есть только PostgreSQL. Если вы не использовали класс абстрактного доступа к базам данных, то вам пришлось бы:

  • заменить весь код работы с mysql на postgresql
  • переписать SQL-запросы (так как есть отличия)

Если бы вы использовали абстрактный слой доступа к БД, то вам скорее всего не пришлось бы менять php-код (только в одном месте указали бы что используете postgresql) и изменить SQL-запросы (хотя иногда и это не понадобилось бы).

Я намеренно в этом описании использовал фразу «абстрактный класс доступа к БД»,

поскольку ADODB — не единственный подобный класс. Наиболее известные конкуренты:

  • Pear::DB
  • Pear::MDB

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

Противники таких массивных классов, как ADODB или Pear::DB утверждают, что их использование плохо сказывается на производительнеости. Да, производительность падает и это вполне логично. НО:

  • Скорость не часто является самым важным фактором
    (Мало кто из вас пишет сайты с очень большой нагрузкой, на которых бы это снижение производительности стало критичным)
  • Использование таких классов повышает производительность программиста
  • При использовании софта, типа phpAccelerator падение производительности будет не таким заметным (и я не поверю, что популярные сайты не имеют возможности использовать такой софт)
  • Разработчики ADODB написали php-extension, который ускоряет работу класса (но работать можно и без него)

От себя могу добавить, что многие из написанных мною сайтов используют ADODB и проблем с производительностью не имеют.

2. Установка

Здесь все просто. Скачайте с http://php.weblogs.com/adodb архив и распакуйте его (например в папку ./adodb).

Все, класс готов к использованию.

Можете еще скачать и php-extension, но я его использовать не пробовал. Чтобы использовать класс, вам необходимо включить (include) файл ./adodb/adodb.inc.php

3. Простые примеры

Пример 1

<?php
// подключаем класс
include_once("adodb/adodb.inc.php");

// указываем тип БД
$conn = &ADONewConnection(‘mysql’);
// соединяемся с БД
$conn->Connect(‘localhost’, ‘root’, ‘password’, ‘scripts’);
// режим отладки&nbsp;— включен (true)
$conn->debug = true;
$conn->setFetchMode(ADODB_FETCH_ASSOC);
?>&lt;/php>

Данный пример демонстрирует подключение к БД. В строке

<?php $conn = &ADONewConnection('mysql'); ?>

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

Что касается режима отладки, то по умолчанию он выключен. При включенном режиме отладки на экран броузера будут выводиться SQL-запросы и тексты ошибок (если такие были). Очень упрощает процесс написания и отладки скриптов.

Метод $conn->setFetchMode() — указывает, каким образом данные о записях будут записаны в массив — будет ли это ассоциативный массив, или простой нумерованный или и тот и другой. Ей нужно установить одно из значений (0, 1, 2, 3). Для пояснений приведу код из исходников adodb:

<?php   define('ADODB_FETCH_DEFAULT',0);
define('ADODB_FETCH_NUM',1);
define('ADODB_FETCH_ASSOC',2);
define('ADODB_FETCH_BOTH',3);
?>

Судя по исходникам ADODB_FETCH_DEFAULT == ADODB_FETCH_BOTH

Теперь сделаем запрос к БД:

<?php
// делаем запрос к БД
$res = $conn->Execute("SELECT id, title, description FROM tab");
// если по запросу найдены записи в таблице
if ($res && $res->RecordCount() > 0) {
// выводим эти записи в цикле
while (!$res->EOF) {
echo "ID = ".$res->fields['id']."\n";
echo "title = ".$res->fields['title']."\n";
echo "description".$res->fields['description'];
// переходим к следующей записи
$res->MoveNext();
}
}
?>

Вот простейший пример запроса к БД. Метод $conn->Execute() выполняет запрос к базе данных и возвращает множество записей (recordset). Множество записей (recordset) — в ADODB является отдельным объектом, который имеет свои поля и методы для работы с полученными записями. Некоторые из них использованы в данном примере.

  • $res->EOF — равен true если обработаны все записи множества
  • $res->fields — хранит ассоциативный массив значений текущей записи
  • $res->RecordCount() — возвращает количество строк, полученных входе выполнения запроса
  • $res->MoveNext() — переходит к следующей записи (в массив $res->fields будет занесена следующая запись множества)

Метод $conn->Execute() — может быть использован для любых запросов:

<?php
// делаем вставку строки
$conn->Execute("INSERT INTO tab(name, value) VALUES ('name', 'ha ha ha')");
// получаем идентификатор вставки
// аналог mysql_insert_id();
$id = $conn->Insert_ID();

$conn->Execute(«DELETE FROM tab WHERE id = «.$id);
?>

Опишу еще некоторые полезные методы класса AdoConnection:

  • $conn->getRow($sql) — возвратит массив со значениями первой записи из всего множестве найденных записей.
  • $conn->getAll($sql) — возвратит 2-мерный массив со всеми найденными записями

4. Практическое использование

Думаю этот раздел будет наиболее интересен программистам.

4.1 Постраничный вывод и ограничение SELECT-запросов

Вообще-то не все базы данных умеют делать запросы типа: SELECT * FROM tab LIMIT 0, 10 а все те, которые умеют, делают это по разному:

MySQL:
SELECT * FROM tab LIMIT 0, 10
PostgreSQL:
SELECT * FROM tab OFFSET 0, LIMIT 10
FireBird:
SELECT FIRST 10 SKIP 0 * FROM tab

Класс adodb сам может делать ограниченные выборки, составляя правильные SQL-запросы под указанную БД, поддерживающую лимитированные SELECT-запросы

<? $res = $conn->SelectLimit("SELECT * FROM tab", 10, 0); ?>

Метод $conn->SelectLimit() сам построит правильный SQL-запрос. На основе этого метода в ADODB работают функции для постраничной выборки:

<?php
// определяем текущую страницу
$start = max(1, intval($_GET['start']));
// количество записей на странице
$rows_per_page = 10;
$res = $conn->PageExecute("SELECT * FROM tab", $rows_per_page, $start);

// получаем найденное количество записей
$records_amount = $res->MaxRecordCount();
?>

Метод $conn->PageExecute() кроме простого LIMIT-запроса делает автоматически еще и запрос типа: SELECT COUNT(*) FROM tab

Таким образом он сам узнает, сколько всего по данному запросу найдено строк. Это количество можно узнать с помощью метода: $res->MaxRecordCount();

Также для управления постраничным выводом есть следующие методы:

  • $res->AbsolutePage() — возвращает текущую страницу
  • $res->AtFirstPage() — возвращает true если текущая страница — первая
  • $res->AtLastPage() — возвращает true если текущая страница — последняя
  • $res->LastPageNo() — возвращает номер последней страницы

4.2 Генерирование INSERT/UPDATE запросов

Для начала пример:

<?php
// пример генерировани INSERT-запроса

// массив, который нужно вставить в таблицу
$frm = array(«field1″=>»value1″, «field2″=>»value2″);
// делаем пустой запрос
$res = $conn->Execute(«SELECT * FROM tab WHERE id = -1″);
// формируем SQL-запрос
$sql = $conn->GetInsertSQL($res, $frm);
// выполняем запрос
$conn->Execute($sql)

// пример генерирования UPDATE-запроса

// получаем данные о строке, которую нужно обновить
$res = $conn->Execute(«SELECT * FROM tab WHERE id = 17″);
$sql = $conn->GetUpdateSQL($res, $frm);
// выполняем запрос
$conn->Execute($sql)
?>

Так вот идея в том, чтобы все данные, которые нужно вставить записать в ассоциативный массив. Сделать запрос к БД чтобы получить имена полей таблицы и сконструировать SQL-запрос по этим данным.

Уверен, что будет много противников этого метода (мол лишний SQL-запрос к БД делвть), но мне эти функции кажутся очень удобными.

4.3 Работа с транзакциями

Ну это вообще сказка :) . Вот пример из мануала:

<?php
$conn->StartTrans();
$conn->Execute("update table1 set val=$val1 where id=$id");
$conn->Execute("update table2 set val=$val2 where id=$id");
$conn->CompleteTrans();
?>

Метод $conn->CompleteTrans(); сам проверит, были ли ошибки и если так  — сделает откат.

ADODB имеет еще и другие функции для работы с транзакциями, но они устарели и разработчики ADODB рекомендуют использовать этот вариант.

4.4 Последовательности

Часто при работе с таблицами каждой записи нужно присвоить уникальный идентификатор, который потом используется в качестве первичного ключа. Но не все СУБД поддерживают такую возможность. ADODB эмулирует эту возможность почти для всех СУБД. На практике это выглядит примерно так:

<?php
$uid = $conn->GenID('site_users');
$conn->Execute("INSERT INTO site_users(uid, login, password) VALUES
(".$uid.", '$login', '$password')");
?>

Метод $conn->GenID() создает последовательность site_users (если она до этого не была создана) и возвращает значение на единицу больше чем текущее значение последовательности.

4.5 Кеширование запросов

ADODB поддерживает серверное кеширование запросов. Суть в том, что при первом выполнении запроса его результаты заносятся в кеш-файл. При последующем таком же запросе (если кеш-файл не устарел) данные будут браться из файла.

Честно говоря, мне не нравится метод, которым они производят кеширование (по-моему они слишком уж универсальным сделали его) и предпочитаю делать кеширование своими руками.

Если вас все-таки интересует кеширование, то работает оно так:

<?php
$ADODB_CACHE_DIR = '/tmp/ADODB_cache';
$rs = $conn->CacheExecute('SELECT * FROM tab');
?>

По умолчанию время жизни кеш-файлов — 1 час. Это время можно изменить 2-мя путями:

<?
$conn->cacheSecs = 24*3600 // 24 часа
$rs = $conn->CacheExecute('SELECT * FROM tab');

// или так:
// время жизни кеша может задаваться первым параметром
// метода CacheExecute
$rs = $conn->CacheExecute(24*3500, ‘SELECT * FROM tab’);
?>

4.6 Статистика запросов.

Наверное видели на некоторых сайтах выводится статистика:

Страница сгенерирована за 0.0016 секунд. Запросов к базе данных — 12

Как вычисляется время генерирования страницы — к данной статье не относится, а вот посчитать количество запросов к БД (а также посчитать количество запросов, взятых из кеша) ADODB позволяет:

<?php
// пример взят из мануала
function CountExecs($conn, $sql, $inputarray) {
global $EXECS;
$EXECS++;
}

function CountCachedExecs($conn, $secs2cache, $sql, $inputarray) {
global $CACHED;
$CACHED++;
}

$conn = NewADOConnection(‘mysql’);
$conn->Connect(…);
$conn->fnExecute = ‘CountExecs’;
$conn->fnCacheExecute = ‘CountCachedExecs’;


// выводим статистику
echo «Всего запросов к базе данных: <b>».$EXECS+$CACHED.»</b><br />»;
echo «Из них взято из кеша : <b>».$CACHED.»</b>»;
?>

Данные функции вызываются до запроса, поэтому вы можете с их помощью переписать SQL-запрос.

5. ADODB & PEAR

Я являюсь фанатом как adodb так и репозитария PEAR.

К сожалению основным классом работы с базами данных в PEAR является PEAR::DB

И многие PEAR-классы используют его. Что же делать любителям adodb?

Во-первых, если хорошо присмотреться, то классов, использующих PEAR::DB не так уж и много. У меня почти весь pear-репозитарий на компьютере и там pear::DB используют лишь

  • DB::NestedSet
  • DB::DataObject
  • DB::Pager
  • DB::QueryTool
  • HTML::Select
  • XML::sql2xml
  • Auth
  • Cache
  • Log
  • LiveUser
  • Mail::Queue
  • Translation

Во-вторых, многие классы использую «контейнеры», и для этих классов можно написать контейнер, использующий ADODB (как писать контейнеры — смотрите на примере контейнеров pear::DB указанных классов).

В-третьих, ADODB имеет файл adodb-pear.inc.php который является эмуляцией класса PEAR::DB и остальные классы можно подогнать под работу с adodb с минимальными телодвижениями (часто достаточно в тексте класса строку     require_once(‘DB.php’); заменить на     require_once(‘adodb-pear.inc.php’); но так бывает не всегда).

Так что ADODB можно успешно применять с pear-классами. Приведу пример использования adodb c классом pear::XML::sql2xml. Для тех кто не в курсе — этот класс трансформирует результат запроса (SELECT) к БД в XML-строку:

<?php
require_once("adodb/adodb-pear.inc.php");
require_once("XML/sql2xml.php");

$db = DB::connect(«mysql://root@localhost/lot»);
$sql2xml = new xml_sql2xml();
$result = $db->getAll(«select * from lot_sessions»);
$xmlstring = $sql2xml->getXML($result);
?>

Те кто уже имеют опыт работы с XML_sql2xml наверное чаще применяют код, который предлагает автор класса XML_sql2xml:

<?php
require_once("XML/sql2xml.php");
$sql2xml = new xml_sql2xml("mysql://root@localhost/lot");
$sql2xml->Add("select * from lot_sessions");
$xmlstring = $sql2xml->getXML();
?>

и

<?php
$db = DB::connect("mysql://root@localhost/lot");
$sql2xml = new xml_sql2xml();
$result = $db->query("select * from lot_sessions");
$xmlstring = $sql2xml->getXML($result);
?>

Оба эти примера не сработают и нужно будет править класс XML_sql2xml.

6. Заключение

Поскольку статья носит ознакомительный характер, многое осталось «за кадром». Я не пытался описать все классы, поля и методы — для этого есть официальная документация. Также я не описывал функциональные возможности, которые не использовал на практике:

  • хранение сессий в БД (в том числе и зашифрованных сессий)
  • работа с хранимыми процедурами
  • работа с БД, находящейся на удаленном сервере
  • словари (позволяют программно создавать базы данных и таблицы)

Ссылки по теме

Автор: Maxim Matyukhin

Источник: http://www.php.su/articles/?cat=phpdb&page=018

Оставить комментарий

Чтобы оставлять комментарии Вы должны быть авторизованы.

Похожие посты