базовая обработка БД

речь пойдет об абстрактном классе base.class.php, который уже больше полутора лет я использую в своих разработках и постепенно совершенствую. Все началось с того, что в один прекрасный проект мне надоело повторять такое как:

$sql = "SELECT * FROM someTbl WHERE id=1";
$result = mysql_query($sql);
$list = array();
while ($row = mysql_fetch_assoc($result)) {
$list[] = $row;
}

вот так и появилась первая версия класса. Тогда он уже умел выбирать и возвращать матрицу по определенным критериям, а так же удалять строки изх таблицы по переданым ID…

Это было в прошлом. Теперь вернемся к настоящему и посмотрим как выглядит base.class.php нынче:
интерфейсный набор методов:

Перейдем к рассмотрению каждого и посмотрим его содержимое и практическую пользу, а так же рассмотрим общее взаимодействие методов класса на примере создания простейшей системы управления новостным блоком.
Итак. Все как обычно начинается с определения класса потомка, который будет манипулировать данными и должен приносить радость людям :) Это будут “НОВОСТИ”. Создадим класс:


class News extends Base {
protected $tblname = "news"; //обозначаем имя таблицы с которой будем работать, и объявляем свойство защищенным
}

впринципе это все что нам надо для создания серверной части новостей :) Собственно сам этот класс нам нужен для контейнера дополнительных методов отображающих специфические модели бизнесс-логики (вдруг понадобится к новостям подгужать картинки или что-то типа того - объявим доп. метод в классе News). Что теперь нам надо от кода? Ну для начала создадим файл. Пусть он называется news.php. И каталог. Для классов. “classes” рядом с файлом news.php. В каталог закидываем базовый класс base.class.php и создаем файл news.class.php, содержимое которого описано в предидущем листинге.
Определимся с задачей. Нам необходимо создать модуль новостей, который поддерживает следующие возможности:
- добавление новости (дата, заголовок, краткое содержание, полное описание)
- удаление новости
- смена статуса новости с активного на неактивную и наоборот.
Также хотелось бы видеть список всех новостей для редактирования в виде таблицы и с делением по страницам, а так же с возможностью фильтрации по некоторым параметрам и/или сортировкой по столбцам.
Что делаем дальше? создаем таблицу в БД (MySQL). сценарий вот:

CREATE TABLE `news` (
`id` int(10) unsigned NOT NULL auto_increment,
`title` varchar(100) NOT NULL,
`date` date NOT NULL,
`anounce` text,
`content` text,
`status` int(10) unsigned default NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB;

ну а теперь приступим к разработке административной части. Открываем наш файл news.php:

require_once "classes/base.class.php";
require_once "classes/news.class.php";

$news = new News();
?>

что можно сделать дальше? смотрим по возможностям класса:
1. мы можем добавить новость:
$id = $news->addRecord(array(’title’ => ‘Заголовок’, ‘anounce’ => ‘Анонс новости’, ‘content’ => ‘Полное содержание новости’, ‘date’ => ‘2007-03-14′));
Этот метод добавит одну запись в нашу таблицу. Вызвать метод логичнее всего на какое-то внешнее событие пришедшее из HTML формы клиента.
Также в переменную $id мы получили порядковый номер записи в таблице БД соответствующей этой новости

2. обновить запись:
$news->saveRecord(array(’title’ => ‘Новый заголовок’, ‘date’ => ‘2007-03-13′), $id);
Тут мы обновили данные о нашей толькочто добавленой записи, которая вернула порядковый номер $id. В данном случае мы указали что надо изменить заголовок и дату записи, но менять можно любое кол-во полей и в любом порядке.

3. удалить запись:
$news->deleteRecord($id); (альтернатива: $news->deleteRecord(array($id1, $id2, $id3, …)) )
удаляем запись по $id… ну или сразу группу записей, переданых массивом

4. установить возможность постраничного вывода результирующих данных:
$news->setPager(30, @$_REQUEST['page']);
устанавливает вывод коллекции по 30 записей на страницу и передает значение текущей страницы (пока что передаем имено значение $_REQUEST['page'])

5. установить варианты сортировки результирующих данных:
$news->setOrder(’date’, ‘descr’);
тут все понятно - сортируем по дате по убыванию… первое - название столбца. второе - порядок сортировки. варианты: ASC/DESC, 1/0

6. устанавливаем параметры фильтрации результирующих данных:
$news->setFilter(’date’, ‘2006-01-01′, ‘>’);
только новости от 2006-01-01

$news->setFilter(’content’, ‘NULL’);
только где содержимое новости пустое

$news->setFilter(’status’, ‘1′);
где статус равен еденице

Так же все эти фильтры можно комбинировать указав последовательно и таким образом получать нужный эффект.

7. Получить результирующие данные:
$list = $news->getList();

в итоге в переменной $list оказывается матрица значений из таблицы, которые попали под условия установленых фильтров, в кол-ве указаном при установке постраничного вывода и отсортированые в прядке, указанном для сортировки.
Так же в метод можно передать массив полей, которые нужны в результате (по-умолчанию возвращаются все).


хочу заметить что вызов методов для подготовки результирующих данных не есть обязательными и могут комбинироваться в любой последовательности. стоит учесть что все они должны быть указаны ДО вызова метода getLst или viewPager (кстати о нем)…

метод viewPager возвращает сформированную HTML строку с ссылками на набор страниц данных…

Вот собственно и весь экскурс в базовый класс.
Будут вопросы - пишите на azazel.tap@gmail.com в аську 298096 или сюда :)
Все пожелания и предложения буду учитывать. Фразы о двухколесных средствах передвижения можете оставлять при себе, так как расцениваться это будет как зависть :)

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


З.Ы. На винте уже лежит версия 4, в которой переписаны некоторые метоы, а так же добавил метод setJoin, который позволяет объеденять любое кол-во таблиц для результирующего сета данных. в данный момент метод проходит тестирование…


 Версия 4: changelog

выложил в общий доступ новую версию базового класса. как и предполагалось - основным изменением стало появлением метода setJoin что позволяет теперь с легкостью связывать любое кол-во таблиц с отношениями 1:n.

пример использования:

setFilter(’test_id’, ‘asd’);
$a->setFilter(’test_id’, ‘asd23′);
$a->setJoin(’tbl2′, ‘tbl1′, ‘field1′, ‘field2′, array(’field3′));
$a->setJoin(’tbl4′, ‘tbl1′, ‘field4′, ‘field3′, array(’field5′));
$a->setJoin(’tbl5′, ‘tbl4′, ‘field6′, ‘field7′, array(’field8′));
$a->setJoin(’tbl6′, ‘tbl5′, ‘field8′, ‘field9′, array(’field8′));
$a->getList(array(’myfield’), 1);
?>

Результат в браузере при $doDebug = 1

Error: Table 'test.tbl4' doesn't exist
ErrNo: 1146
SQL:

SELECT t1.myfield, t2.field3, t3.field5, t4.field8, t5.field8
FROM tbl1 AS t1
LEFT JOIN tbl2 AS t2 ON t2.field1 = t1.field2
LEFT JOIN tbl4 AS t3 ON t3.field4 = t1.field3
LEFT JOIN tbl5 AS t4 ON t4.field6 = t3.field7
LEFT JOIN tbl6 AS t5 ON t5.field8 = t4.field9
WHERE t1.test_id = 'asd'
	AND t1.test_id = 'asd23'

Time: 0.000589 s

 Версия 5: changelog

  1. в метод setFilter добавлен необязательный параметр $type, который содержит логику фильтрации.
  2. класс переименован из Base в Db_Bicycle для уникальности и раскрытия сущности
  3. добавлено свойство error_handler, которое может содержать имя пользовательской функции для перехвата и обработки ошибок. Функции передается в качестве аргумента строка запроса и номер ошибки. по-умолчанию выключена и использует встроенный обработчик.
  4. исправлен метод addRecord. Теперь можно передавать параметры с MySQL функциями (TODO. возможна некорректная работа)
  5. введена нормальная документация
  6. добавлено свойство do_debug для вкл./выкл. глобальной отладочной информации
  7. из метода getList() удален агрумент doDebug
  8. метод setFilter() позволяет устанавливать условия для связываемых таблиц (добовлен один аргумент)

Скачать базовый класс