DB.Schema
При разработке проектов возникают ситуации, когда в локальную базу данных вносятся какие-либо изменения. А потом забываются при переносе данных на хостинг. Что может приводить к сбою в работе сайта. Т.е. разработчику необходимо следить за этим. ТАО позволяет автоматизировать данный процесс.
Для настройки и автоматической синхронизации структуры базы данных можно использовать модуль DB.Schema, с помощью которого можно задавать схему базы данных в виде массива и автоматически ее синхронизировать. Разработчику не нужно пользоваться SQL-менеджерами и т.п. Структура базы данных задается в PHP-коде, и при заливке на хостинг она автоматически синхронизируется.
Как пользоваться
Основной порядок действий:
1. Заводим файл с именем Schema.php (app/components/NameComponent/lib/NameComponent/Schema.php).
2. В нем загружаем модуль DB.Schema. Заводим класс, который будет называться Component_NameComponent_Schema, он стандартно реализует интерфейс Core_ModuleInterface.
3. Внутри него достаточно определить один статический метод run(), в котором вызывать DB_Schema::process().
Последний метод и осуществляет изменение базы в соответствии с передаваемым описанием схемы данных. При первом запуске он будет создавать, а при последующих, если потребуется — менять схему.
// components/Redirector/lib/Redirector/Schema.php Core::load('DB.Schema'); class Component_Redirector_Schema implements Core_ModuleInterface { static function run() { DB_Schema::process(array( 'redirector' => array( 'mysql_engine' => 'MyISAM', 'columns' => array( array('name' => 'id', 'type' => 'serial'), array('name' => 'url_from', 'type' => 'varchar', 'length' => 255, 'default' => '', 'not null' => true), ... ), 'indexes' => array( array('type' => 'primary key', 'columns' => array('id')), array('name' => 'idx_from', 'columns' => array('url_from')), ... ), ), )); } }
Функция DB_Schema::process() имеет два параметра:
- Первый параметр - массив таблиц, в котором ключами являются имена таблиц, а значениями - массивы, описывающие структуру таблиц. Т.е. одним вызовом мы можем создать (синхронизировать) сразу несколько таблиц.
- Второй параметр - подключение к БД. Он не обязательный и имеет смысл только на проектах с несколькими подключениями.
После этого вызова:
- если не существовало таблицы, то она будет создана;
- если не было каких-то из указанных колонок, то они добавятся;
- если описание колонки изменилось, то будет произведен alter table для соответствующего изменения в таблице;
- если не было указанных индексов, то они создадутся.
Так же можно указывать действия для удаления:
'remove_columns' => array( array('name' => 'content'), array('name' => 'select') )
Аналогичное описание для индексов.
Такой метод несколько устарел, но его можно применять при необходимости.
Описание структуры таблицы
Структура таблицы задается массивом, который может содержать следующие ключи:
- description - описание таблицы (пока не поддерживается).
- mysql_engine - параметры специфичные для MySQL (например, MyISAM, InnoDB).
- columns - массив, каждый из которых описывает колонку (обязательные параметры name и type).
- description: описание,
- name: имя колонки,
- mysql_definition, pgsql_definition ... : строка определения колонки, если задано, то все последующие опции игнорируются.
- type' - тип. Может быть: 'char', 'varchar', 'text', 'blob', 'int', 'timestamp', 'datetime', 'date','float', 'numeric', 'serial'. Используйте 'serial' для auto incrementing колонок, для MySQL - это аналогично 'INT auto_increment'.
- mysql_type, pgsql_type, sqlite_type, etc. - тип, специфичный для конкретной БД.
- size - размер: 'tiny', 'small', 'medium', 'normal', 'big'. Аналогично TINYINT TINYTEXT и т.д. в MySQL.
- not null - по умолчанию false.
- default - значение по умолчанию для колонки.
- default_quote - помещать ли значение по умолчанию в кавычки. По умолчанию, если default строка, то true.
- length - длина для таких типов как 'char', 'varchar' or 'text'.
- unsigned - по умолчанию false.
- precision, scale - для типа 'numeric'.
- indexes - массив массивов описывающие индексы. Каждый индекс может содержать:
- name: имя индекса,
- type: тип: null, primary key, unique.
- columns: массив колонок входящих в индекс, каждая может быть строкой с именем колонки или массивом, первый элемент которого имя, а второй длина.
Кэшируемый запуск cached_run()
Вызов DB_Schema::process() обладает существенным недостатком: он медленный. И если его делать при каждой загрузке страницы, то сервер неизбежно ляжет, а хозяин сайта будет опечален.
Для этого предусмотрен метод CMS::cached_run(). Как видно из названия, это - кешируемый запуск. В качестве параметра в эту функцию передается имя модуля.
Происходит следующее: в первый раз система просто загружает модуль и запускает в нем статическую функцию run() (она обязательно должна быть). При этом в кеш записывается время модификации PHP-файла, содержащего модуль. При следующем вызове система проверяет дату модификации и сравнивает ее с той, которая сохранена в кеше. Если файл с прошлого раза изменился, то функция опять запускается, а дата в кеше обновляется.
Таким образом, после описания схемы в initialize() модуля Component.MyComponent произвести кешированный запуск:
CMS::cached_run('Component.MyComponent.Schema');
Использование событий
Перед изменением схемы или сразу после можно подписаться на события для внесений каких-либо изменений (например, добавить столбцы). Вносить изменения можно для конкретной таблицы. Для этого достаточно передать в параметре имя таблицы в соответствующем событии.
db.schema.execute($data) | db.schema.execute($data['name'], $data)
(../tao/lib/DB/Schema.php $data - массив DB.Schema)
Вызывается при обработке DB.Schema в методе execute() перед изменением данных. Позволяет добавить поля, столбцы в таблицу, внести какие-то изменения. Для конкретной таблицы необходимо указать ее имя в качестве аргумента ($data['name']).
- параметры: $data['name'] - имя таблицы
db.schema.after_execute($data) | db.schema.after_execute($data['name'], $data)
(../tao/lib/DB/Schema.php $data - массив DB.Schema)
Вызывается при обработке DB.Schema в методе execute() после изменения данных. Позволяет добавить поля, столбцы в таблицу, внести какие-то изменения. Для конкретной таблицы необходимо указать ее имя в качестве аргумента ($data['name']).
- параметры: $data['name'] - имя таблицы.
Хранение схемы в конфигурационных настройках
Описание структуры таблиц можно хранить в конфигурационных настройках компонента в файле /components/MyComponent/config/schema.php. Формат описания возвращаемого массива такой же, как и описание в методе DB_Schema::process().
// components/Delivery/config/schema.php return array( 'subscribers' => array( 'mysql_engine' => 'MyISAM', 'columns' => array( 'id' => array('type' => 'serial'), 'email' => array('type' => 'varchar', 'length' => 255, 'default' => '', 'not null' => true), 'idate_subscribe' => array('type' => 'int', 'default' => '0', 'not null' => true), 'idate_confirm' => array('type' => 'int', 'default' => '0', 'not null' => true), 'idate_off' => array('type' => 'int', 'default' => '0', 'not null' => true), ), 'indexes' => array( array('type' => 'primary key', 'columns' => array('id')), array('name' => 'idx_idate_confirm_off', 'columns' => array('idate_confirm', 'idate_off')), ), ), );
Достаточно описать схему, дальше система автоматически будет обращаться к ней.
CMS.Fields sqltype
При описании полей в качестве параметра можно передать массив schema.
$fields = array( 'coords' => array( 'type' => 'maps', 'schema' => array( 'columns' => array( 'lat' => array( 'type' => 'float', ), 'lon' => array( 'type' => 'float', ), ), 'indexes' => array( ... ) ) ), );
Но такой подход часто не удобен и достаточно громоздкий.
Для упрощения был введен параметр 'sqltype', который представляет из себя просто строку. Формат строки:
type[(length)] [null] [index][/parms] index: index[_name][(columns|length)] columns: column_name|column_name:length parms: parmname=value,parmname=value,...
Например,
'sqltype' => 'serial'
Здесь мы указали только тип, будет создано автоинкрементное поле и primary key. В качестве типа можно указать следующие:
Тип данных | Описание |
---|---|
int | Целое число. По умолчанию 0. |
tinyint | Очень маленькое целое число. Размер: tiny. |
char | Строка фиксированной длины, которая справа дополняются пробелами до указанной длины, при хранении. |
varchar | Строка переменной длины (конечные пробелы удаляются при сохранении). |
text | Текстовая строка. Может быть использована во всех 4 размерах: tinytext, text, mediumtext, longtext. |
blob | Массив двоичных данных. Может быть использован во всех 4 размерах: tinyblob, blob, mediumblob и longblob. |
time | Время. Хранит поле в виде «HH:MM:SS», но позволяет присваивать значения столбцам TIME с использованием строки или числа. |
datetime | Дата и время. По умолчанию формат: 0000-00-00 00:00:00. |
date | Дата. По умолчанию формат: 0000-00-00. |
serial | Автоинкрементное поле. Для MySQL это аналогично INT auto_increment. |
float/double | Число с плавающей запятой. По умолчанию: 0.0. |
timestamp | Дата и время. Диапазон от 1970-01-01 00:00:00 до ~2037 г. |
price | Для указания цены. Тип: numeric. По умолчанию: 0.00. |
numeric | Числа с точностью, указываемой пользователем. |
Правила описания типов
После типа можно указать длину поля в скобках.
'sqltype' => 'varchar(255)'
По умолчанию создаваемая колонка в базе всегда NOT NULL, если по какой-то причине необходимо разрешить нулевое значение, то достаточно добавить null в описание.
'sqltype' => 'varchar(255) null'
Через "/" можно указывать любые параметры допустимые в DB.Schema.
'sqltype' => 'int null/default=1'
В данном случае мы указали значение по умолчанию 1.
Для создания индекса по колонке достаточно добавить index в описание
'sqltype' => 'int index'
Имя индекса в таком случае формируется автоматически, если это не устраивает, то через знак _ можно указать свое имя.
'sqltype' => 'int index_my_index_name'
Тогда будет создан индекс с именем my_index_name.
В скобках можно указать длину индекса.
'sqltype' => 'text index(128)'
Если необходимо создать индекс по нескольким колонкам, то их можно перечислить в скобках.
'is_active' => array( ... ), 'status' => array( 'sqltype' => 'int index(status,is_active)' )
Если для каждой колонки еще необходимо указать и длину, то это можно сделать через двоеточие :.
'title' => array( ... ), 'body' => array( 'sqltype' => 'bigtext index(body:255,title:128)' )
Если в качестве типа поля указать empty, то колонка в базе создана не будет, это можно использовать для создания индексов в сложных случаях. Например, когда primary key составной
'id' => array( 'sqltype' => 'empty index_primary(user_id, group_id)', )
Если в качестве имени индекса указать primary, то будет создан primary key.
В CMS.Fields также доступен параметр sqltypes, который представляет из себя массив sqltype-ов. Это удобно, если значения поля необходимо хранить в нескольких колонках.
'coords' => array( 'sqltypes' => array( 'lat' => 'float', 'lon' => 'float', ) )