LINUX.ORG.RU

История изменений

Исправление KRoN73, (текущая версия) :

Почему я не представляю использование такого решения в нынешних фремворках? Потому-что архитектура не та и вынуждает пользователя в контроллере прописывать настройки для ресурса.

Я у себя практикую такой подход. Реализован не всюду до конца, всё же, 15 лет развития тянут тонну legacy и я сторонник принципа «преждевременная оптимизация — зло». Но в целом — так:

— Есть базовый класс, описывающий унифицированную работу с данными/интерфейсами/сеттерами/геттерами. Исторически сложилось так, что инициацией и конфигурированием занимается не конструктор, а отдельные методы, которые вызываются класс-лоадером. Класс может быть написан на PHP (и с недавних пор метод совместим с Composer), может быть написан на YAML (при вызове компилируется в PHP). В принципе, достаточно примитивный класс, но исторически тянет в себе много мусора. Понемногу перевожу в аналоги DI с подключением нужных зависимостей, но это процесс долгий и не всюду однозначный. А пока там «внутре» даже такие монстрообразные решения, как формирование человеко-читаемого вида классов с автоматизированным склонением на русском (достаточно задать class_title='Пользователь', чтобы в админках появились «Управление пользователями» или «Добавить пользователя»). Но это, повторюсь, вопросы больше legacy.

— Есть модели. Собственно, это описание формата данных и основных методов для работы с ними. Работа с бэкендом сюда не включена. В простейшем случае это просто перечисление имён полей в таблице БД или даже вообще одно только указание на таблицу (поля извлекутся из описания БД). Но может быть и весьма сложным, с указанием типов полей в базе, типов полей при обработке/редактировании, условий валидации... С одной стороны это немного нарушает чистоту MVC, с другой — я гонюсь не за шашечками, а за удобством разработки. И один из главных принципов, которые усвоил тут — избегать многократного повторения имён полей/свойств. А то один список в модели, другой — в форме редактирования, третий — в валидаторе. Переименовываешь в одном, надо переименовывать в других... Я стараюсь всё задать сразу в модели. Что-то в духе:

class lor_user extends lor_model
{
    function table_fields()
    {
        return [
            'id',
            'title' => 'username', // это когда свойство класса не равно полю таблицы
            'create_ts' => 'UNIX_TIMESTAMP(registered)',
            'group_id' => [ // расширенное описание
                'class' => 'lor_group',
            ],
        ];
    }
}

— Есть классы бэкендов (у меня исторически это «storage»). В модели указывается, с каким бэкендом она работает и бэкенд можно в идеале менять одной строчкой, не затрагивая модель целиком. Класс бэкенда занимается извлечением/парсингом данных и, опционально, их записью. Бэкенды бывают разнообразные, от mysql и oracle до markdown (объекты-«страницы» могут целиком описываться в текстовых плоских файлах)

— Есть классы представлений. В простейшем случае — вообще, одна заглушка для указания шаблона, откуда берутся данные. В сложном — довольно жирные классы, тянущие в себе уйму кода для подготовки данных для шаблонов. При чём исторически есть смешанные случаи, когда один класс описывает как модель, так и представление. Краткость записи провоцировала в своё время экономить на спичках :) Последующий опыт показал ущербность такого подхода, но такого legacy, написанного в 2006..2008гг ещё полно.

А вот чего нет в явном виде — это контроллеров. Я считаю, что это функция, которой и должен заниматься сам фреймворк. Поэтому контроллеры размазаны по всей структуре в целом. Моё дело, как разработчика прикладного проекта, в точке входа вызвать инициацию фреймворка. Всё. Дальше он всё делает сам. Найдёт карты роутинга или автоматически роутящийся класс. По найденному представлению загрузит необходимые для представления объекты. Проведёт все шаблонные обработки, если нужно и, если нужно, закеширует результаты.

То есть при разработке какого-то компонента мне обычно достаточно указать модель и описать представление. Всё. Их взаимодействие и прочее — «Machines should work; people should think» © IBM

Например, представление может состоять из:
— user.php — собственно, класса, который может быть пустышкой только для указания наличия сущности
— user.tpl — шаблона тела представления (тип может быть любой, реально я поддерживаю/развиваю Smarty, чистый PHP и HAML)
— user.inc.css — дополнительные CSS-элементы для конечной страницы встраивающиеся в результат
— user.inc.js — аналогично для JS.

Вместо того, чтобы связывать это всё вручную, я поручил заниматься этим фреймворку. Он загрузит user.php и выполнит методы, генерирующие данные. Скормит их шаблонизатору вместе с user.tpl и получит html основного тела страницы. Это тело потом скормит общему генератору темы, типовому шаблону страницы (хедеры/футеры/меню и т.п.), добавив user.inc.css и user.inc.js. И уже готовую страницу отдаст браузеру пользователя.

В последний год ковыряю 2-ю версию фреймворка, которая изначально задумывалась полностью несовместимой, но сейчас понемногу свожу в один. На чём имею много внутренних идеологических проблем, над которыми понемногу и ломаю голову. Основным отличием второй версии должны были стать «без-NPE-шная логика», т.е. никакой метод не должен возвращать NULL. И цепочки вызовов методов можно писать без лишних проверок. В этом основная проблема несовместимости со старым кодом. Плюс к этому — работа с Composer, но это в итоге появилось и в первой версии. Плюс переделка логики загрузки объектов из бэкендов и переход от множества процедурных вызовов к объектным. Также планировался полный рефакторинг с целью выноса из базовых классов всего legacy, в итоге это привело к тому, что в composer-зависимостях сейчас вторая версия тащит целиком фреймворк первой :) Ну да буду чистить ещё...

Исходная версия KRoN73, :

Почему я не представляю использование такого решения в нынешних фремворках? Потому-что архитектура не та и вынуждает пользователя в контроллере прописывать настройки для ресурса.

Я у себя практикую такой подход. Реализован не всюду до конца, всё же, 15 лет развития тянут тонну legacy и я сторонник принципа «преждевременная оптимизация — зло». Но в целом — так:

— Есть базовый класс, описывающий унифицированную работу с данными/интерфейсами/сеттерами/геттерами. Исторически сложилось так, что инициацией и конфигурированием занимается не конструктор, а отдельные методы, которые вызываются класс-лоадером. Класс может быть написан на PHP (и с недавних пор метод совместим с Composer), может быть написан на YAML (при вызове компилируется в PHP). В принципе, достаточно примитивный класс, но исторически тянет в себе много мусора. Понемногу перевожу в аналоги DI с подключением нужных зависимостей, но это процесс долгий и не всюду однозначный. А пока там «внутре» даже такие монстрообразные решения, как формирование человеко-читаемого вида классов с автоматизированным склонением на русском (достаточно задать class_title='Пользователь', чтобы в админках появились «Управление пользователями» или «Добавить пользователя»). Но это, повторюсь, вопросы больше legacy.

— Есть модели. Собственно, это описание формата данных и основных методов для работы с ними. Работа с бэкендом сюда не включена. В простейшем случае это просто перечисление имён полей в таблице БД или даже вообще одно только указание на таблицу (поля извлекутся из описания БД). Но может быть и весьма сложным, с указанием типов полей в базе, типов полей при обработке/редактировании, условий валидации... С одной стороны это немного нарушает чистоту MVC, с другой — я гонюсь не за шашечками, а за удобством разработки. И один из главных принципов, которые усвоил тут — избегать многократного повторения имён полей/свойств. А то один список в модели, другой — в форме редактирования, третий — в валидаторе. Переименовываешь в одном, надо переименовывать в других... Я стараюсь всё задать сразу в модели. Что-то в духе:

class lor_user extends lor_model
{
    function table_fields()
    {
        return [
            'id',
            'title' => 'username', // это когда свойство класса не равно полю таблицы
            'create_ts' => 'UNIX_TIMESTAMP(registered)',
            'group_id' => [ // расширенное описание
                'class' => 'lor_group',
            ],
        ];
    }
}

— Есть классы бэкендов (у меня исторически это «storage»). В модели указывается, с каким бэкендом она работает и бэкенд можно в идеале менять одной строчкой, не затрагивая модель целиком. Класс бэкенда занимается извлечением/парсингом данных и, опционально, их записью. Бэкенды бывают разнообразные, от mysql и oracle до markdown (объекты-«страницы» могут целиком описываться в текстовых плоских файлах)

— Есть классы представлений. В простейшем случае — вообще, одна заглушка для указания шаблона, откуда берутся данные. В сложном — довольно жирные классы, тянущие в себе уйму кода для подготовки данных для шаблонов. При чём исторически есть смешанные случаи, когда один класс описывает как модель, так и представление. Краткость записи провоцировала в своё время экономить на спичках :) Последующий опыт показал ущербность такого подхода, но такого legacy, написанного в 2006..2008гг ещё полно.

А вот чего нет в явном виде — это контроллеров. Я считаю, что это функция, которой и должен заниматься сам фреймворк. Поэтому контроллеры размазаны по всей структуре в целом. Моё дело, как разработчика прикладного проекта, в точке входа вызвать инициацию фреймворка. Всё. Дальше он всё делает сам. Найдёт карты роутинга или автоматически роутящийся класс. По найденному представлению загрузит необходимые для представления объекты. Проведёт все шаблонные обработки, если нужно и, если нужно, закеширует результаты.

То есть при разработке какого-то компонента мне обычно достаточно указать модель и описать представление. Всё. Их взаимодействие и прочее — «Machines should work; people should think» © IBM

Например, представление может состоять из:
— user.php — собственно, класса, который может быть пустышкой только для указания наличия сущности
— user.tpl — шаблона тела представления (тип может быть любой, реально я поддерживаю/развиваю Smarty, чистый PHP и HAML) — user.inc.css — дополнительные CSS-элементы для конечной страницы встраивающиеся в результат — user.inc.js — аналогично для JS.

Вместо того, чтобы связывать это всё вручную, я поручил заниматься этим фреймворку. Он загрузит user.php и выполнит методы, генерирующие данные. Скормит их шаблонизатору вместе с user.tpl и получит html основного тела страницы. Это тело потом скормит общему генератору темы, типовому шаблону страницы (хедеры/футеры/меню и т.п.), добавив user.inc.css и user.inc.js. И уже готовую страницу отдаст браузеру пользователя.

В последний год ковыряю 2-ю версию фреймворка, которая изначально задумывалась полностью несовместимой, но сейчас понемногу свожу в один. На чём имею много внутренних идеологических проблем, над которыми понемногу и ломаю голову. Основным отличием второй версии должны были стать «без-NPE-шная логика», т.е. никакой метод не должен возвращать NULL. И цепочки вызовов методов можно писать без лишних проверок. В этом основная проблема несовместимости со старым кодом. Плюс к этому — работа с Composer, но это в итоге появилось и в первой версии. Плюс переделка логики загрузки объектов из бэкендов и переход от множества процедурных вызовов к объектным. Также планировался полный рефакторинг с целью выноса из базовых классов всего legacy, в итоге это привело к тому, что в composer-зависимостях сейчас вторая версия тащит целиком фреймворк первой :) Ну да буду чистить ещё...