Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "Прочее";
Текущий архив: 2009.08.30;
Скачать: [xml.tar.bz2];

Вниз

WEB <-> Настройки <-> БД   Найти похожие ветки 

 
Пит   (2009-06-30 18:04) [0]

Давно терзают меня измышления на тему WEB-настроек программы, хранения их в БД и кажется, я готов хоть как-то более-менее грамотно описать ситуацию.

В целом, проблема как я понимаю давно рассматриваемая и в некоторых (C#) языках есть средства упрощения данного процесса, а некоторые так и созданы для таких вещей, но рассматривается именно Delphi.

Задача описывается достаточно кратко: в программе есть объекты (сущности, записи - как угодно), у них есть свойства. Хранить эти свойства офлайн нужно в БД. Со стороны пользователя данные сущности настраиваются через WEB.
Собственно, все.

А вот теперь о реализации. Общие проблемы такие:

I) неудобно постоянно разбирать / собирать HTML. Как-то надо автоматизировать, обегчить этот процесс, обеспечить расширяемость и так далее.

II) HTTP протокол текстовый, постоянные конвертации, нужно упрощать процесс.

Более подробно на техническом уровне в чем состоит задача:

1) со стороны сервера формируется HTML-страничка, позволяющая в браузере делать настройки объекта, данная страница пересылается клиенту

Вопрос номер один: кто будет формировать эти странички, позволяющие настраивать объекты?

Это может быть сам объект (его внутренний функционал), какой-то дополнительный внешний код, другие варианты?

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

Сделанные настройки отправляются POST-запросом на сервер. Допустим, серверу это доступно в виде TStrings, имя параметра и его значение (все параметры, напомню, текстовые)

Пример пересылаемых данных для удаления объекта:
action=delete
id=54


Пример для редактирования:
action=edit
id=54
name=Объект номер 54
number=3
...
[свойства объекта]
...


3) сервер обрабатывает полученную информацию: проверяет на корректность, делает соответствующие изменения в БД, изменяет свойства объектов (сущностей) в программе.

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

Что должен уметь функционал - принимать WEB-запросы в виде набора "параметр=значение" и формировать ответ в HTML.


 
Пит   (2009-06-30 18:05) [1]

Вариант 1. Примитивный.

Набор свойств в программе хранится в виде списка на структуры, "ручная" обработка всего и вся

var
 List: TList;   // список свойств
...
function WEBRequest(ParamList: TStrings): string;
var
 id: int64;
begin
 if ParamList["action"] = "add" then
 begin
   if not TryStrToInt64(ParamList["priority"], iPriority) then begin
     Result := "Отсутствует параметр приоритет";
     exit;
   end;
   if (iPriority < 0) or (iPriority > 100) then
   begin
     Result := "Параметр приоритет должен быть в диапазоне 0..100";
     exit;
   end;
   .......
   // сохранение в БД
   INSERT INTO xxx Priority = [iPriority] ...
   // получаем ID получившейся записи
   ID := SELECT ... ;
   // добавление новой сущности в работу
   New(Param);
   Param.ID := ID;
   Param.Priority := iPriority ;
   ...
   List.Add(Param);
 end;
end;


Код ужасен. Приведена только часть кода, которая отвечает за создание нового объекта. А еще есть редактирование, удаление, другие объекты. Да и показана обработка только одного свойства.

Код будет очень избыточным. Проверка на допустимость Priority нужна и в секции редактирования объекта, и в секции добавления нового объекта. И так каждый раз, плюс конвертации постоянные текстовых параметров в нужные типы.

Более-менее сложная иерархия, кода будет килобайты, ну в общем что тут обсуждать.


 
Пит   (2009-06-30 18:05) [2]

Вариант 2. Продвинутый.

Пишется некоторый класс, который обегчает работу с WEB-ом. Параллельно он вынужден инкапсулировать работу с хранилищем данных, иначе от него немного толку.
Для наглядности некоторое описание этого класса:

THTMLDBConfig = class
...
procedure AddProperty(Name, Desc: string; TypeProp: TTypeProp;
     DefValue: string; HTMLMaxLength:integer=-1; HTMLSize: integer=-1;
     CheckBoxLabel:string="");
function AddRecord(ParamList: TStrings; ErrText: PString): boolean;
   function EditRecord(ParamList: TStrings; PKValue: int64;
     ErrText: PString): boolean;
   procedure DeleteRecord(ParamList: TStrings); overload; virtual;
   procedure DeleteRecord(PKValue: int64); overload; virtual;
   function GetHTMLCodeRecord(PKValue: int64): string; overload; virtual;


Пример настройки такого класса:

AddProperty("Name", "Название магазина", tpTextNotEmpty, "", 50, 20);
AddProperty("Priority", cParamRegCodeNum_desc, tpInt, "", 3, 3);


Дело улучшается. Имея такой класс, двумя строчками я даю понять о существовании двух полей. Задаю имена (это как имена POST-данных, так и имена полей в БД), задаю описание поля (по которому класс может генерировать текстовое описание WEB-ошибок), тип поля, другие характеристики, нужные классу.

Соответственно, пример 1 трансформируется:

function WEBRequest(ParamList: TStrings): string;
begin
 if ParamList["action"] = "add" then Result := HTMLDBConfig.AddRecord(ParamList);
end;


Соответственно, на основании заданной структуры (с помощью AddProperty) класс из переданного ParamList проверяет наличие всех нужных параметров, проверят их валидность, пишет их в базу, выдает ошибки в случае чего.

Процедура же GetHTMLCodeRecord в HTML табличном виде выдает форму настройки параметров данной сущности, опять же на основании заданой структуры через AddProperty.

Налицо инкапсуляция работы с WEB и с БД. Но при этом реальный код по работе с сущностью отделен от БД (плохо это? Хорошо?), конечно класс наносит определенные ограничения, а что поделать.

Зато сколько свойств у объекта, столько строчек кода нужно для определения формата вывода в HTML, редактирование, добавления объектов через HTML в БД.
Недостатки возможно в том, что сторонний класс "отнимает" работу с БД у собственно функционального кода.
И при этом получается, что WEB настраивает не сами сущности в программе, а сущности в БД. То есть, допустим, при редактировании объекта класс проверяет и записывает информацию в БД, откуда для применения изменений она уже должна быть считана в программу. Немного, наверное, порочная практика.

Также нужно обеспечивать синхронизацию между работой вспомогательного WEB-DB класса и той части кода, отвечающей за функционал объекта.

В случае использования такого вспомогательного класса вручную остается писать:

- загрузку свойств объекта из БД для работы (для загрузки при старте программы, для загрузки изменений из БД, которые туда сохранил вспомогательный класс)
- сохрание изменившихся свойств объекта в БД


 
Пит   (2009-06-30 18:05) [3]

Дальнейшие улучшения

Видимо, надо копать в сторону интеграции функционала объекта с возможностью настройки через WEB, сохранение и загрузка свойств из БД.
То есть, каждый функциональный класс имеет метод аля:

TClient = class
 // функционал
 ...
 // WEB
 function WEBRequest(ParamList: TSTrings): string;
 ...
 // property
 property Priority: integer write SetPriority read FPriority;
...
end;


При этом не хочется вручную обрабатывать каждый параметр и свалиться в код примера 1.
Первоначальную проверку данных можно сделать аля как через класс THTMLConfig, но без функционала работы с БД, что-то типа:

function TClient.WEBRequest(ParamList: TStrings): string;
begin
 if ParamList["action"] = "edit" then
 begin
   Result := THTMLConfigCheck.Request(ParamList) ;
   if Result <> "" then exit;
   Self.Priority := StrToInt(ParamList["priority"]);
   ...
 end;
end;


При этом класс THTMLConfigCheck настраивается аля:

HTMLConfigCheck.AddProperty("priority", "Приоритет клиента", tpInteger);

тем самым он может производить первоначальные проверки и выдавать ошибки. Дополнительная проверка, возможна, допустим через исключения в сетерах. То есть:

procedure TClient.SetPriority(Priority: integer);
begin
 if (Priority < 0 ) or (Priority > 100) then EMy.Create("Ошибка такая то");
 ...
end;


Соответственно, тогда в WEBRequest должна быть "поимка" исключений при присвоении свойств.

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

- если форма заполнена неверно и отправлена - должен вернуться ПЕРЕЧЕНЬ ошибок (а не первая встретившаяся) заполнения формы с повторным выводом неправильно заполненной формы

Есть еще кое-какие фишки, забыл уже ((

В общем, проблема достаточно многогранная, думаю, у кого нет опыта работ таких связок на дельфи сразу во все проблемы просто не въедет. Описать полностью просто сил нету, статью надо писать. Хотелось бы обсудить, возможно кто-то имеет опыт, решал аналогичные проблемы. От мыслей пухнет голова, сделаешь в одном месте красиво - в другом не очень. А хочется красиво везде, без избыточности...

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


 
test ©   (2009-06-30 18:12) [4]

В Java давно на этот счет сделали DAO DB суть в том что класс отвечает за таблицу(связку таблиц), ты оперируеш обьектами, обьектами уже сами занимаются SQL DML и Select"ом.


 
test ©   (2009-06-30 18:13) [5]

Там этот момент облегчен тем что удалять ничего не надо сервер сам узнает своих.))


 
Пит   (2009-06-30 18:32) [6]


> В Java давно на этот счет сделали DAO DB

круто


> класс отвечает за таблицу(связку таблиц), ты оперируеш обьектами,
>  обьектами уже сами занимаются SQL DML и Select"ом.

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


 
sniknik ©   (2009-06-30 18:33) [7]

много букв.., не осилил, но суть вроде уловил, про создание компонент для автоматизации http...

один вопрос по этому поводу, а у тебя есть в дельфи закладки с названиями начинающимися с IW ххх (Intra Web)? и что ты про них думаеш?


 
Пит   (2009-06-30 19:23) [8]


> про создание компонент для автоматизации http...

ну это очень приблизительное описание, ну что-то типа.


> а у тебя есть в дельфи закладки с названиями начинающимися
> с IW ххх (Intra Web)? и что ты про них думаеш?

никогда не работал. Почитал в вики: http://ru.wikipedia.org/wiki/IntraWeb

почитал статейку про IW... Сложилось мнение, что данная технология сильно ограничит возможности.

У нас, во-первых, свой HTTP-сервер (основанный на idHTTP). Свои стили CSS, подсказки, свой дизайн продукта, логика настроек и редактирования. Плюс приложение не использует форм, управляется только через WEB, работает в качестве сервиса... Боюсь, заточка IW под проект будет куда геморойнее, чем реализовать изложенную задачу.

Единственное, чую не встречу человека, который сможет детально понять проблемы ((( Сейчас копаю в сторону того, что сторонний универсальный вспомогательный класс может задавать свойства Delphi объектов через RTTI, ошибки генерировать через исключения в Set"ерах.

Но как конкретно это будет выглядеть. Как организовать редактирование, создание нового объекта... Как связать с сохранением в БД, не писать же в коде каждого set"ера сохранение этого свойства в БД отдельно...

Блин, просто голова пухнет. Писать все варианты и смотреть времени нету, изначально раставить по полочкам и выбрать оптимальный вариант не получается, не хватает опыта, памяти и мозгов ))


 
DVM ©   (2009-06-30 20:40) [9]

Мне тоже было бы интересно услышать кто как подходил к решению данной задачи, описанной Пит-ом.


 
test ©   (2009-06-30 21:46) [10]

Пит   (30.06.09 18:32) [6]
DAO DB это просто требования к классам(программе) как оно должно фунциклировать, реализовать можно хоть на Бейсике.


 
Пит   (2009-06-30 21:49) [11]


> DAO DB это просто требования к классам(программе) как оно
> должно фунциклировать, реализовать можно хоть на Бейсике.
>

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


 
test ©   (2009-06-30 21:55) [12]

Пит   (30.06.09 21:49) [11]
У тебя есть класс основной который занимается соединением и тд, есть соглашения как должен работать класс который таблицу перекрывает. Ты создаешь класс таблицы получаешь запись, меняеш его, сохраняеш состояние, сумбурно мал мало, посмотри в инете там толково расписано, как многие технологии Java они представляет собой описание реализацию делает каждый по своему. Сама технология описывает основные правила и требования к группе классов.


 
Пит   (2009-06-30 22:26) [13]

Попытаюсь кратко подвести размышления, на которых я остановился.
У нас есть три аспекта:

- база данных, которая хранит настройки конфигурации
- некий функциональный код, реализующий нужное нам поведение на основе параметров объектов (свойства которого и хранятся в БД)
- WEB-интерфейс по управления и настройке свойств объектов

Все эти три аспекта должны быть реализованы. Сразу вопрос - где? Сам функционал объекта должен уметь работать с БД, с WEB-параметрами или это отдельный вспомогательный класс, как он будет интегрирован с конкретным объектом?

Грубо говоря у объекта есть:

property Number: integer write xxx read yyy;

Где и кто должен сохранять / загружать это свойство из БД и управлять этим свойством через WEB?

Сохрание свойства. Можно сделать через сетер. То есть, присваиваем объекту новое значение свойство - оно тут же сохраняется в сетере в БД, присваивается или в случае неправильно заданного значения выдает исключение.
Такой вариант слишком геморный, в каждом сетере надо генерировать запрос к БД.

Можно сделать это в методе объекта, который по команде сбрасывает все значения свойств в базу. Другой метод из БД загружает свойства.
Плохо с точи зрения начальной инициализации. Допустим, при 100 объектах надо 100 раз вызвать метод загрузки свойств из БД, будет сгенерировано 100 запросов. Да и тот кто для 100 объектов будет вызывать эти методы загрузки сам должен уметь работать с БД, чтобы получить список всех объектов и для каждого вызвать метод загрузки.

Можно сделать внешний класс, который при инициализации сделает общий SELECT и по циклу инициализирует все объекты, одна запись в БД = один объект.

Опять же, проверка в сетерах на допустимость значений - это прекрасно. Но допустим нужно проверить не только разрешенный диапазон, но и проверить при загрузке / создания объекта со свойством CodeName равным CodeValue не существует ли уже другого такого объекта с таким же значением CodeName. Сетер класса должен знать о всех других экземплярах данного класса значит. или должен быть вышестоящий управляющий объект со списком всеъ экземпляров, тогда логично в него перенести функционал по работе с БД и WEB...

Блииин, короче я просто не в состоянии продумать адекватную логику. И так, и эдак всплывают подводные камни.

Лучшее, что я сейчас реализовал - это "Пример 2" из поста [2]

Некий универсальный класс, который первоначально нужно настроить на свойства объекта аля:

Config.PKName = "ID";

Config.AddProperty("Name", "Название магазина", tpTextNotEmpty, "", 50, 20);
AddProperty("Priority", "Приоритет", tpInt, "", 3, 3);


Где Name название свойства в БД и в WEB-запросах. Далее идет описание свойства (для текстового описания ошибок), потом тип этого поля (в данном случае текстовое поле, которое не может быть пустым, потом его значение по-умолчанию для нового объекта, размер и максимальная длина в WEB-форме.

В результате этот класс на основании настроек умеет:

- по данным WEB / POST / GET запроса вычленять параметры и сохранять их в БД (редактирование объекта)
- ... создавать в БД новый объект
- ... удалять объект из БД по primary key
- выдавать настройки объекта в виде HTML таблицы
- выдавать HTML таблицу с настройками по-умолчанию для создания нового объекта

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

Поэтому плюс нужно уметь загружать настройки из БД.

И алгоритм немного порочный получается.

- При редактировании по WEB настроек объектов - через универсальный класс они сохраняются в БД
- далее вызывается функция, которая очищает список всех объектов из программы и загружает их всех заново скопом из БД

Минусы очевидны... Но что дальше делать - не знаю. ((


 
antonn ©   (2009-06-30 23:46) [14]

странно что в теме еще не прозвучало слово "xml" :)


 
sniknik ©   (2009-07-01 01:42) [15]

> странно что в теме еще не прозвучало слово "xml" :)
а это что, панацея от всего? или для всего, как бы это правильно выразить.

перечитал, и наконец то все. стало непонятно почему такой упор на дельфи и класс/компонент в дельфи.
"плясать" то тут, имхо, надо от клиента, т.е. html странички, и проверки все в ней яваскриптом делать, и введенное из нее посылать... (а там кстати xml формировать посложнее чем тот же json), на сервере же (в программе) только принять готовые данные и в базу(или ini, без разницы) положить. ну и послать сам html естественно, готовый, просто в нужные места дефоултные значения/старые настройки из базы подставлять.
зачем тут "пляски" с классами, не пойму.


 
antonn ©   (2009-07-01 02:03) [16]


> а это что, панацея от всего? или для всего, как бы это правильно
> выразить.

а это хорошо бы вписалось в протокол обмена через html, и при должном следовании стандарту был бы неплохой независимый интерфейс обмена под разные клиенты, оформленный отдельным "звеном". А пока я почитал автора и какая то каша, точно не понять что не получается, что хочется.
И со стороны расширяемости все таки получше, в данном случае гораздо лучше ini-структуры, удобней описывать св-ва и значения.
Чтоб не делать всякие проверки в коде типа:
if ParamList["action"] = "add" then


 
test ©   (2009-07-01 05:25) [17]

Пит   (30.06.09 22:26) [13]
Поищи в интернете требования/описания/пояснения к Data Acess Objects там готовый вариант алгоритма на описанное тобой лежит, в том числе и с подводными камнями.


 
test ©   (2009-07-01 09:33) [18]

antonn ©   (01.07.09 02:03) [16]
И Ajax и DataMinig и SAX и ....
Не плоди сущности без надобности, бритва Аккамы.


 
makvell   (2009-07-01 10:32) [19]

Послушай test, он дело говорит ;)

Почитать можно, например тут: http://javagu.ru/portal/dt?last=false&provider=javaguru&ArticleId=GURU_ARTICLE_81158&SecID=GURU_SECTION_ 80703
это жава, но проблем с пониманием сути быть не должно :)


 
makvell   (2009-07-01 10:36) [20]

И кстати, по поводу XML, знаю что тут есть его большие "не любители" :) но суть не в этом, как раз тут оно может быть уместно в случае, когда веб морда должна/может меняться, при этом не хочется править код, который ее генерирует. Это, конечно, добавление еще одного звена но... допустим сервер генерит не html, а xml, после чего это xml при помощи xslt трансформируется в нужный нам html. Захотели что-то поменять, поправили трансформацию, ничего не компилим, в код не лезем. Но это просто так, вдруг тоже пригодится :)


 
Пит   (2009-07-01 12:08) [21]


>  стало непонятно почему такой упор на дельфи

потому что программа на дельфи разрабатывается?!


> и класс/компонент
> в дельфи

о компоненте речи не шло. Что касается оформления в виде набора функций / классом - ну так вопрос не принципиальный. Но у нас же все таки ООП, так что наверное лучше оперировать объектами.


> "плясать" то тут, имхо, надо от клиента, т.е. html странички,
>  и проверки все в ней яваскриптом делать

нуууу, братец... Ты сейчас сказанул.

Элементарно отключен JS в браузере и все. Браузер тебе пошлет какие угодно данные (это мы не рассматриваем примеры, когда насильно будут посылать неверные данные). Например, в качестве системного номера тебе прийдет строка:

SysNum=Hi man

И что ты будешь делать? Сделаешь аля:

INSERT INTO ... SysNum = :param1

Query.SetParamByName("param1").AsInteger := StrToInt(PostData["sysnum"]) ;

А там строка - получишь исключение. Клиенту максимум выведется на английском языке стандарный текст исключения об ошибке преобразования. А если параметра нету?

А если тебе нужно гарантировать, что данный SysNum уникальный, что ты там проверишь с помощью JS?

Ты все таки не въехал в проблему. Проблема лежит на НИЗКОМ уровне. Ты просто представь тот объем однотипного кода лишь по извлечению параметров из HTTP-запроса у разных классов (ну или частей кода в общем случае), проверка параметров, запись в БД.

Блин, ну я в общем подозревал, что это бесполезно. Кто реально не пытался такое писать и не понял всего гемора - так не поймет (((


 
Пит   (2009-07-01 12:12) [22]

ну а про DAO почитал - это связь реляционной БД с ООП моделью. Ну это вообще о другом.


 
test ©   (2009-07-01 12:13) [23]

Пит   (01.07.09 12:08) [21]
>>Блин, ну я в общем подозревал, что это бесполезно. Кто реально не пытался
такое писать и не понял всего гемора - так не поймет (((

makvell   (01.07.09 10:32) [19]
Почитать можно, например тут: http://javagu.ru/portal/dt?last=false&provider=javaguru&ArticleId=GURU_ARTICLE_81158&SecID=GURU_SECTION_  80703
это жава, но проблем с пониманием сути быть не должно :)

Какая разница на каком языке дока, главное что Тьюринговые языки, ты почитай ознакомься.


 
Пит   (2009-07-01 12:25) [24]


> ты почитай ознакомься

см [22]


 
Sergey Masloff   (2009-07-01 13:11) [25]

Зачем весь этот геморрой с классами? Классы ради классов?
Вся эта задача решается элементарно процедурно на встроенном процедурном языке СУБД (если таковой имеется - я бы все остальные варианты просто не рассматривал).
 На стороне клиента функции - трубы "записать параметр", "зачитать параметр", "зачитать все параметры определенного объекта"
 Как все это работает можно посмотреть на сайте Ингоса если есть хоть один наш полис - зарегистрироваться и посмотреть свои настройки в личном кабинете ;-) Все работает именно так как я написал. Весь код несколько строчек на PL/SQL который может поддерживать человек слыхом не слыхавший про дельфи, джава и це с решеткой а прочитавший тонкую брошюрку по процедурному программированию ;-)


 
test ©   (2009-07-01 13:16) [26]

Пит   (01.07.09 12:25) [24]

(*псевдокод похожий на Дельфи*)
TAbstractTable= class
(*тут DML и Select*)
.....
end;

TTable = class(TAbstractTable)
private
  fNumber:integer;
  procedure SetNumber(i:integer);
...
published
  property Number: read fNumber write SetNumber;
...
end;

procedure TTable.SetNumber(i:integer);
begin
....
if i > 10000 then
 RaiseException("Выход за диапазон");
....

end;

Ошибку обрабатывает твой личный обработчик но ему не надо возиться с открытием соединений с БД и запросами, логика на TTable, запросы на TAbstractTable.


 
test ©   (2009-07-01 13:18) [27]

Sergey Masloff   (01.07.09 13:11) [25]
Каждому свое.


 
Пит   (2009-07-01 13:18) [28]

Эх, попытка номер 5 )))

У нас фактически три сущности: WEB, БД, функционал

- WEB это совокупность действий, алгоритмов по приему и ответу на WEB / HTTP / POST /GET запросы и взаимодействие с функционалом
- БД это совокупность действий, связывающих функционал и базу данных
- сам функционал это то, что обеспечивает работу объекта )))

Возьмем функциональный класс, допустим который хранит информацию о ВСЕХ клиентах

TClients = class
 function AddClient(Name, Family, ...): boolean;
 function GetClientByName(Name: string): TClientInfo;
 function DeleteClientByID(ID: int64): boolean;
...
end;


Первые вопросы - где и кто будет реализовывать функционал по работе с WEB и БД?

Работа с WEB

Это может быть или частью функционала TClients:
>function TClients.WEBRequest(WebParams: TStrings): string;
Или реализовано во внешнем коде.

В первом случае мы сталкиваемся с записью аля из первого примера в посте [1]. Вручную обработка каждого параметра, выдача ошибок - ужасно. И каждый раз этот функционал нужно повторять в каждом классе: в TOperators, в TPlaces... и т.д.

Во втором случае (вынесено во внешний код) возможно написание некоего универсального класса. по обработке POST-запросов. Но без работы с БД от него мало толку.
Этот универсальный класс не может настраивать конкретные классы аля TClients, TOperators по понятным причинам (ну вот только если грамотно как-то приплести RTTI, но как...). Этот универсальный класс можно настраивать типа того:

>Config.AddProperty("Name", "Название магазина", tpTextNotEmpty, "", 50, 20);
>Config.AddProperty("Priority", "Приоритет", tpInt, "", 3, 3);

И вот он уже готов обрабатывать сущности, имеющие свойства Name и Priority. В смысле работы с WEB он можнт принимать набор параметров (не редактирование, удаление, добавление), вычленять их (по настройкам), проверять правильность. Как-то так:

>Error := Config.CheckParam(WebParams);

На основе заданного описания (AddProperty) он проверяет правильность HTTP параметров, выдает ошибки. Упрощает немного дело, но лишь очень частично.
Можно его завязать еще и на БД. Тогда помимо проверки он еще будет писать / извлекать данные из БД. Что-то вроде:

Config.AddRecord(WebParams) - после проверки добавляет новую запись на основании описания
Config.DeleteRecord(WebParams) - удаляет некую сущность из БД

Опять же на основании заданного описания может генерировать в HTML виде описания сущости из БД :
Config.GetHTMLView(PKValue: int64): string;

Это уже сильно упрощает дело. Но при этом разрывается связь между функционалом (классами TClients, TOperators) и базой данных. Фактически, такой вспомогательный класс TConfig становится провайдером по редактированию БД, без привязки к функциональным классам. У меня пока это обходится где-то так:

{ поступил WEB-запрос }
Config.AddRecord/EditRecord (WebRequest);
Clients.Clear;
Client.LoadAllFromDB;

То есть, фактически, например при добавлении нового экземпляра сущности через WEB - оно через настроенный элементарным образом TConfig пишется в БД с контролем ошибок, а далее идет приказ функциональному классу перезагрузить все из БД.

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

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


 
sniknik ©   (2009-07-01 13:24) [29]

> Браузер тебе пошлет какие угодно данные
ничего он не пошлет, если "посылалка" будет на том же яваскрипте (а много данных посылать придется именно так). и кроме того раз он отключен то лучше выдать страницу с отказом работать без него чем бесполезную форму ввода параметров.

> (это мы не рассматриваем примеры, когда насильно будут посылать неверные данные).
кто? разве не ты пишешь обе стороны, и клиент и сервер?

> И что ты будешь делать? Сделаешь аля:
если там должно быть число, то так и буду работать с ним как с числом.

> А там строка - получишь исключение.
ну и что? нормальное поведение, по нему буду знать что сам где то в передаче напортачил, а исключение обработаю как обычно, сохраню в лог, и выдам его в ответ вместо данных (и вместо таблички например у меня на странице появится ошибка от базы, типа "неверное значение параметра" или "ошибка преобразования").
ничего страшного в этом нет.
ты не путай проверку всего и вся, всякой мелочи типа, входит ли число в диапазон, с нормальными исключениями.  

> А если тебе нужно гарантировать, что данный SysNum уникальный, что ты там проверишь с помощью JS?
а почему нет? если нужно его генерить на клиенте то использую гуид. на крайний случай будет исключение. не проблема.

> Ты все таки не въехал в проблему.
я ее не вижу, в моем представлении как это реализовать.  

> тот объем однотипного кода лишь по извлечению параметров из HTTP-запроса
вот весь однотипный объем -
JSONObject.Text:= ARequestInfo.Document;
для получения готового распаренного объекта со структурой как xml-документа (работал с xml?). это если передавать json-ом, как советовал, и использовать их компонент для дельфей. (в обратную сторону и этого не надо, стандартная функция eval делает стандартный "родной" обьект в яваскрипте (и да там можно сказать что ее использование небезопасно, но это решаемо, а для варианта когда и то и то свое попросту не нужно))

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


 
Пит   (2009-07-01 13:30) [30]


> Sergey Masloff   (01.07.09 13:11) [25]

Сергей, не очень понятно. Можно на примере привести путь прохождения данных?

Допустим, со стороны клиента от заполненной формы мы получаем следующие WEB параметры типа:

action=edit
id=45
Name=Sergey
Number=4574


Логически это означает отредактировать клиента с ID равным 45 и присвоить ему (поля в БД) имя сергей и номер 4574. Можешь описать всю цепочку прохождения и обработки данных?


> procedure TTable.SetNumber(i:integer);
> begin
> ....
> if i > 10000 then
>  RaiseException("Выход за диапазон");
> ....
>
> end;

test, пример абсолютно понятный, но ЧТО идет в случае else? ))
Если больше 10000 - исключение, ок. А если параметр верный, какой код в else который по идее должен сохранить параметр в БД?

И еще раз, я тебе уже говорил - это ты говоришь о связи реляционной БД с ООП моделью. Но у меня задача иная, там еще сильно впрягается управление по WEB.


 
test ©   (2009-07-01 13:31) [31]

(*Псевдо код похожий на Дельфи*)

TAbstractWeb=class
// Стандартная обработка
end;

TDEfaultPage=class(TAbstractWeb)
// Обработка Default Page
end;

TMailPage=class(TAbstractWeb)
// Обработка почты
end;

ИМХО Вот как то так


 
Polevi ©   (2009-07-01 13:47) [32]

Для каждого объекта создается набор хранимых процедур для создания, сохранения, удаления и выборке по ID
Пишется генератор, который используя информацию о метаданных базы через INFORMATION_SCHEMA генерирует классы-обертки, которые умеют эти процедуры вызывать.
Реализация ф-ии WebRequest сводится к поиску нужного типа, инстанцированию и вызова метода.


 
Polevi ©   (2009-07-01 13:49) [33]

Да, и генерируемый объект также имеет ф-ию Render, которая возвращает html представление


 
makvell   (2009-07-01 13:57) [34]


> test ©   (01.07.09 12:13) [23]
>
> makvell   (01.07.09 10:32) [19]
> Почитать можно, например тут...
>
> Какая разница на каком языке дока, главное что Тьюринговые
> языки, ты почитай ознакомься.


Не совсем понял, к чему наезд на меня :) я сказал, что ты прав, и что близко к использованию ДАО, дал пример (описание) как оно в яве. Не думаю что мне стоит с чем-то еще знакомиться, уже знаком и вполне успешно использую ;) и вообще я не вижу проблемы у топик стартера, такое ощущение, что это обсуждение ради обсуждения, типа "вы все не правы, мне нужно что-то другое, а что я и сам не знаю".


 
Пит   (2009-07-01 13:59) [35]


> ничего он не пошлет, если "посылалка" будет на том же яваскрипте

нуу... Это значит система станет ненастраиваемой в браузерах с отключенным JS. Такой вариант неприемлим.

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


> а почему нет? если нужно его генерить на клиенте то использую
> гуид. на крайний случай будет исключение. не проблема

ты не понял. Допустим, есть список устройств. У каждого устройства есть номер, не ID из БД, а конкретно задаваемый номер, идентифицирующий устройство в транспортной сети. У каждого устройства допустим свой адрес, но при этом адреса не могут повторяться. Где ты будешь делать проверку, в JS? Тебе же нужен доступ к данным всех устройств, чтобы понять, что новый заданный номер не катит по причине уже наличия данного номера у другого устройства.

Нее, полностью переложить проверку на JS нереально, он не обладает данными для проверки. Только так, максимум контроль передаваемых параметров, число, строка, не пустая строка и подобное, на уровне типов.


> JSONObject.Text:= ARequestInfo.Document;
> для получения готового распаренного объекта со структурой
> как xml-документа

а что там парсить в HTTP-параметрах? ребенок справится.
Вопрос не в том, чтобы распарсить (у меня точно такие же распарсенные параметры в TStringList хранятся), а в том, чтобы присвоить эти значения функциональному коду.


 
Sergey Masloff   (2009-07-01 14:05) [36]

Пит   (01.07.09 13:30) [30]
Конечно. Только нужно на стороне сервера еще как-то отличить что это за команда (или писать по паре процедура сервера приложений -процедура в субд. Предлагаю вариант с одной процедурой на стороне веб-сервера. Назовем ее ApplyCommand

допустим с такой сигнатурой (абстрактно)

ApplyComand(inBuf, outBuf : string) : boolean;

Тогда примерно так
Клиент передает данные как строку через get или post
 |
Сервер перехватывает строку и (возможно) приводит ее в нужный вид. После преобразования параметров вызывается функция ApplyCommand в которую как входной буфер отдаются параметры приехавшие с клиента. Этот код написан и отлажен 1 (один) раз и не меняется никогда
 |
Процедурный код СУБД
Пусть процедура называется
 ApplyCommandImp() она
    - вызывает другую процедуру которая по буферу понимает что это за команда допустим GetCommandCode(inBuf : string) : integer;

  тогда ApplyCommand() выглядит так

 begin
    case GetCommandCode(inBuf : string) of
    0 : return SaveClientParams(inBuf, outBuf);
    1 : return ReadClientParams(inBuf, outBuf);
    2 : return SomeOtherUsefulInfo(inBuf, outBuf);
    ....
    else
       return StdErrorMsg(inBuf, outBuf);
 end


а например SaveClientParams(inBuf, outBuf); выглядит так

 begin
   if AnalyzeInputParams(inBuf) then begin
      update Client set Name=GetParam(inBuf,"name"), Number=(inBuf,"number") where id = GetParam(inBuf,"id")
      if (ничего не проапдейтилось)
      insert into Client(Name, Number, id) values (Params......
      outBuf = (текст, HTML, XML о том что все в порядке)
      return true;
   else return ReportInvalidParams(outBuf) // в буфер запишем что там плохого - можно сразу как HTML (XML)
 end;

 
Ну что-то в этом роде. На практике все несложно получается и каждая процедура примитивна - даже документировать не нужно
   
 

<>


 
test ©   (2009-07-01 14:06) [37]

makvell   (01.07.09 13:57) [34]
Опечатка, хотел Питу отправить тебя целеком переписывать было лень скопировал. Немного криво получилось ))


 
Пит   (2009-07-01 14:12) [38]


> Для каждого объекта создается набор хранимых процедур для
> создания, сохранения, удаления и выборке по ID
> Пишется генератор, который используя информацию о метаданных
> базы через INFORMATION_SCHEMA генерирует классы-обертки,
>  которые умеют эти процедуры вызывать.
> Реализация ф-ии WebRequest сводится к поиску нужного типа,
>  инстанцированию и вызова метода.

это прекрасно, но я это уже сделал.

Это фактически провайдер по редактированию записей в БД. Он связывает WEB и БД, но он никоим образом не связывает это с функциональными классами!!! Вы каждый раз предлагая такой вариант выкидываете один компонент - сам функционал программы.

Я писал в постах [2] и [28] как это реализовано в моем случае. Тоже самое, что ты предложил, только прототип объекта (сущности) берется не из метаданных БД, а настраивается вручную аля:

Config.AddProperty("Name", "Название магазина", tpTextNotEmpty, "", 50, 20);

Это и задает его отображение в WEB (функция Render), и задает способы записи в БД.
Вызов описанных тобой хранимок заменен у меня опять же на основании заданного прототипа сущности на исполнение запросов INSERT, UPDATE и DELETE. В используемой БД нету хранимок, да и не принципиально это. Такой функционал уже есть, он реализован.

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

В WEB интерфейсе клиент УДАЛЯЕТ некий экземпляр сущности. Провайдер TConfig понимает это и удаляет из БД соответствующую запись. Но как это изменение скажется в программе? Грубо говоря класс TSomebodyCollection, который хранит информацию о всех сущностях - как узнает об изменениях?


 
Polevi ©   (2009-07-01 14:24) [39]

>класс TSomebodyCollection как узнает об изменениях?
а зачем он хранит эту информацию ? web приложение stateless по умолчанию
запросит броузер этот список - выдай ему свежие данные
а вся валидация на уровне базы - констрэйнтов и хранимых процедур


 
Polevi ©   (2009-07-01 14:28) [40]

если ты хочешь отобразить данные на объектную модель и в дальнейшем работать со списками и экземплярами связаных сущностей - тебе нужен ORM.. чтото вроде Hibernate



Страницы: 1 2 вся ветка

Форум: "Прочее";
Текущий архив: 2009.08.30;
Скачать: [xml.tar.bz2];

Наверх




Память: 0.64 MB
Время: 0.006 c
1-1212999896
Separator
2008-06-09 12:24
2009.08.30
Построение и расчет графов


2-1246216548
Pasha
2009-06-28 23:15
2009.08.30
Обращение к ячейкам в БД


2-1246017930
VoyagerEternal
2009-06-26 16:05
2009.08.30
Как продолжить выполнение программы после искл. ситуации(raise)?


15-1246131261
DillerXX
2009-06-27 23:34
2009.08.30
Вопрос по терверу


9-1182001225
Li-ion
2007-06-16 17:40
2009.08.30
Насчет графики





Afrikaans Albanian Arabic Armenian Azerbaijani Basque Belarusian Bulgarian Catalan Chinese (Simplified) Chinese (Traditional) Croatian Czech Danish Dutch English Estonian Filipino Finnish French
Galician Georgian German Greek Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Korean Latvian Lithuanian Macedonian Malay Maltese Norwegian
Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish Bengali Bosnian
Cebuano Esperanto Gujarati Hausa Hmong Igbo Javanese Kannada Khmer Lao Latin Maori Marathi Mongolian Nepali Punjabi Somali Tamil Telugu Yoruba
Zulu
Английский Французский Немецкий Итальянский Португальский Русский Испанский