|
|
dkLab | Конструктор | DB_Pgsql_Type: прозрачное преобразование сложных типов PostgreSQL в PHP и обратно
2009-03-03
Хотите помочь с разработкой и улучшением библиотеки? Это можно сделать в социальной сети для OpenSource-разработок GitHub, построенной на основе системы контроля версий Git.
DB_Pgsql_Type это фреймворк для преобразования сложных типов PostgreSQL 8.3+ в их аналоги на PHP и обратно. С ее помощью вы можете работать с полями сложного типа (к примеру, двумерным массивом композитных типов) так же просто, как с привычными массивами PHP. Поддерживаются следующие типы данных и любые их вложенные комбинации:
Для чего это нужно?PostgreSQL славится своей поддержкой сложных типов данных. Например, вы можете определить столбец некоторой таблицы как двумерный массив строк: CREATE TABLE something( id INTEGER, matrix TEXT[][] ); INSERT INTO something(id, matrix) VALUES( 1, ARRAY[ARRAY['one','two'], ARRAY['three "3"','four']] ); Однако в PHP-скрипте при попытке получить значение из такого столбца:
вы увидите лишь строковое представление этих данных, нечто вроде: {{one,two},{"three \"3\"",four}}
DB_Pgsql_Type как раз и позволяет преобразовать выражения вида {{one,two},{"three \"3\"",four}} в двумерный массив PHP (с учетом особенностей квотинга спец-последовательностей: кавычек, апострофов, пустых строк, NULL и т. д.) Или обратно, если нужно записать двумерный массив в БД.
Как использовать библиотеку?Встроенные в PHP инструменты при работе со сложными типами возвращают данные "упакованными", в виде специально сформированных строк, которые мы далее будем называть "строковыми представлениями сложных типов". Выше мы уже встречались с примером такой "упаковки": {{one,two},{"three \"3\"",four}} для массива array(array("one", "two"), array('three "3"', "four")). Библиотека же позволяет собирать объекты для разбора/построения сложных типов, как из конструктора.
Для каждого типа данных Сложные типыНекоторые типы имеют сложную структуру, поэтому соответствующие классы принимают в конструкторах уточняющую информацию о структуре вложенных элементов. Тип "массив": ArrayТип массив DB_Pgsql_Type_Array принимает первым параметром
А вот так можно разобрать двумерный массив строк из первого примера этой статьи:
Можно обратно построить строку по массиву PHP для вставки в БД:
Тип "ROW": RowКласс DB_Pgsql_Type_Row позволяет разбирать данные типа "строка таблицы" (ROWTYPE) или, что практически то же самое, композитный тип.
Что примечательно, в PostgreSQL можно создавать массивы элементов композитного типа. Из разбор при помощи DB_Pgsql_Type не отличается от разбора массива любого другого типа:
Видите, структура типа строится из классов, как из конструктора, и может быть любой. Тип "ассоциативный массив": Hstore
Как водится, можно делать массивы типов hstore, либо же, наоборот, hstore композитных типов и массивов. Можно даже работать с двумерными хэшами (правда, внутри PostgreSQL хэш из величин произвольного типа заставляет СУБД преобразовывать данные в строки и обратно, что не очень выгодно сказывается на производительности). Простые типаПростые (или, как еще говорят, "скалярные") типы в DB_Pgsql_Type нужны для того, чтобы на их основе строить сложные. Некоторые из таких типов совсем тривиальны (например, строки и целые числа), другие же содержат в себе некоторую логику разбора (TIMESTAMP, DATE и т. д.). Тип "таймстэмп": TimestampПри работе с TIMESTAMP-ом в PHP удобно получать время в формате "Unix epoch time", т.е. число секунд, прошедших c 1 января 1970 года. Класс DB_Pgsql_Timestamp позволяет преобразовывать тип PostgreSQL TIMESTAMP в Unix epoch time и обратно.
Естественно, можно объявлять массивы таймстэмпов, композитные типы с таймстэмпами и т. д. Тип "дата": DateКласс DB_Pgsql_Type_Date позволяет разбирать и строить значения типа PostgreSQL DATE. Тип "время": TimeКласс DB_Pgsql_Type_Time используется для разбора типа TIME. Тип "булевский": BooleanВ PostgreSQL значния булевских типов записываются как 't' (true) и 'f' (false). Это порождает определенную путаницу в PHP, для которого 't' === true, но 'f' !== false. Класс DB_Pgsql_Type_Boolean преобразовывает 't' в true, а 'f' и еще некоторые значения, похожие на "ложь", в false. Типы Int, Numeric, StringИмеются также классы для разбора и построения (а точнее, для определения при создании сложных типов) совсем простых значений:
Модификаторы типовЧасто данные перед попаданием в БД (или, наоборот, после извлечения) должны проходить некоторую минимальную обработку. В DB_Pgsql_Type есть поддержка для некоторых наиболее частых операций; вы можете также написать свои собственные "классы-модификаторы". Отсечение пробелов: TrimВ подавляющем большинстве случае перед вставкой строковых данных в БД из них хотелось бы вырезать ведущие и концевые пробелы. Чтобы делать это автоматически, "оберните" тип DB_Pgsql_Type_String в объект класса Trim:
Пустая строка в NULLСледующая по популярности
Отсечение части времениКлассу DB_Pgsql_Type_Date можно передать первым параметром "критерий отсечения",
если вы знаете, что значение нужно сократить, к примеру, до месяца (отбросив день).
Например, Точно так же работают и сокращения для типа DB_Pgsql_Type_Time, только сокращать можно до минут (TRUNC_MINUTE) и часов (TRUNC_HOUR). Наконец, тип DB_Pgsql_Type_Timestamp поддерживает отсечение до минут, часов, дней, месяцев и лет. Возврат константы: ConstantЕсли вызвать метод output() у объекта класса DB_Pgsql_Type_Constant, то возвращаемое значение будет одно и то же вне зависимости от переданных параметров.
Зачем это нужно? Для формировани "заглушечных" данных в композитных типов, которые должны меняться вызывающей программой. В общем, это довольно сложно, но если вы вдруг столкнетесь, то сразу поймете, зачем оно. ВалидаторыИногда требуется ограничить диапазон значений или формат данных, которые подаются на вход методу output() классов DB_Pgsql_Type. Ограничение на длину строкиТип DB_Pgsql_Type_String принимает в конструкторе 2 необязательных параметра: мнимальная и максимальная разрешенная длина строки. В случае, если строка не удовлетворяет этим критериям, генерируется исключение.
Ту же самую функцию несет класс DB_Pgsql_Type_Length: он наклазывает ограничение по длине на тип, переданный в первом параметре конструктора:
Встроенные валидаторыНужно заметить, что некоторые ограничения уже встроены в типы Array, Row и Hstore: попытка передачи в них значений неверного формата приведет к исключению. Также типы Int и Numeric генерируют исключение, если им передается нечисловое значение. Тип Int дополнительно проверяет, что значение модет хранения в 32-битной ячейке памяти. Другие типы (Time, Date и т. д.) также проверяют переданные данные и генерируют исключения в случае несоответствия их формату. РезюмеСемейство классов DB_Pgsql_Type позволяет задействовать мощь разнообразия типов данных PostgreSQL в PHP-скриптах. Код классов покрыт юнит-тестами, поэтому вы можете использовать преобразователи данных без опасений. Зачем делать колонки сложных типов в БД? Давайте рассмотрим самый простой пример: массив целых чисел. Довольно удобно хранить отношение "многие ко многим" не в виде традиционной таблицы-связки, а в формате "массив идентификаторов":
Конечно, при этом мы теряем возможность использовать внешние ключи для ограничения целостности (впрочем, всегда можно реализовать эту проверку на триггерах, а в одной из следующих версий PostgreSQL, вероятно, сделают поддержку внешних ключей в полях-массивах). Но зато работа с такими данными сильно упрощается (с точки зрения сокращения письма). В ряде случаев удается достичь многократного прироста производительности (особенно с использованием расширений intarray или патча dkLab PostgreSQL patch). У такого метода "денормализации" есть, правда, и свои противники. Они советуют
"нормализовать все, что только можно". Что ж, их точка зрения заслуживает
уважения, но в каждом конкретном случае лучше все-таки смотреть по обстоятельствам,
какой метод удобнее и производительнее. Где-то хороша нормализация, а
где- |