- Подробности
-
Категория: PHP. Базы данных
Подробное описание абстрактного класса баз данных ADODB. Установка, примеры использования, особенности, ADODB & PEAR.
1. Пару слов об ADODB
Для начала, скажу что статья рассчитана на программистов, имеющих опыт работы с СУБД, а не на начинающих пхпешников. Я предполагаю, что вы знакомы с PHP, ОПП, SQL и имеете опыт разработки web-приложений.
ADODB — это абстрактный класс доступа к базам данных, написанный на PHP.
Для тех, кто в танке поясню на примере.
Предположим вы написали скрипт под mysql. И тут заказчик говорит Вам, что хостинг меняется и там есть только PostgreSQL. Если вы не использовали класс абстрактного доступа к базам данных, то вам пришлось бы:
- заменить весь код работы с mysql на postgresql
- переписать SQL-запросы (так как есть отличия)
Если бы вы использовали абстрактный слой доступа к БД, то вам скорее всего не пришлось бы менять php-код (только в одном месте указали бы что используете postgresql) и изменить SQL-запросы (хотя иногда и это не понадобилось бы).
Я намеренно в этом описании использовал фразу "абстрактный класс доступа к БД",
поскольку ADODB — не единственный подобный класс. Наиболее известные конкуренты:
Насколько я знаю, другие классы имеют слабую функциональность, хотя должен признать, работают быстрее. Многие пишут такие классы сами, но я не сторонник изобретения велосипедов.
Противники таких массивных классов, как 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');
// режим отладки — включен (true)
$conn->debug = true;
$conn->setFetchMode(ADODB_FETCH_ASSOC);
?></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->EOFtrue
- $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. Заключение
Поскольку статья носит ознакомительный характер, многое осталось "за кадром". Я не пытался описать все классы, поля и методы — для этого есть официальная документация. Также я не описывал функциональные возможности, которые не использовал на практике:
- хранение сессий в БД (в том числе и зашифрованных сессий)
- работа с хранимыми процедурами
- работа с БД, находящейся на удаленном сервере
- словари (позволяют программно создавать базы данных и таблицы)