LINUX.ORG.RU

[Вопрос] А как народ конструирует свой PHP-фреймверк, так чтоб прогать «как на обычном языке программирования»?

 


0

0

Здравствуйте!


Меня интересует вот какой концептуальный вопрос. Протокол HTML-это протокол "без памяти", то есть каждый вызов урла с именем скрипта никак явно не связан с предыдущим вызовом. Обойти это помогает механизм сессий, который реализован на достаточно "низком" уровне, и по сути является костылем. Использование "продвинутых" сессий, например как они реализованы в CodeIgniter, несколько упрощает работу, но логически механизм сессий остается тем же самым.

А хотелось бы понять вот что. Как построить свой фреймверк так, чтобы можно было прогать "как на обычном языке программирвания"? Ну, то есть, чтоб не замечать, что страница открыта заново с какими-то GET-параметрами, POST-данными, данными в сессиях... Чтобы данные в сессиях не приходилось чистить вручную при их ненадобности. То есть, чтобы абстрагироваться от этих веб-особенностей.

У меня при создании веб-приложения все время получается такой порочный круг:

- Формируем HTML с возможными URL, куда может перейти пользователь
- Запишем какие надо данные в сессию
- Показываем пользователю HTML, ждем его действий
- По совокупности состояний GET/POST/Сессий выясняем, что сделал пользователь
- Реализуем реакцию на действия пользователя
- Переходим к первому пункту

Этот цикл очень неестественен, хотя бы потому, что если в действиях пользователя происходит ветвление (пользователь может нажать "Далее", а может нажать какой-нить "Выбор из списка"), то получается, что будут вызваны разные контроллеры, и эти разные контроллеры должны правильно среагировать на предыдущее состояние программы, извлекаемое из GET/POST/SESS.

Получается, что контроль логики размывается по контроллерам, и приходится следить в нескольких разных (инкапсулированных!) местах кода за одними и теми же данными GET/POST/SESS. И если что-то в программе меняется (например добавляется/удаляется/переименовывается GET/POST/SESS переменная), надо прыгать по всем контроллерам, чтобы состыковывать эти изменения и обеспечить правильную логику. Такой процесс меня очень не радует.


Посему хочу услышать мнения профи, какую структуру фреймверка вы применяете, чтобы не долбаться, как я?


На сабжекте мой парсер сломался, так что только по вопросам в тексте.

Нужно чётко разделять, что же такое важно хранить между запросами. Мне, вот, пока кроме временного ввода данных в формы, других надобностей этого не было.

Данные для форм прекрасно хранятся и в сессиях. Ну, или даже тупо из POST/GET-параметров берутся и повторно используются.

Во всех остальных случаях данные, всё же, удобнее хранить в тех же БД или в memcache.

Естественно, что работа со всем этим не ручная, все вопросы связывания, загрузки, кеширования берёт на себя фреймворк.

...

Мне кажется, что тут нужно плясать не от общего принципа - «хочу безсессионную персистентность данных», а от конкретной задачи.

KRoN73 ★★★★★
()

> И если что-то в программе меняется (например добавляется/удаляется/переименовывается GET/POST/SESS переменная), надо прыгать по всем контроллерам, чтобы состыковывать эти изменения и обеспечить правильную логику

Явные проблемы с архитектурой. Все зависимости от внешних факторов надо аккумулировать в одном месте и уже оттуда распределять их по реквестам от модулей.

> Протокол HTML


Лютый фейл

boombick ★★★★★
()
Ответ на: комментарий от boombick

> Протокол HTML
Лютый фейл

HTTP, опечатался.

xintrea
() автор топика
Ответ на: комментарий от boombick

> Все зависимости от внешних факторов надо аккумулировать в одном месте и уже оттуда распределять их по реквестам от модулей.

По-простому объяснить можно? На каком-нить примитивном примере.

xintrea
() автор топика

Лор колбасит, нажимаем Ctrl+Enter для отправки сообщения.

xintrea
() автор топика
Ответ на: комментарий от xintrea

>По-простому объяснить можно? На каком-нить примитивном примере.

Вот я потому и спрашиваю пример конкретной задачи, чтобы на её примере показать как решение делал бы я в своём фреймворке :)

А то я не могу навскидку придумать вариант кроме форм. Да и с ним я уже давно обхожусь без хранения временных данных сессии. Обычно все данные форм или в POST есть, или вообще для конкретного пользователя сохраняются.

KRoN73 ★★★★★
()
Ответ на: комментарий от KRoN73

Ну например да, работа с формами.

В форме несколько полей.
Некоторые поля обязательны к заполнению.
Некоторые поля - выбор из справочника (или даже цепочки справочников).

Работа должна вглядеть так:

1. После отправки формы необходимо проверить валидность и заполненность полей. Если обязательное поле незаполнено, отобразить сообщение, что поле незаполнено, показать форму с уже заполненными на предыдущем шаге остальными полями.

2. Учесть, что при заполнении формы возможен временный переход "на другие контроллеры" для выбора значения в поле-справочнике.


Этот код заполнения формы должен работать как сам по себе, например, в режиме ДОБАВЛЕНИЯ одной записи в базу, так и легко срабатывать в режиме РЕДАКТИРОВАНИЯ, когда пользователь выбирает (либо напрямую, либо по цепочке вложений) нужную запись, а потом вызывается вышеописанный код работы с формой.


Как бы ты вот это все реализовал?

xintrea
() автор топика

> Этот цикл очень неестественен

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

isden ★★★★★
()
Ответ на: комментарий от xintrea

1. виджет-валидатор на обязательные поля. либо, аяксом без рефреша проверять.
2. эмм. что это значит? поподробнее плиз.

> Этот код заполнения формы должен работать как сам по себе, например, в режиме ДОБАВЛЕНИЯ одной записи в базу, так и легко срабатывать в режиме РЕДАКТИРОВАНИЯ, когда пользователь выбирает (либо напрямую, либо по цепочке вложений) нужную запись, а потом вызывается вышеописанный код работы с формой.


ну и в чем проблема? заполнили поля, засабмитали форму. записи сохранились. возвращаемся опять на страницу - записи подсасываются из базы. можно все это гламурно на аяксе сделать.

isden ★★★★★
()
Ответ на: комментарий от xintrea

>Ну например да, работа с формами.

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

Я же прошу привести пример какой-нибудь другой задачи :)

>Как бы ты вот это все реализовал?


Примерно так сейчас работает (на практике сессии не используются):

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

1. Опеределяем некий системный признак «была попытка отсылки формы», который по умолчанию сброшен. Взводиться может или через сессии, или прямым флагом, скажем, в автоматическом (т.е. добавляемом фреймворком, а не при описании формы) hidden-поле.

2. Для каждого стандартного элемента формы (input/textarea/select/etc.) делается проверка на этот признак. Если взведён, то в форме рисуем не данные объекта или дефолтовые данные, а значение из предыдущей отсылки данных.

3. Если при обработке объекта возникает ошибка, то не обламываем сообщением об ошибке, а повторно выводим форму.

...

В общем-то, в считанные строки решение укладывается. При чём будет работать автоматически для всех уже реализованных форм без их переписывания.

KRoN73 ★★★★★
()
Ответ на: комментарий от xintrea

>Этот код заполнения формы должен работать как сам по себе, например, в режиме ДОБАВЛЕНИЯ одной записи в базу, так и легко срабатывать в режиме РЕДАКТИРОВАНИЯ

Да, а это у меня итак унифицировано. Для добавления и редактирования используется один и тот же класс-редактор объекта и одна и та же форма.

KRoN73 ★★★★★
()
Ответ на: комментарий от isden

Давайте аякс оставим за пределами нашего обсуждения. Все должно работать по-деревянному на любом экзотичесвом браузере.


> 2. эмм. что это значит? поподробнее плиз.


Ну, например, есть трехуровневый справочник у какого-нить орифлейма "Страна"->"Город"->"Агент". И есть в форме поле "ID Агента". Нужно дать возможность пользователю последовательно выбрать страну, город, агента, чтобы получить его ID.

При заполнении формы пользователь должен иметь возможность нажать на ссылочку "Выбор агента", выбрать его в несколько шагов, на последнем шаге вернуться обратно в форму, при этом не потеряв уже введенные данные.

xintrea
() автор топика
Ответ на: комментарий от KRoN73

> Для добавления и редактирования используется один и тот же класс-редактор объекта и одна и та же форма.

А можно увидеть интерфейс этого класса-редактора? Ну или полную реализацию.

xintrea
() автор топика
Ответ на: комментарий от xintrea

А можно увидеть интерфейс этого класса-редактора?

В принципе, фреймворк гибкий, в том плане, что позволяет реализовать разные подходы. Я и сам долго не мог для себя выработать единую удобную концепцию. Например, сперва делал так:

1. Есть собственно класс объекта. Ну, скажем, запись какого-то каталога.

2. Есть отдельный админский класс-редактор, который хранится отдельно, является наследником своего же класса объекта, но работает с другим шаблоном, в котором и реализован механизм редактирования.

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

Сейчас же пользуюсь такой моделью.

1 - всё то же самое. Ну, например:

class catalogue_object extends base_object_db
{
    function main_db() { return 'DB'; }
    function main_table() { return 'catalogue'; }
    function main_table_fields() { return array('id', 'title', 'category_id'); }
}

2. Класс-редактор теперь простая страничка:

class admin_catalogue_edit extends base_page
{
    function title() { return $this->id() ? 'редактор объекта' : 'новый объект'; }
()); }
}

И к этому делу прилагается шаблон формы такого вида:

{form class="catalogue_object" id=$this->id()}
Название товара: {input name="title"}<br/>
Категория: {dropdown name="category_id" list="..."}<br/>
{submit value="Сохранить"}
{go value="newpage_admin"}
{/form}

list="..." - там может быть много всяких вариантов, т.к. списки категорий могут быть заданы статическим классом, динамическим списком из БД, списком уже имеющихся категорий в БД, генерироваться кодом и т.п.

{go ...} - это куда сделать редирект после удачного сохранения. Может быть как конкретный URL, так и некоторый из предопределённых макросов.

Ну или полную реализацию.

Весь фреймворк целиком лежит на http://hg.balancer.ru под GPL.

Документации, как это часто бывает в таких проектах нет. Предполагаю понемногу писать её на http://bors.balancer.ru/ , но пока нет стимула :)

Конкретно по формам. Код некоторых Smarty-плагинов: http://hg.balancer.ru/hgwebdir/bors-core/file/b2dd4c99910b/engines/smarty/plu... http://hg.balancer.ru/hgwebdir/bors-core/file/b2dd4c99910b/engines/smarty/plu... и т.д.

Обработчик сохранения формы: http://hg.balancer.ru/hgwebdir/bors-core/file/b2dd4c99910b/inc/bors/form_save...

KRoN73 ★★★★★
()
Ответ на: комментарий от xintrea

>Если можно, через на paste.org.ru

Ну, у меня открытый mercurial-репозиторий с фреймворком. Можно всё скачать скопом :)

...

Чёрт, в LORCODE ссылки автоматом не выделяются сейчас, ну да копипаст тогда поможет. Да и повторить можно для удобства:

http://hg.balancer.ru/
http://bors.balancer.ru/
http://hg.balancer.ru/hgwebdir/bors-core/file/b2dd4c99910b/engines/smarty/plu...
http://hg.balancer.ru/hgwebdir/bors-core/file/b2dd4c99910b/engines/smarty/plu...
http://hg.balancer.ru/hgwebdir/bors-core/file/b2dd4c99910b/inc/bors/form_save...

KRoN73 ★★★★★
()
Ответ на: комментарий от KRoN73

Завтра посморю, ничо уже не соображаю

xintrea
() автор топика

На похапе - никак.

Это можно сделать на любом языке с поддержкой континуэйшенов. Схема, Руби, вроде бы Смоллток...

Miguel ★★★★★
()
Ответ на: комментарий от Miguel

>Это можно сделать на любом языке с поддержкой континуэйшенов. Схема, Руби, вроде бы Смоллток...

Еще есть UCW на CL, Ocsigen на Ocaml. Когда последний раз смотрел Seaside на смоллтоке, они уже отказывались от континюэйшнов, правда не помню в пользу чего.

volh ★★
()
Ответ на: комментарий от xintrea

>Чота я нигде не русском про континуэйшены в руби не нашел.

И не надо искать. Начальные реализации были жутко медленными, в 1.9 продолжения вообще выпилили, время от времени обещают вернуть в 2.0. Но лучше считать что в руби их нет)

volh ★★
()
Ответ на: комментарий от Miguel

>Не знал, но не удивлён.

Ах черт, таки я не прав. Они опять их добавили какое-то время назад и вынесли в отдельный модуль Continuation, и даже поработали над какими-то ошибками. Гхм, ушел смотреть.

volh ★★
()
Ответ на: комментарий от volh

> Они опять их добавили какое-то время назад и вынесли в отдельный модуль Continuation

Скажи лучше, кто то в этом мире использовал continuations руби в практических целях?

anonymous
()
Ответ на: комментарий от anonymous

Я уже сказал о качестве начальных реализаций. Ави Брайант, когда писал свой фреймворк, написал прототип на руби, но работало коряво, он переписал его на няшном Squeak и теперь бегает по конференциям, рассказывая "энтот ваш руби - тоже самое что и ли^W смоллток двадцать лет назад!11".

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

volh ★★
()

Все это велокактусные костыли, такое можно только на языке с continuations.

Deleted
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.