Engine v3

Случайный афоризм:
Добрые никогда не говорят правды. Быть таким добрым — болезнь.

Ф. Ницше. «Так говорил Заратустра»

Engine v3

Q1. Что это такое и для чего оно нужно?

A1. Это база данных с методами для работы с ней, расчитанная на хранение страниц сайта.

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

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

Q2. А можно кратко об основных концепциях ENGINE-a?

A2. Конечно.

Положение 1: В базе хранится дерево объектов. Каждый объект имеет родителя, таким образом они выстраиваются в древовидную структуру на сайте, и благодаря этому не нужно для каждого объекта хранить полный путь от корня сайта. Если мы изменили путь у объекта родителя, то и у него и у его детей автоматически изменится полный путь на сайте и нам не нужно ничего больше менять.

Положение 2: У каждого объекта есть фиксированный (но изменяемый) набор основных полей. Именно эти поля определяют основную функциональность объекта. Это то, что может быть специфично для сайта и то, над чем в первую очередь нужно думать, создавая таблицы (в первую очередь OBJECT). Основные поля обязательны, без них ENGINE жить не будет, а дополнительные мы можем создать описав их в файле _objects_additional_fields.cfg и ENGINE будет доставать данные из этих полей. Соответственно из обработчиков можно будет работать с ними.
Этот фиксированный набор работает существенно быстрее, чем динамические блоки данных, и об этом нужно помнить!

Положение 3: К каждому объекту мы можем привязать обработчик (хоть для каждого объекта свой!) который представляет собой парсерный код и который знает что нужно сделать с объектом чтобы сформировался его xml. По умолчанию, если не привязан никакой обработчик, результирующий xml объекта формируется из xml всех блоков привязанных к объекту (без установленного флажка: «только при ручном вызове») + xml всех полей объекта + xml блоков объекта «данные о сайте».

Положение 4: К каждому объекту мы можем привязать xsl шаблон (хоть для каждого объекта свой!) который знает как преобразовать пришедший от обработчика страницы xml и сформировать окончательный результат (обычно html).

Положение 5: К каждому объекту мы можем привязать любое количество динамических блоков, мы может определить функциональность этих блоков с помощью написанных для них обработчиков (парсерный код). Обработчик блока знает что нужно сделать с данными содержащимися в блоке, перед складированием их в общую пачку xml-данных объекта.

Положение 6: Для хранения дополнительных файлов объекта (например картинки меню, фотографии, описания, скачиваемые файлы и т.д.) используется структура на диске аналогичная структуре дерева сайта и загруженный файл попадает в каталог, который для каждого объекта свой.
Эти файлы нигде в базе данных не регистрируются! Это позволяет использовать альтернативный метод работы с файлами (ftp), позволяет легко найти/заменить/добавить/удалить файл(ы).

При изменении пути объекта в дереве файлы автоматически перемещаются на нужное место. Для работы с этими файлами есть методы, которые достают файы по имени, по маске, которые достают список файлов и т.д. При этом не нужно указывать пути, т.к. пути для данного объекта они всегда однозначно определены.
При этом когда мы пишем в блоках данных xml, нам не нужно писать там парсерные конструкции (да и не будут они там работать по умолчанию), достаточно использовать специальные теги, которые автоматически будут обрабатываться.

Q3. Это наверное жутко навороченная и огромная структура, заранее заточенная под множество целей? Можно посмотреть структуру базы данных?

Структура БД

Q4. Можно кратко прокомментировать таблицы базы и поля?

A4.
SITE:
В этой таблице хранится список сайтов, для которых в этой базе данных хранятся деревья объектов.
  • NAME - название сайте
  • URL - url сайта
  • CACHE_TIME - время кэширования страничек для этого сайта «по умолчанию»
  • SITE_ROOT_DIR - путь к DOCUMENT_ROOT сайта относительно DOCUMENT_ROOT сайта администрирования. Нужно для управления из одного интерфейса несколькими сайтами
  • SORT_ORDER - порядок сортировки списка сайтов (например в админе или в подписке на новости)

OBJECT_TYPE:
В этой таблице хранится список типов объектов, например:

  • container (id=1)
  • leaf (id=2)
  • global (id=3). В объекте данного типа хранится глобальная для данного сайта информация (например текст футера)
  • fake (id=4). Фиктивный объект, не имеющий своего представления на сайте.
  • ... На ваш выбор. Вводите новые типы и используйте их.
  • NAME - название типа объекта
  • ABBR - символьная аббревиатура типа
  • TEMPLATE_ID - шаблон, который будет привязываться к вновь создаваемым объектам данного типа
  • DATA_PROCESS_ID - обработчик, который будет привязываться к вновь создаваемым объектам данного типа
  • CONSTRUCTOR - инструкции, по которым будет происходить создание объектов данного типа
  • SORT_ORDER - порядок сортировки типов объектов в админе

OBJECT:
В этой таблице хранится информация об объектах (страницах сайта).

  • NAME - имя объекта
  • WINDOW_NAME - title страницы (если не определен, берется NAME)
  • DOCUMENT_NAME - заголовок (h1) страницы (если не определен, берется NAME)
  • PATH - путь объекта. Это не полный путь от корня, а кусочек пути. Если у объекта нет пути, он не может быть отображен на сайте (например, если это фиктивный объект или сервисный центр, у которого есть параметры, но который не имеет собственной страницы для отображения не сайте).
  • SITE_ID - определяет сайт, которому принадлежит объект
  • OBJECT_TYPE_ID - тип объекта
  • PARENT_ID - «родитель» в дереве (если объект расположен в корне дерева, то parent_id = 0)
  • TEMPLATE_ID - xsl-шаблон, который будет использован при отображении объекта
  • DATA_PROCESS_ID - обработчик, который будет использован для обработки xml данных объекта перед применением xsl шаблона
  • IS_PUBLISHED - показывать объект на сайте или нет
  • IS_SHOW_IN_MENU - показывать объект в меню или нет
  • IS_SHOW_ON_SITEMAP - показывать объект на карте сайте или нет
  • URL - нужен, если объект в дереве есть на самом деле есть ссылка на страницу, расположеную на другом сайте
  • LINK_TO_OBJECT_ID - ссылка на другой объект. Если у объекта указана ссылка на другой объект, то при обращении к этому объекту будет показана информация объекта, на которого ссылается данный объект
  • CACHE_TIME - определяет время жизни кэша для данного объекта
  • CONSTRUCTOR - инструкции, по которым будет происходить создание «дочерних» объектов
  • AUSER_ID - владелец объекта (для разделения доступа)
  • IRF - маска наследуемых прав (для разделения доступа)
  • THREAD_ID - object_id корневого объекта, заполняется автоматически. Нужно для увеличения быстродействия.
  • NESTING - на каком уровне находится объект. заполняется автоматически. Нужно для увеличения быстродействия некоторых специфических операций
  • UPDATE_TIME - дата/время последней модификации объекта, не редактируемое поле
  • FULL_PATH - полный путь объекта, не редактируемое поле, заполняется автоматически. Нужно для увеличения быстродействия.
  • EDITOR - IP и HOST того, кто последним редактировал объект, не редактируемое поле
  • SORT_ORDER - определяет порядок следования объектов на одном уровне
  • ... - да, именно многоточие. Структура определяется детально для каждого конкретного случая, но engine уже будет уметь работать с ней, т.к. мы опищем дополнительные поля в файле _objects_additional_fields.cfg

TEMPLATE_TYPE:
В этой таблице хранится список типов шаблонов. От типа шаблона зависит что можно с ним делать. Например, шаблон документа можно привязать к объекту, а «вспомогательный шаблон» можно только видеть/редактировать в админе и делать import из других шаблонов.

  • NAME - название
  • SORT_ORDER - порядок сортировки

TEMPLATE:
В этой таблице хранятся шаблоны для отображения объектов. Шаблон не имеет понятия о том, какие xml-данные ему придется отображать, он не хранит их. Он знает что нужно сделать с xml-данными, которые ему передают.

По сути шаблон есть файл на диске, а в базе хранится только ссылка на этот файл.

  • TEMPLATE_TYPE_ID - тип шаблона.
  • NAME - название шаблона. Оно показывается в списке, и по нему ориентируются, когда выбирают шаблон
  • DESCRIPTION - описание шаблона. Комментарий, но он очень важен, чтобы не приходилось открывать файл с шаблоном и из кода выяснять, что делает тот или иной шаблон.
  • FILENAME - имя файла, который лежит в каталоге /templates/
  • UPDATE_TIME - дата/время последней модификации шаблона
  • SORT_ORDER - порядок сортировки

TEMPLATE_GROUP:
В этой таблице хранятся группы шаблонов. Фактически имеется возможность одни и те-же данные преобразовать в разное визуальное представление. Например, одни и те-же данные могут быть выданы в виде обычной html версии, в виде версии для печати или в версии для WAP броузеров.

Это делается с помощью групп шаблонов.

  • NAME - название
  • DESCRIPTION - описание
  • DIRECTORY_NAME - имя подкаталога в каталоге /templates/ в котором лежат шаблоны данной группы
  • SORT_ORDER - порядок сортировки

DATA_PROCESS_TYPE:
В этой таблице хранится список типов обработчиков. От типа обработчика зависит, что он может делать. Например, обработчик страницы знает как и из чего нужно сформировать данные страницы перед xsl-трансформацией, а обработчик блока знает что нужно сделать с данными блока перед помещением их в общую кучу xml-данных страницы.

  • NAME - название типа (например: Обработчик страницы, Обработчик блока)
  • SORT_ORDER - порядок сортировки

DATA_PROCESS:
В этой таблице хранятся обработчики объектов и блоков: код на парсере. Обработчик не имеет понятия о том, как будут показаны данные, но он знает как их нужно модифицировать. Один обработчик эти данные пропустит через типографику, а другой - на основании переданных ему данных определит заголовки страницы и т.д.

Обработчик есть файл на диске, а в базе данных хранится только ссылка на этот файл.

  • DATA_PROCESS_TYPE_ID - тип обработчика. нужно фактически только для внутренних целей. Чтобы обработчик страницы показывать там, где нужно выбрать обработчика блока и т.д.
  • NAME - название обработчика. Оно показывается в списке, и по нему ориентируются, когда привязывают обработчик
  • DESCRIPTION - описание обработчика. Комментарий, но он очень важен, чтобы не приходилось открывать файл с обработчиком и из кода выяснять, что делает тот или иной обработчик.
  • FILENAME - имя файла, который лежит в каталоге /processes/ и где хранится код обработчика.
  • UPDATE_TIME - дата/время последней модификации кода обработчика
  • SORT_ORDER - порядок сортировки обработчиков

DATA_TYPE:
Описание типов данных, которые бывают в блоке. Используется только в административном интерфейсе, чтобы при редактировании разных данных иметь разные возможности.

  • NAME - название типа данных (например: Дата, Число, Строка, Текст с типографикой)
  • DESCRIPTION - описание типа данных блока
  • EDIT_SCRIPT - скрипт, который знает, как надо В АДМИНЕ вывести данные блока для редактирования
  • UPDATE_SCRIPT - скрипт, который знает, как надо В АДМИНЕ сохранить в базе данные блока после редактирования
  • SORT_ORDER - порядок сортировки

BLOCK:
Самое интересное. Все данные, которые мы можем выделить из документа по каким-либо критериям есть блоки, принадлежащие объекту.

  • DATA - тут лежат сами данные, вне зависимости от их типа. Это может быть xml, текст, парсерный код (автоматически процессится не будет) и т.д.
  • DATA_TYPE_ID - тип данных в этом блоке. Определяет, как именно в админе будет редактироваться содержимое данного блока.
  • NAME - название блока. Именно по названию мы обращаемся к блокам из обработчика и именно его имя принимает участие в формировании xml этого блока! Существуют групповые операции для блоков («вытащить все блоки начинающиеся на main и обработать их», операции с одним блоком: «достать блок с именем main и обработай его», операции извлечения блоков: «дай мне список блоков текущего объекта», «дай мне список блоков вот этого объекта» и т.д.)
  • DESCRIPTION - описание блока. Например, этот блок для объекта новость называется «тело новости», и в админе он будет виден как «тело новости», а для обращения он будет иметь имя, ну например, body.
  • IS_PUBLISHED - показывать блок на сайте или нет. Например для временного отключения блока.
  • IS_SHARED - блок может быть использован другими объектами, не редактируемое поле.
  • IS_HIDE_PUBLISHED - Флажок для запрещения отображения в админе поля IS_PUBLISHED. Например, мы знаем, что у новостей не нужно вовсе ставить рядом с каждым блоком флажек is_published.
  • IS_PARSED_MANUAL - блок будет обработан только при ручном вызове его из обработчика объекта или другого обработчика блока
  • IS_NOT_EMPTY - 1, если поде data не пустое. Нужно для ускорения обработок, не редактируемое поле
  • UPDATE_TIME - дата последней модификации данных в блоке.
  • TEMPLATE_ID - атавизм, оставшийся от предыдущей версии. не используется.

BLOCK_TO_OBJECT:
Связи определяющие какие блоки принадлежат каким объектам.

  • SORT_ORDER - порядок следования блоков. Например, к объекту может быть привязано 5 блоков с одинаковым именем, и когда мы вызовет обработку блоков с этим именем, то все они правильно обработаются в заданном порядке. Т.к. поле для сортировки находится в таблице BLOCK_TO_OBJECT то два shared блока могут быть отсортированы по разному для разных объектов.

BLOCK_TEMPLATE:
Заготовки блоков. Если нам постоянно требуется создавать блок 'text' с описанием 'содержимое страницы' и установленными обработчиками или иными флажками, то удобно создать заготовку блока, и можно будет создавать блоки на её основе. Все её поля копируются в соответствующие поля таблицы BLOCK. После создания блока на основании заготовки, блок и заготовка никак больше друг с другом не взаимодействуют.

  • DATA
  • DATA_TYPE_ID
  • NAME
  • DESCRIPTION
  • IS_PUBLISHED
  • IS_SHARED
  • IS_HIDE_PUBLISHED

Q5. Тут что-то упоминалось про CACHE_TIME, но получается, что для того, чтобы определить время кэширования объекта нам уже нужно лезть в базу? А подключение к БД достаточно медленная операция. Разве это хорошо?

A5. Да, несомненно, это плохо, именно поэтому ENGINE не использует такой метод :)

CACHE_TIME используется для хранения этой информации, и на основании неё при любой модификации объектов в БД автоматически создается cfg-файл. А вот уже на основании этого cfg-файла работают механизмы кэширования, при этом даже не подключаются основной класс engine (который занимает ~ 55 КБ).

Q6. Все это здорово, но не говорите, что может быть удобно редактировать новости компании и, например, список дилеров одним механизмом. Да, он может быть универсальным, но при этом он не будет удобным для всех случаев.

A6. Согласен. Поэтому следует помнить о следующих вещах:
  1. Все таки всегда можно все редактировать из основного средства редактирования, которое универсальное, но не всегда удобное
  2. Основное средство редактирования включает в себя конструкторы, которые существенно облегчают управление данными. Если у нас в разделе news лежат новости, и после создания объекта-новость мы должны создать 4 блока с определенными именами/описаниями, то мы можем определить это в конструкторе родителя, и после создания нового объекта он сразу будет содержать эти поля, и иметь необходимые параметры объекта (обработчик, шаблон, тип...)
  3. Можно создать специализированные средства редактирования, которые могут управлять содержимым частей дерева и выполнять там специфические вещи, скрывая от редактора такие операции как создание блоков объекта, задание их типов и т.д.
  4. Не стоит забывать, что ENGINE в первую очередь - структура данных и набор методов для работы с ними, что для клинических случаев можно написать отдельный модуль, который будет уметь работать с другими структурами даннах, но данные все равно можно хранить в общем дереве, или можно не хранить их там, а обращаться к ним из обработчиков.

Q7. И что? Прямо все так круто и нету недостатков?

A7. Конечно есть! Во первых это все-таки медленный доступ к данным, обусловленный их блочной структурой. Это отчасти решается кэшированием и внутренней реализацией ENGINE-а (вытаскивание всех таблиц template и data_process, а не постоянные join к ним, кеширование шаблонов, обработчиков, объектов, блоков, компиляция обработчиков...), но все равно для работы с интенсивно обновляемыми данными следует искать другой путь. Во вторых... А хватит. Недостатков можно придумать много, как многие из них можно обойти, если все делать с умом. Следует не забывать, что ENGINE может работать на сайте рядом с любыми другими системами доступа к данным.

Кроме этого в новой версии было добавлено несколько новых и очень востребованных механизмов, а также значительно улучшен административный интерфейс.

P.S. На самом деле не все так плохо, для построения типовой страницы производится не более 7-8 запросов к БД (3 запроса - join из 2 таблиц, остальные запросы достают данные из одной таблицы). Все запросы используют индексы и выполняются достаточно быстро.

Краткое описание доступных механизмов engine-а:

  1. Посмотреть выдаваемые xml объекта можно следующим образом: <censored>
  2. Посмотреть неопубликованный документ можно так: <censored>
  3. Получить debug информацию можно так: <censored>
    При этом в <censored>/sql.txt запишется информация обо всех sql запросах, которые были сделаны при формировании страницы с указанием времени их выполнения и количеством использованной ими памяти.
  4. В админе есть механизмы, позволяющие выполнять бакап БД, проверять её на наличие ошибок и автоматически исправить некоторые из них.
  5. В админе есть механизм, позволяющий копировать объекты с разными опциями (копировать только параметры объекта, параметры объекта + структуру блоков, параметры объекта + структуру блоков с их содержимым)
  6. В админе не производится загрузки сразу всех объектов и отображения дерева сайта целиком. Куски дерева подгружаются по мере необходимости (загрузка xml), таким образом управлять структурой из 5000 страниц не намного медленнее, чем из 50 страниц.
  7. Сортировка объектов. Объекты показываются на сайте в том виде, в котором они видны в админе. А отсортированы они могут быть примитивным drag & drop-ом.
  8. Кеширование страниц. Страницы кешируются автоматически. Не кешируются страницы, если на них идет отправка данных методом POST или если установлено время кеширования 0 (логику несложно изменить)
  9. Кеширование шаблонов/обработчиков. Данные о зарегистрированный с системе шаблонах кешируются на диске (используется механизм кеширования запросов, встроеный в sql классы). Кроме этого информация обо всех шаблонах/обработчиках разом загружается в память (но не код), таким образом спасаемся от большого количества join-ов.
  10. Компиляция обработчиков. При обработке блока, к которому привязан обработчик происходит компиляция текста обработчика и при обработке последующих блоков, к которым привязан этот обработчик используется уже скомпилированный код и не происходит повторный process текста обработчика. Это существенно ускоряет обработку.
  11. Кеширование обращения к объектам/блокам. Если у нас обработчик обращается к одному блоку 5 раз, то этот блок достается из базы данных только один раз. Более того, при обращении к первому блоку объекта достаются и запоминаются все его блоки (одним запросом). Обращения к последующим блокам не производят запросов в БД. То-же самое касается объектов: если мы достаем объекты какого-либо треда, а потом независимо, в другом обработчике достаем данные объекта, который уже был в этом треде - второго запроса к БД не будет.
  12. Ahead кеширование блоков. При вызове типовых методов, в некоторых случаях сразу после доставания из БД объектов происходит доставание всех их блоков. Этот механизм также дает большое сокращение запросов к БД.
  13. Типы данных блоков. Если нам нужен блок, в котором будет храниться всего-лишь одна строка, не имеет смысла смотреть на эту строку в textarea. Можно указать у блока при его создании тип - строка, и поле для редактирования будет занимать меньше места. При копировании/создании конструктора тип блока будет учтен.
  14. Есть такое понятие, как «shared блок», это блок, использующийся одновременно двумя или более объектами. Физически, в базе хранится одна копия блока, и изменение этого блока у одного объекта приводит к его изменению во всех местах. Иногда требуется, чтобы одинаковые данные присутствовали у нескольких объектов, тогда решение, лучшее чем copy/paste - это shared блок. В этом случае, при исправлении данных нам надо править их в одном месте. Тело этого блока в админе показывается на «сереньком фоне», и при onfocus выдается предупреждение, что это shared блок (можно отключить). Следует помнить, что обработка shared блока осуществляется в контексте объекта, из какого вызывается блок. Например, если в блоке есть конструкция <file alias="aaa.pdf" ..., и блок принадлежит объектам А и Б, то для корректного вывода ссылки на файл он должен присутствовать у обоих объектов.
  15. Конструкторы. Они созданы для облегчения создания новых объектов. Это - некие шаблоны. Например, если у объекта А есть конструктор, то когда мы создаем у этого объекта ребенка, выполняется этот конструктор и ребенок получает все параметры, которые описаны у конструктора родителя. Если у объекта А нет конструктора, но у нас определен конструктор для объекта типа leaf, то в случае, если мы создаем ребенка-container у объекта А, то блоки к этому объекту приделаны не будут, а если создаем ребенка-leaf - будут. Конструктор родительского объекта полностью перекрывает конструктор типа объекта.
  16. В любом блоке по умолчанию происходят следующие преобразования:
    <img alias="aaa.gif".../> -> <img src="/off-line/.../aaa.gif" width="xx" height="xx" .../> (если файла не существует - тег вставлен не будет)
    <file alias="aaa.zip".../> -> <file src="/off-line/.../aaa.gif" ext="" size="размер файла" icon="/i/icons/exe.gif" icon-width="xx" icon-height="xx" .../>
    <a alias="aaa.gif">xxx</a> -> <a href="/off-line/.../aaa.gif/">xxx</a> (если файла не существует - ссылка поставлена не будет)
    <a object-id="123">xxx</a> -> <a href="/Путь_К_Объекту_с_object-id/">xxx</a>
    <a object-id="123"/> -> <a href="/Путь_К_Объекту_с_object-id/">Название объекта с object-id</a>
    [FORM|ENV|REQUEST] -> добавит в заданое место блока значения, пришедшие из $form, $env или $request
  17. Если в админе указать у объекта URL («Ссылка на другой сайт»), то при выдачи информации о дереве объектов (<branches ..>) будет выдан не реальный URI этого объекта на данном сайте, а тот, что указан в этом поле
  18. Если в админе указать «ссылку на объект», то в случае обращения к этому объекту выдастся информация не по этому объекту, а по тому, куда указывает ссылка, при этом дерево сайта будет показывать, как будто мы находимся в контексте запрошенного объекта.
  19. По умолчанию, при обращении к странице, выдаются следующие данные (xml): текущая дата (info/date), информация о посетителе (info/client), все поля текущего объекта (document), информация о корневых объектах и текущей ветке объектов (<navigation><branche .../>...<navigation>), и данные обо всех блоках, привязанных к объекту (если у объекта есть блоки body и title, то будет выдано: <body>содержимое body</body><title>содержимое title</title>)
  20. Для того, чтобы обработать блок текущего объекта в обработчике нужно написать ^parseBlock[имя блока] при этом, если у нас существует несколько блоков с заданным именем, то обработаются они все в последовательности, заданной в админе.
  21. Если нам нужно обработать блок другого объекта, нужно написать:
    ^parseBlock[имя блока;ссылка на другой объект]
  22. Если нам надо обработать пачку блоков по маске (match). нужно написать:
    ^parseBlockByMask[маска для имени]


© 1998 — 2024