На этом шаге мы рассмотрим компоненты TDataSetTableProducer и TDataSetPageProducer палитры Internet.
Воспользуемся базой данных DBDemos из поставки Delphi 6.0, а из нее выберем таблицу events.db. В таблице перечисляются ожидаемые спортивные состязания.
В поле Event_Name занесено название соревнования, поля Event_Date и Event_Time - дата и время соревнования, а в Memo-поле Event_Description приводится описание соревнования.
Event_Photo это BLOB-поле, в котором хранятся фотоснимки с сюжетами соревнований и последнее поле, Ticket_price, - это цена билета на означенное соревнование.
В программе сначала показывается все содержимое таблицы в HTML-документе. Откуда по щелчку "мыши" на нужной записи будет выводиться подробное описание соревнования с выводом графического изображения.
Создадим новое Web-приложение. Поместим на его форму компонент ТТable, и подключим его к таблице events.db.
Зададим в свойстве DataBaseName имя DBDemos, а свойству TableName присвоим значение events.db. При Web-запросе без параметров, когда путь не указан, будем выводить содержимое отобранных столбцов всей таблицы. В другом варианте запроса, со значением пути равным, например строке "/full", будем выводить подробную информацию о состязании.
Добавим на форму компонент TDataSetTableProducer, и создадим объект-действие по имени initial, с пустым значением PathInfo и свойством Enabled равным True. Вслед за этим добавим действие по умолчанию: назовем его default, а путь оставим пустым. Поставим отметку на свойстве Default, a Enabled установим в False. Последняя установка делается для того, чтобы это действие гарантированно выполнялось только в самую последнюю очередь. Это действие по умолчанию будет выдавать сообщение об ошибке.
Далее необходимо связать объекты DataSetTableProducer1 и Table1. Для этого указываем имя Table1 в свойстве DataSet поставщика данных.
Заголовок всей таблицы находится в свойстве Caption. В свойствах Footer и Header задаются необходимые заголовочные и заключительные HTML-теги, которыми поставщик обрамляет данные из таблицы базы данных, чтобы получить корректный HTML-документ, понятный Web-браузеру.
В этой программе для Header используется следующий фрагмент:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN"> <HTML> <HEAD> <TITLE>3аказ билетов на спортивные состязания</TITLE> </HEAD> <BODY>
В свойстве Footer указывается только две строки
</BODY> </HTML>
В ответ на активизацию свойства Columns загрузится окно Редактора этого свойства, показанное на рисунке 1. Основное, что можно сделать в этом редакторе, это отобрать нужные поля для вывода в таблицу.
Рис.1. Окно редактора свойства Columns
Кроме этого можно задать имена для выводимых колонок и настроить цвет и обрамление. Если в таблице полей много, а вывести нужно меньшую их часть, то в этом случае, можно добавить их по одному, нажимая на кнопку Add New. Либо, воспользовавшись кнопкой Add All, добавить все поля сразу, а затем, удалить ненужные с помощью кнопки Delete Selected. Если выбрать поле в правом окне редактора и раскрыть свойство Title в окне Инспектора Объектов, то можно поменять имя заголовка столбца, его цвет и выравнивание. Далее вводим строку кода в обработчик события OnAсtion объекта-действия initial.
procedure TWebModule1.WebModule1initialAction(Sender: TObject; Request: TWebRequest; Response: TWebResponse; var Handled:Boolean); begin Response.Content:=DataSetTableProducer1.Content; end;
Оформим обработчик события ОnAсtion для действия по умолчанию, который может выглядеть так:
procedure TWebModule1.WebModule1defaultAction(Sender:TObject; Request: TWebRequest; Response: TWebResponse; var Handled: Boolean); begin Response.Content:='<H2>Ошибка запроса!</H2>'; Response.Content:=Response.Content+'<a href=../dstr2.exe> <h2>Назад</h2></a>'; end;
Первая часть задачи выполнена. Клиент может отправить запрос к базе данных на сервер и получить отчет о запросе в окне браузера. Внешний вид итога запроса можно видеть на рисунке 2.
Рис.2. Внешний вид итога запроса к базе данных в окне браузера
Далее реализуем вторую половину задачи: просмотрев весь список соревнований, клиент должен иметь возможность получить полную информацию об интересующем его состязании.
Простейший вариант, это превратить строки с названиями состязания в первой колонке в ссылки. И это можно сделать, если воспользоваться одним из трех событий поставщика, возникающих в процессе формирования таблицы. Эти события называются OnCreateContent, OnGetTableCaption и OnFormatCell.
Первое инициируется перед созданием таблицы. В нем можно внести последние изменения в этот процесс или совсем отменить: если обработчик этого события вернет в параметре Continue значение False, то таблица формироваться не будет.
Второе событие вызывается при формировании заголовка таблицы. В этом обработчике можно изменить название таблицы и задать режим выравнивания текста заголовка.
Третье событие, вызывается при формировании содержимого каждой ячейки.
Ниже приводится текст обработчика этого события:
procedure TWebModule1.DataSetTableProducer1FormatCell(Sender:TObject; CellRow, CellColumn: Integer; var BgColor:THTMLBgColor; var Align: THTMLAlign; var VAlign:THTMLVAlign; var CustomAttrs,CellData: String); begin if (CellRow>0)and(CellColumn=0) then Celldata:='<a href=dstr2.exe\full?EventNo='+ table1.Fields[0].AsString+'>'+Celldata+'</A>'; end.
Параметры этой процедуры включают в себя номера столбца и строки таблицы для обрабатываемой ячейки (нумерация начинается с нуля), цвет фона ячейки, горизонтальное и вертикальное выравнивание, дополнительные атрибуты ячейки и текст, помещаемый в ячейку. В операторе if проверяются номера столбца и строки ячейки, и если ячейка не из первой строки (строки заголовка) и находится в первом столбце, то ее содержимое меняется: добавляются соответствующие теги HTML и данные о пути и запросе. В качестве имени пути здесь вставлена строка full, но объекта-действия для этого пути пока нет.
Далее, за именем пути прописывается строка EventNo=, к которой добавляется содержимое первого поля базы данных, поле EventNo, преобразованное в строку. В результате Query-часть запроса для одной из строк таблицы будет иметь вид EventNo=7. Теперь нужно создать объект-действие для пути /full. Это действие должно сформировать новый документ с подробными данными о выбранном спортивном состязании. Для данной цели будет использоваться другой поставщик данных из таблиц данных: ТDataSetPageProducer. Добавим его в форму приложения. В свойстве DataSet этого компонента нужно указать таблицу, с которой работает поставщик, - Table1 в данном случае. Затем заполняем свойство HTMLDoc шаблоном, заготовкой того документа, который должен получить клиент.
Шаблоны использует еще один компонент из панели Internet: TPageProducer. Когда в процессе обработки документа, компоненту встречается шаблон, он инициирует событие OnHTMLTag. В обработчике этого события должна пройти замена шаблона на соответствующие данные. Но в случае рассматриваемого компонента, TDataSetPageProducer, обработкой шаблонов можно и не заниматься, если дать им имена совпадающие с именами полей таблицы данных. В этом случае компонент сам произведет замену шаблонов на значения соответствующих полей текущей записи таблицы данных. В качестве "шаблонного" документа можно использовать следующий текст:
<html> <head> <title>Обзор спортивного состязания</title> </head> <Table Width="100%" Border=1 BgColor="Silver"><Caption>Заказ билетов на спортивные соревнования</Caption> <TR><TH Align="Right">Номер по порядку</TH> <TH Align="Left"><#EventNo></TH></TR> <TR><TH Align="Right">Название состязания</TH> <TH Align="Left"><#Event_Name></TH></TR> <TR><TH Align="Right">Дата проведения</TH> <TH Align="Left"><#Event_Date></TH></TR> <TR><TH Align="Right">Время проведения</TH> <TH Align="Left"><#Event_Time></TH></TR> <TR><TH Align="Right">Цена билета</TH> <TH Align="Left"><#Ticket_price></TH></TR> </Table> <br><br> <#Event_Description> <br><br> <#Event_Photo> <br><br> <a href="../dstr2.exe">Вернуться назад</a> </body> </html>
В тексте фрагмента программы хорошо видны шаблоны, имена которых совпадают с именами полей таблицы events.db. В конце текста выводится ссылка, активизировав которую, можно вернуться к полному списку записей таблицы.
Далее нужно создать объект-действие для значения пути full, у него в свойстве PathInfo задается строка /full. Если выбрать из списка значений свойства Producer значение DataSetPageProducer1 и откомпилировать проект, то получим следующее: при выборе разных строк таблицы выводятся данные только из первой записи. И это закономерно, так как данные для получения сведений из нужной записи есть, но они никак не используются.
Очевидно, самым простым решением в данном случае было бы простое перемещение указателя в таблице на отобранную запись. И сделать это можно в обработчике события OnAction объекта-действия full. Но чтобы этот обработчик срабатывал первым, необходимо очистить свойство Producer и вызывать нужный поставщик из обработчика события OnAction. Обработчик будет выглядеть следующим образом:
procedure TWebModule1.WebModule1fullAction(Sender: TObject; Request: TWebRequest; Response: TWebResponse; var Handled:Boolean); var q1:string; options:TLocateOptions; begin q1:=Request.QueryFields.Values['EventNo']; Table1.Locate('EventNo',q1,options); Response.Content:=DataSetPageProducer1.Content; end;
Здесь в переменную q1 помещается значение для поля EventNo, затем выполняется переход к соответствующей записи таблицы, которая становится теперь текущей записью, а в последней строке вызывается функция Content поставщика данных, который подставляет на место шаблонов, значения из текущей записи таблицы и возвращает сформированный документ свойству Content объекта Response.
В таблице events.db имеются еще графические изображения и Mемо-поля, их также нужно выводить в ответ на запрос пользователя. Сейчас вместо графики и текста из Mемо-поля можно увидеть в браузере "условные обозначения" (MEMO) и (GRAPHIC). Для работы с этими полями придется ввести в обработчик события OnHTMLTag компонента TDataSetPageProducer следующий код:
procedure TWebModule1.DataSetPageProducer1HTMLTag(Sender:TObject; Tag: TTag; const TagString: String; TagParams: TStrings; var ReplaceText: String); begin if CompareText(TagString,'Event_Photo')=0 then ReplaceText:='<img src=Photo?EventNo='+ table1.Fields[0].AsString+'>'; if CompareText(TagString,'Event_Description')=0 then ReplaceText:=Table1.FieldByName('Event_Description').AsString; end;
Что касается обработки Mемо-поля, то здесь все делается в одну строку, а за рисунком придется еще раз обратиться на Web-сервер. Здесь формируется тег HTML, используемый для описания графических изображений, который ссылается на приложение (имя приложения и адрес сервера браузер в данном случае подставляет сам) с новым значением пути Photo, за которым следует ссылка на нужную запись таблицы данных. Естественно, нужно добавить в приложение еще один объект-действие, обработчик события OnAction, код которого приводится ниже:
procedure TWebModule1.WebModule1PhotoAction(Sender: TObject; Request: TWebRequest; Response: TWebResponse; var Handled:Boolean); var B:TBitmap; S:TMemoryStream; q1:string; options:TLocateOptions; begin q1:=Request.QueryFields.Values['EventNo']; Table1.Locate('EventNo',q1,options); B:=TBitmap.Create; B.Assign(Table1.FieldByName('Event_Photo')); S:=TMemoryStream.Create; B.SaveToStream(S); S.Position:=0; Response.ContentType:='image/x-xbitmap'; Response.ContentStream:=S; B.Free; end;
В вышеприведенном фрагменте вначале указатель в таблице устанавливается на нужную запись, затем создается объект типа BitMap и в него переписывается содержимое поля Event_Photo. Затем создается поток в памяти и в него переписывается нужный графический образ. Далее остается установить указатель в потоке на начало и передать Web-серверу указатель на поток и тип данных в потоке.
Пример ответа на запрос приведен на рисунке 3.
Рис.3. Ответ на запрос
Листинг программы приводится ниже.
unit DSTR1; interface uses SysUtils, Classes, HTTPApp, DBWeb, DB, DBTables, HTTPProd, DSProd,Graphics; type TWebModule1 = class(TWebModule) Table1: TTable; DataSetTableProducer1: TDataSetTableProducer; DataSetPageProducer1: TDataSetPageProducer; procedure WebModule1initialAction(Sender: TObject; Request: TWebRequest; Response: TWebResponse; var Handled: Boolean); procedure WebModule1defaultAction(Sender: TObject; Request: TWebRequest; Response: TWebResponse; var Handled: Boolean); procedure DataSetTableProducer1FormatCell(Sender: TObject; CellRow, CellColumn: Integer; var BgColor: THTMLBgColor; var Align: THTMLAlign; var VAlign: THTMLVAlign; var CustomAttrs, CellData: String); procedure WebModule1fullAction(Sender: TObject; Request: TWebRequest; Response: TWebResponse; var Handled: Boolean); procedure DataSetPageProducer1HTMLTag(Sender: TObject; Tag: TTag; const TagString: String; TagParams: TStrings; var ReplaceText: String); procedure WebModule1PhotoAction(Sender: TObject; Request: TWebRequest; Response: TWebResponse; var Handled: Boolean); private { Private declarations } public { Public declarations } end; var WebModule1: TWebModule1; implementation {$R *.DFM} procedure TWebModule1.WebModule1initialAction(Sender: TObject; Request: TWebRequest; Response: TWebResponse; var Handled: Boolean); begin Response.Content:=DataSetTableProducer1.Content; end; procedure TWebModule1.WebModule1defaultAction(Sender: TObject; Request: TWebRequest; Response: TWebResponse; var Handled: Boolean); begin Response.Content:='<H2>Ошибка запроса!</H2>'; Response.Content:=Response.Content+'<a href=../dstr2.exe><h2>Назад</h2></a>'; end; procedure TWebModule1.DataSetTableProducer1FormatCell(Sender: TObject; CellRow, CellColumn: Integer; var BgColor: THTMLBgColor; var Align: THTMLAlign; var VAlign: THTMLVAlign; var CustomAttrs, CellData: String); begin if (CellRow>0)and(CellColumn=0) then Celldata:='<a href=dstr2.exe\full?EventNo='+ table1.Fields[0].AsString+'>'+Celldata+'</A>'; end; procedure TWebModule1.WebModule1fullAction(Sender: TObject; Request: TWebRequest; Response: TWebResponse; var Handled: Boolean); var q1:string; options:TLocateOptions; begin q1:=Request.QueryFields.Values['EventNo']; Table1.Locate('EventNo',q1,options); Response.Content:=DataSetPageProducer1.Content; end; procedure TWebModule1.DataSetPageProducer1HTMLTag(Sender: TObject; Tag: TTag; const TagString: String; TagParams: TStrings; var ReplaceText: String); begin if CompareText(TagString,'Event_Photo')=0 then ReplaceText:='<img src=Photo?EventNo='+table1.Fields[0].AsString+'>'; if CompareText(TagString,'Event_Description')=0 then ReplaceText:=Table1.FieldByName('Event_Description').AsString; end; procedure TWebModule1.WebModule1PhotoAction(Sender: TObject; Request: TWebRequest; Response: TWebResponse; var Handled: Boolean); var B:TBitmap; S:TMemoryStream; q1:string; options:TLocateOptions; begin q1:=Request.QueryFields.Values['EventNo']; Table1.Locate('EventNo',q1,options); B:=TBitmap.Create; B.Assign(Table1.FieldByName('Event_Photo')); S:=TMemoryStream.Create; B.SaveToStream(S); S.Position:=0; Response.ContentType:='image/x-xbitmap'; Response.ContentStream:=S; B.Free; end; end.
На следующем шаге мы рассмотрим компонент TPageProducer.