title: пре-Фреймворк friends, версия 9, идеология и структура !3Прохождение запроса Контроллер разбирает запрос (/data/friends.htm?id=1&rel=friends) - помещает в Def::$request набор значений: 'site' => "папка", в которой находятся запрашиваемые "файлы", 'file' => friends.htm - запрашиваемый "файл", 'subj' => "имя файла" (friends), 'ext' => "расширение файла" (htm), 'query' => набор значений из "id=1&rel=friends" (parse_str), 'where' => получаемый сложным способом указатель на строку или ряд строк таблицы: во-первых, проверяется, не пришёл ли "флаг" (из Модели) - например, для Сущности friends флагом является "rel"; затем извлекаются значения из $_GET; затем весь этот набор проверяется функцией Def::get_key на предмет наличия полного первичного ключа (однозначной идентификации записи). //Пытались выкинуть "флаг" на. Не получается. Меняем немного идею: флаг указывает на поле, в котором хранится имя Сущности (в таблице, где может быть несколько Сущностей). На самом деле это делает даже не Контроллер, а метод Def::parse_query, Контроллер только передаёт туда url. Хотя, если разобраться, url тоже "вычисляется" в Def, поэтому Контроллер в данном случае передаёт в parse_query "ничего" (просто вызывает без параметров). Но иногда нужно вызывать с параметрами: например, при вызове адреса delete.com происходит удаление записей из базы; так вот, они удаляются на основании... (вы не поверите!) реферера! т.е. где-то (типа в View::delete()) написано: $request = Def::parse_query($referer, false); и дальше на основании данных $request вычисляется первичный ключ. Метод Def::get_key используется также во View для построения ссылок в списке (таблице): методу передаются все Данные текущей строки, он проверяет Модель на наличие Флага (и при необходимости добавляет в начало тестируемых данных пару "сущность" => Текущая Сущность), затем создаётся набор для УРЛ: проверяются вошедшие Данные, извлекаются все свойства текущего поля из Модели, и если у поля есть метка key=1 (Первичный ключ), на выход добавляется пара Имя_Поля => Значение из Данных; затем проверяется полнота Ключа: из Модели извлекаются все поля с меткой key=1 и проверяется наличие каждого такого поля во входящих Данных (если есть все - full_key = true). !3Javascript Файлы js могут присоединяться к HEAD HTML-страницы в разных местах. Точнее, HEAD - это, конечно, одно место :-), но список файлов js для него формируется на разных этапах. !№Поле site из Запроса: к нему можно привязать файлы js через Модель (указать в разделе Сайты для конкретного сайта файл - типа construct.js), это проверяется прямо в Контроллере (function set_request()) - что, может, не очень правильно. У каждой Сущности в Модели есть поля js и css. Они пока, кажется, нигде не проверяются. В таблице Данных тоже могут быть свои поля js и css - для каждой отдельной страницы. !№ !3Def::$response Вот здесь, кстати, формируется список файлов js и css (сюда они пихаются на разных этапах - из Контроллера и Вью). Если js там изначально нет, то site.css записывается сразу, "из ниоткуда", т.е. предполагается, что если без js сайт ещё как-то может прожить, то без css сайтов не бывает вообще. !3Def::$page А в этот массив собирается "почти готовая" страница: если в Def::$response складываются заготовки, например, для скриптов в виде массива (файл - содержание), то в Def::$page['head'] попадает !_строка!_, состоящая из одного или нескольких тэгов script (плюс другие строки - Заголовок, стили...). !3View Ладно, мы отвлеклись. Контроллер анализирует УРЛ и (если расширение - 'com') ПОСТ. Т.е. в Контроллере сейчас есть IF. Если будет много разных расширений, вместо IF надо будет запихать эту логику в Модель. Это один из основных моментов "офреймворчивания": перемещение логики из кода в статические массивы. В конце Контроллер вызывает-таки следующий объект - View. Без параметров (sans pantalone!). Или вызывает (в разделе 'com') что-то типа View::delete() - тоже без параметров, но зато конкретную статическую функцию. То есть Контроллер всегда вызывает View. За одним исключением: авторизация. Когда от пользователя приходят данные из формы авторизации (на адрес auth.com), Контроллер проверяет эти данные прямо внутри себя, и только потом передаёт результат во View. !3Data Вид лезет в Данные с запросом Data::get($subj), где $subj - наименование текущей Сущности (полученной из Запроса - request). В общем-то, Данные, как и все остальные участники проекта, могут получать Сущность сами, из того же Запроса. Но не будут: Данные должны работать, не думая: тупо и быстро, так как это самый критичный по скорости участок работы. Класс Данных очень мал: функции общего вида "прочитать - записать - стереть", которые передают задачу для исполнения в конкретный класс (Mysql, Xml...). У Данных есть собственные методы: отчасти из-за ненужности отдельных классов (например, для get/write _file_ini), отчасти по-правде нужные: get_table_time, set_num_rows. При выдаче любой выборки, Данные возвращают ещё и !_время!_ последнего изменения строк выборки (если находят подходящее поле в таблице). При записи-удалении, Данные заносят в Модель состояние всей Сущности (есть/нет вообще там записи) - чтобы в дальнейшем Контроллер мог не лазить в Данные, а сразу, по полю в Модели решать, что показывать, если запрошенных записей не существует (паттерн ""хороший библиотекарь""), - например, выдавать сакраментальное "404", как это я люблю делать. !3Тип отображения Где-то, блин, в середине всей этой возни, кто-то должен выбрать мудрый способ отображения полученных Данных. Способа, в зависимости от количества данных, принципиально может быть два: Таблица или Обект (Список). Если получена одна строка - это, ясно море, Объект. Если несколько строк - Таблица ("двухмерный Объект"). Как мы это определяем? Да уж не по количеству полученных строк. А наоборот, по типу Запроса: если есть полный Первичный ключ, - будем показывать Объект; если нет ключа (или ключ неполный) - Таблицу (даже если будет получена одна строка). Да, ещё есть такой прикол - в Модели тоже можно указывать тип отображения. Как этот прикол использовать, пока не знаю. Особые решения. Если дело происходит на сайтах Данные или Конструктор (а, я же забыл сказать, что в системе по умолчанию ТРИ сайта!), то Объект всегда отображается в виде формы для редактирования. И если данных в запрошенной таблице вовсе нет, - всё равно выдаётся Объект в виде (пустой) формы для редактирования (точнее, в этом случае уже для !_добавления!_) Данных/Метаданных. !3Сайты Общий, Данные и Конструктор Сейчас система настроена работать с тремя "сайтами": корневая папка www - обычный сайт, каким его видят пользователи (Public), папка /www/data - сайт для правки Данных, папка /www/constructor - сайт для правки Метаданных (Модели). Права доступа для Конструктора должны быть самые большие (на сегодня - "2"), для правки Данных - "1", ну, а Public может использовать кто угодно (права "0" у неавторизованных пользователей). Править Модель - опасно. Код сильно привязан к Модели. Например, он проверяет права в полях status_w каждой Сущности, а на сайте Конструктор можно легко удалить любое поле Модели. Не знаю, как от этого защититься, кроме постоянной записи резервных копий Модели (на каждое действие Крутого Администратора). !3Модель Модель, кстати, хранится в файле model.xml. Это главное на данный момент достижение описываемого фреймворка. Раньше Модель хранилась просто в массиве PHP, и править её (визуально), соответственно, было гораздо сложнее. Уже, кстати, не в одном файле. Потрахавшись с кучей "таблиц" в файле model.xml, я решил разделить-таки его на "элементарные таблицы": subjects.xml, fields.xml, sites.xml... !3Данные - хранилище В xml, кстати, хранятся некоторые данные: например, Пользователи (это, разумеется хорошо только для Администраторов, которых мало; если предполагается авторизация на сайте кого попало, Пользователей, конечно, надо хранить в БД). А также ряд настроек. Там, собсно, логика простая: для списков "Имя - Значение" проще и веселее использовать ini-файлы; для более сложных структур (вида Имя: Набор_свойств) - xml. Хотя теоретически в ini-файл можно запихать и таблицу (номера строк - в секции), и даже, например, наш файл fields.xml - если убрать оттуда именованный доступ к таблицам, а хранить всё в одной таблице (а таблицы, к которым относятся описываемые поля, определять через свойство "table" каждого поля). Но так увеличивается время доступа к объектам. Если таблицы в fields именованные, мы просто указываем: Def::$model['fields'][имя_таблицы][имя_поля]. А если все поля в одной таблице, надо будет сначала получить Def::$model['fields'], а потом в цикле искать "if ($props['table'] == имя_таблицы)".