Как оно работает. Отображение объектов [страниц, в частности]

документация
 
+
-
edit
 

Balancer

администратор
★★★★★
Начну, пожалуй, понемногу хотя бы в общих чертах фреймворк документировать :)

Начну от печки, т.е. по порядку, с начальной инициации.

Общий принцип работы системы

1. Web-сервер при необходимости передаёт управление крошечному скрипту-загрузчику, в котором устанавливаются необходимые переменные с адресами хранения ядра фреймворка и его расширений (если они не являются стандартными) и происходит передача управления фреймворку. Настроечный скрипт вынесен отдельно с целью удобства смены юзера для suPHP, возможности использования локальных модулей и т.п.

2. Основной скрипт фреймворка пытается распознать переданную ссылку как объект системы и загрузить его. В случае неудачи - опиционально переадресуется на обработчик 404-й ошибки.

3. Если объект находится, передаёт управление компоненту, отвечающему за отображение (рендеринг) объектов.
 
+
-
edit
 

Balancer

администратор
★★★★★
Загрузка объектов

Объекты могут быть как жёстко запрограммированными (когда не требуется подгрузка сторонних данных или этим занимается сам объект), так и использующим тот или иной готовый механизм загрузки сторонних данных, storage_engine в терминах фреймворка.

Загрузка объекта считается успешной, если методом can_be_empty() явно говорится, что проверка на загруженность не нужна, либо если метод loaded() возвращает истину. По умолчанию каждый storage_engine при удачной загрузке внешних данных устанавливает set_loaded(true) для целевого объекта.

В настоящее время поддерживаются следующие готовые источники данных:
  • storage_db_mysql_smart - Позволяет автоматически загружать объекты или массивы объектов, инициируя их из баз данных MySQL, включая сложные запросы, распределённые по разным базам данных и таблицам.
  • storage_fs_separate - Данные загружаются с файловой системы в формате одно свойство объекта - один файл. Очень удобно для ручного редактирования «статических» страниц сайта. Файлы лежать прямо в файловой структуре веб-сервера, по каталогам, соответствующим виртуальным. Только чтение.
  • storage_fs_xml - Данные загружаются из XML-файла определённого формата, хранящемся в отдельной структуре каталогов, параллельной структуре веб-сервера.
  • storage_db_oci - альфа-версия, только чтение. Работа с БД Oracle.


Отсутствует в ядре, но имеется в расширениях Авиабазы хранилище
  • storage_fs_hts - одиночные файлы исходников страниц Авиабазы в старом .hts формате образца ~1999-го года :)
 
+
-
edit
 

Balancer

администратор
★★★★★
Загрузка классов

Небольшое отвлечение. Все основные классы системы имеют автоматические загрузчики, конструкторы явно не вызываются, путь к классу задаётся его именем. Имя класса разбивается на части, отделённые подчерком и из этих частей составляется относительный путь к *.php-файлу в каталоге classes/ или classes/bors/. Например, storage_db_mysql_smart будет искаться в подкаталоге classes/storage/db/mysql/smart.php

Инициализация отдельного класса системы или объекта проходит одинаково, через функцию object_load:
code php
  1. <?php
  2. $post = object_load('forum_post', 1457);

Для механизма данных storage_db_mysql_smart реализована загрузка массивов (одним mysql-запросом):
code php
  1. <?php
  2. $posts = objects_array('forum_post', array('id IN' => array(1457, 1458, 1459)));


или так:
code php
  1. <?php
  2. $posts = objects_array('forum_post', array('order' => '-create_time', 'limit' => 10));


Второй аргумент функции objects_array - «интеллектуальный» массив, в котором можно указать почти все особенности запроса. Значения для where, order, limit, group, left/inner join'ы и т.п. Подробно он будет рассмотрен позже. В данных же примерах, думаю, итак всё доступно :)

Есть ещё функция загрузки одного, первого элемента запроса. Дело в том, что object_load требует явного указания ID объекта и только его. А, скажем, как нам загрузить пользователя с наивысшей репутацией на форумах, если его ID неизвестен?
code php
  1. <?php
  2. $posts = objects_first('forum_user', array('order' => 'reputation'));


По сути это обёртка для objects_array, только при вызове автоматически будет добавлен limit=1, а результат вернётся не массивом, а скаляром.
 
+
-
edit
 

Balancer

администратор
★★★★★
Вернёмся к основной линии...

Отображение объектов

После успешной загрузки объекта и проведения ряда опциональных вспомогательных действий (об этом позже. Например, проверки валидности данных, обработка GET/POST-запросов, в т.ч. аплоады файлов и т.п. - до отображения дело может и не дойти) система пытается отобразить его, вызвав метод content() (соответственно, можно при крайней необходимости его переопределить) и, в случае удачи, выводя результат в браузер. Также, полученный результат может автоматически сохраниться на диске для статического кеширования. Об этом подробно тоже позднее.

Метод content() работает, если не вдаваться в детали кеширования, защиты от параллельных запросов и т.п. просто. Он загружает механизм отображения, render_engine класса и вызывает метод render() этого механизма, передав в качестве параметра отображаемый объект. Метод же возвращает готовый результат.

Сейчас совсем немного таких механизмов:
  • render_page (по умолчанию) - Отображает обычную страницу сайта, используя шаблонные механизмы и т.п.
  • render_fullpage - аналогично render_page, но шаблон тела сообщения является готовым результатом, а не вставкой в общий шаблон, как в случае render_page (подробнее разница будет рассмотрена позже)
  • render_php - рендеринг шаблона средствами самого PHP.
  • render_self - когда метод render() определён в самом рисуемом классе. Собственно, из-за наличия этого механизма список готовых рендереров так невелик. Вся экзотика (включая картинки) рисуется обычно через render_self.


Рассмотрим подробнее основной механизм, render_page.

Рядом с каждым .php-файлом класса может лежать одноимённый *.html-файл. Это Smarty-шаблон собственного контента объекта. В простейшем случае может быть вообще статический HTML-код. render_engine насыщает нужными данными Smarty-объект, парсит этот шаблон, и результат подставляет в переменную $body глобального шаблона страницы.

Пример

Выведем на отдельной странице имя того самого пользователя с самой высокой репутацией, которого упоминали выше.

Привязка ссылки (в виде регекспа) к отображающему эту ссылку объекту осуществляется в каталогах handlers/bors_map.php, локальных, системных или глобальных. Формат очень простой, файл должен вернуть переменную $map, содержащую простой массив строк.

Каждая строка - запись вида
регексп_ссылки => объект

Предположим, что мы хотим видеть нашего пользователя на странице /bors/examples/top-reputation/

А класс, который будет заниматься отображением пользователя, будет иметь имя examples_topReputation (тут некрасивое смешение стилей, так как для примеров не хочется усложнять структуру :))

Так и запишем:
code php
  1. <?php
  2. $map = array(
  3.     // ...
  4.     "(/bors/examples/)top\-reputaton/ => examples_topReputation",
  5.     // ...
  6. );


Никаких параметров в нашем случае объекту передавать не надо.

Необязательное выделение первой регексповой группой (скобки) - это автоматическое указание на родительский объект в навигационной иерархии. Вполне можно допустить, что родитель страницы /bors/examples/top-reputaton/ - это /bors/examples/ :) При необходимости каждый класс может и явно указывать на своих родителей.

Перейдём к классу объекта:
code php
  1. <?php
  2. class examples_topReputation extends base_page
  3. {
  4.     function title() { return ec("Наш лучший пользователь"); }
  5.     function local_template_data_set()
  6.     {
  7.         return array(
  8.             'user' => objects_first('forum_user', array('order' => '-reputation')),
  9.         );
  10.     }
  11. }


Всё. Разберём этот класс.

extends base_page - класс является наследником базового класса страницы. Характерная особенность - необязательность загрузки данных. Ибо, вот, в нашем случае мы ничего не грузим со стороны, только вычисляем.

метод title() возвращает заголовок страницы. (Функция ec() - encode перекодирует строку из UTF-8, в которой по умолчанию пишется вся система в текущую локаль сайта.)

метод local_template_data_set() возвращает хэш-массив данных, которыми насыщается шаблон. В нашем случае мы в шаблоне сможем использовать переменную $user.

Файл находится по пути classes/bors/examples/topReputation.php в одном из возможных каталогов - локальном, системном или ядра фреймворка.

Перейдём к шаблону. Всё, что сейчас сделаем для простоты, изобразим имя пользователя и уровень его репутации.

Сам шаблон будет находится по пути classes/bors/examples/topReputation.html

code html4strict
  1. <p>Пользователь: {$user->title()}</p>
  2. <p>Репутация: {$user->reputation()}</p>


Всё.

Данная страница уже может отображаться. Смотрите результат по адресу:
http://balancer.ru/bors/examples/top-reputation/.

Естественно, что шаблон там используется от сайта по умолчанию. Мы задали только контент страницы.

Ловите файлы, закоммиченные в ядро: bors-core: changeset 302:2dd234357c12
 
+
-
edit
 

Balancer

администратор
★★★★★
Надеюсь, пока не было ничего сложного? :)

Продолжу описание системы позднее...
 
+
-
edit
 

Balancer

администратор
★★★★★
Небольшое дополнение. Генерируемый последним примером запрос будет один:

code mysql
  1. SELECT `id`,username AS `title`, `group_id`, title AS `user_title`, `use_avatar`, `avatar_width`, `avatar_height`, `num_posts`, `signature`, `signature_html`, `warnings`, `warnings_total`, `reputation`, `salt`, password AS `saltp`, user_cookie_hash AS `saltu`, `pure_reputation`, registered AS `create_time`, last_post AS `last_post_time` FROM `users`     ORDER BY reputation DESC LIMIT 1;


Возвращаемые результаты запроса задаются соответствующим классом, forum_user, исходник которого лежит на bors-airbase: classes/bors/forum/user.php@30143ac88174

Но это уже сложный большой (и даже некрасивый/некорректный местами... тяжёлое наследие прошлого :D) реально работающий прямо в этом форуме класс, до таких мы дойдём со временем :)
 

в начало страницы | новое
 
Поиск
Настройки
Твиттер сайта
Статистика
Рейтинг@Mail.ru