Главная страница
Top.Mail.Ru    Яндекс.Метрика
Текущий архив: 2009.08.30;
Скачать: CL | DM;

Вниз

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;
Скачать: CL | DM;

Наверх




Память: 0.66 MB
Время: 0.015 c
15-1246138614
Германн
2009-06-28 01:36
2009.08.30
Тем, кто успел что-то скачать по ссылкам Коляна


2-1245711399
Снегурочка
2009-06-23 02:56
2009.08.30
Передача данных Tcp/ip


2-1246660919
Frizel07
2009-07-04 02:41
2009.08.30
Не загружается GIF из .res файла.


15-1246469626
dmk
2009-07-01 21:33
2009.08.30
Бесперебойники


2-1246452586
student_91
2009-07-01 16:49
2009.08.30
Canvas