Часть 2. Построение электронного магазина

Часть 2. Построение электронного магазина

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

База данных, спроектированная в главе 3, будет использоваться в главах этой части. Мы возьмем ее за основу и начнем строить на ее основе электронный магазин. В главе 6 рассматривается основной механизм перемещения по сайту, домашняя страница и общая инфраструктура Web-сайта.

В главе 7 создаются базовые средства для построения корзины. В главе 8 мы перейдем к следующей стадии - процессу оформления заказов. Наконец, в главе 9 рассматривается организация обратной связи с покупателем на примере получения информации о состоянии заказов.

Глава 6. Построение пользовательского интерфейса

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

Проектирование основных функциональных возможностей магазина

Наш магазин будет состоять из нескольких страниц ASP, объединенных при помощи приложения Visual Basic. Страницы работают с базой данных, структура которой была определена в главе 3. В табл. 6.1 перечислены страницы нашего магазина с краткими описаниями их функций. Также для каждой страницы указывается номер главы, в которой анализируется ее программный код.

Таблица 6.1. Web-страницы электронного магазина ECStore
Страница Описание Глава
Footer.asp Нижний колонтитул, выводимый в конце каждой отображаемой страницы сайта. Содержит теги, завершающие область основного содержимого Глава 6
Header.asp Верхний колонтитул, выводимый в начале каждой отображаемой страницы сайта. Определяет основную структуру сайта Глава 6
AddItem.asp Включение новой позиции в корзину при выборе товара пользователем Глава 7
Basket.asp Вывод текущего содержимого корзины Глава 7
Confirmed.asp Выводит подтверждающее сообщение и благодарит пользователя за оформление заказа Глава 8
Default.asp Домашняя страница сайта Глава 6
DeleteItem.asp Удаление позиции из корзины Глава 7
Dept.asp Вывод списка всех разделов магазина Глава 6
EmailPassword.asp Отправка по электронной почте пароля, соответствующего заданному профилю Глава 9
EmptyBasket.asp Удаление всего содержимого корзины Глава 7
Global.asa Файл уровня приложения, выполняемый при запуске нового приложения или сеанса Глава 6
OrderHistoryDisplay.asp Вывод истории заказов покупателя Глава 9
OrderReceipt.asp Вывод экранного отчета для полученного заказа Глава 8
OrderStatus.asp Страница, на которой вводится имя и пароль для получения доступа к истории заказов покупателя Глава 9
Payment.asp Ввод данных для выписки счета Глава 8
Product.asp Вывод информации о заданном товаре Глава 6
Products.asp Вывод всех товаров заданного раздела Глава 6
Profile.asp Страница для ввода имени и пароля при чтении и редактировании покупателем своего профиля Глава 9
ProfileDisplay.asp Вывод профиля заданного покупателя Глава 9
Search.asp Поиск товаров в базе данных Глава 6
Shipping.asp Ввод данных для доставки заказа Глава 8
UpdateBasket.asp Обновление корзины новым количеством товаров Глава 7
UpdateProfile.asp Обновление профиля покупателя Глава 9
ValidatePayment.asp Проверка платежных реквизитов, введенных покупателем Глава 8
ValidateShipping.asp Проверка данных доставки, введенных покупателем Глава 8

ПРИМЕЧАНИЕ
В части 3 "Управление магазином" мы создадим инструменты для управления данными магазина. В части 4 "Реклама" будут реализованы дополнительные средства для поддержки различных рекламных функций.

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

Архитектура сайта

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

Мы надеемся, что в ходе просмотра покупатель помещает отобранные товары в корзину. Затем покупатель выполняет с корзиной некоторые операции (изменяет количество единиц товара, удаляет отдельные позиции и т. д.) и переходит к оформлению заказа. В процессе оформления мы получаем ключевые данные покупателя, в том числе данные для выписки счета и доставки. После обработки данных покупатель может просмотреть историю заказов в своем профиле. Общая схема покупки товаров изображена на рис. 6.1.

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

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

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

Создание проекта

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

Сценарии SQL, находящиеся на прилагаемом компакт-диске, заполняют таблицы разделов и товаров примерами данных. Кроме того, на диске присутствует вся графика, использованная в оформлении нашего магазина. Если вы захотите увидеть магазин в работе, зайдите по адресу www.activepubs.com.

Что касается программирования, ASP-код нашего магазина будет относительно простым. Как вы вскоре убедитесь, последовательное и хорошо продуманное распределение кода по страницам решает многие проблемы, возникающие при построении магазина. Имена переменных также выбираются по простым и понятным правилам.

Построение проекта начинается со страницы global.asа, приведенной в листинге 6.1. Функции этого файла вызываются при каждом запуске приложения (запуске/перезапуске Web-сайта) или создании нового сеанса пользователя. Файл содержит специализированные функции, инициируемые в определенной ситуации. Например, функция Application_OnStart выполняется при запуске приложения Web-сайта. Обычно она используется для начальной инициализации параметров. Также существует парная функция Application_OnEnd. Хотя в нашем примере эти функции не используются, они часто применяются для сохранения глобальных данных (например, статистических счетчиков), служебных переменных сервера и т. д.

В нашем магазине используется функция Session_OnStart. Вспомните: как было сказано выше, мы должны отслеживать идентификатор покупателя. В момент посещения идентификатор еще не существует, но в дальнейшем мы собираемся проверить наличие cookie с профилем на компьютере покупателя. Чтобы обеспечить правильную инициализацию этой ключевой переменной, мы обнуляем idShopper. В дальнейшем этой переменной либо присваивается новый идентификатор покупателя, либо существующее значение загружается из профиля, хранящегося на компьютере клиента. У функции Session_OnStart существует парная функция Sess1on_OnEnd, которая в нашем примере не используется.

Листинг 6.1. Global.asa

<SCRIPT LANGUAGE=VBScript RUNAT=Server>

' ****************************************************
' Global.ASA - выполняется при создании нового сеанса для нового покупателя.
' ****************************************************

' Процедура, выполняемая в начале сеанса.
Sub Session_OnStart

' Присвоить идентификатору покупателя нулевое значение.
session("idShopper") = 0

End Sub

</SCRIPT>

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

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

ПРИМЕЧАНИЕ
Для подключения также можно воспользоваться механизмом OLE DB. В процессе разработки и тестирования допускается применение имени SA и соответствующего пароля - это упрощает процесс разработки, но не забудьте установить защиту в окончательной версии.

Загрузка данных

Средства управления магазином еще не готовы, однако мы должны занести в таблицы примерные данные. На прилагаемом компакт-диске находятся сценарии SQL для заполнения таблиц. Давайте рассмотрим процесс вставки данных - это поможет вам лучше разобраться в том, как происходит заполнение таблиц.

Сначала мы заполняем таблицу Department данными о разделах. Запись содержит название раздела, описание и ссылку на графическое изображение. Пример вставки записи приведен в листинге 6.2.

Листинг 6.2. Заполнение таблицы разделов

insert into department(chrDeptName, txtDeptDesc, chrDeptImage) values('Funky Wacky Music','The craziest music you have ever seen. Is it even music?','funk.gif')

Затем мы загружаем информацию о товарах и указываем, к какому разделу относится тот или иной товар. Команда SQL, приведенная в листинге 6.3, создает описание товара- в таблицу Products включается компакт-диск Joe Bob's Thimble Sounds.

СОВЕТ
Не забудьте проверить апострофы в строках и удвоить их перед вставкой в базу данных. Методика замены продемонстрирована в главе 5.

Следующая команда SQL в листинге 6.3 связывает новый товар с одним из разделов. В нашем примере первый товар, вставленный в таблицу, связывается с первым разделом посредством создания записи в таблице DepartmentProducts.

ПРИМЕЧАНИЕ
Сначала запустите сценарий LoadDepts.sql, и только потом - сценарий LoadProducts.sql. Последний предполагает, что записи разделов уже были занесены в таблицу и следуют в определенном порядке.

Листинг 6.3. Заполнение таблицы товаров

insert into products(chrProductName, txtDescription, chrProductImage, intPrice, intActive)
values('Joe Bob''s Thimble Sounds', 'Great thimble music that you will love!', 'thimble.gif', 1000, 1)

insert departmentproducts(idDepartment, idProduct) values(1,1)

Некоторые товары обладают атрибутами, которые также необходимо загрузить. Например, в ассортименте нашего магазина имеются футболки разных цветов и размеров, выбираемых покупателем. Две команды SQL, приведенные в листинге 6.4, включают в таблицу категорий два типа атрибутов: размер (Size) и цвет (Color).

Листинг 6.4. Создание атрибутов

insert into attributecategory(chrCategoryName) values('Size')

insert into attributecategory(chrCategoryName) values('Color')

Значения атрибутов, входящих в эти категории, заносятся в таблицу Attribute. Например, категория Size в нашем примере состоит из атрибутов Small, Medium, Large и X-Large. Команды SQL, приведенные в листинге 6.5, создают атрибуты категорий Size и Color.

Листинг 6.5. Создание категорий атрибутов

insert into attribute(chrAttributeName, idAttributeCategory) values('Small', 1)
insert into attribute(chrAttributeName, idAttributeCategory) values('Medium', 1)
insert into attribute(chrAttributeName, idAttributeCategory) values('Large', 1)
insert into attribute(chrAttributeName, idAttributeCategory) values('X-Large', 1)

insert into attribute(chrAttributeName, idAttributeCategory) values('Red', 2)
insert into attribute(chrAttributeName, idAttributeCategory) values('Blue', 2)
insert into attribute(chrAttributeName, idAttributeCategory) values('Green', 2)
insert into attribute(chrAttributeName, idAttributeCategory) values('White', 2)

Остается связать товары с различными атрибутами. В нашем примере магазин предлагает два типа футболок. Построение комбинаций осуществляется командами, приведенными в листинге 6.6.

Листинг 6.6. Назначение атрибутов

insert into productattribute(idAttribute, idProduct) values(1, 9)
insert into productattribute(idAttribute, idProduct) values(2, 9)
insert into productattribute(idAttribute, idProduct) values(3, 9)
insert into productattribute(idAttribute, idProduct) values(4, 9)

Создание структуры страниц

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

Кроме того, мы создадим ссылки для получения информации о состоянии заказа и операций с профилем покупателя.

Рис. 6.2. Интерфейс перемещения по электронному магазину

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

Страницы Header и Footer

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

В этом случае необходимо решить две задачи. Во-первых, мы проверяем, имеется ли на компьютере пользователя cookie с предыдущим идентификатором покупателя. Для этого мы получаем значение cookie с именем WWCD. Если оно равно пустой строке, значит, необходимо создать новую запись покупателя. В противном случае идентификатор читается из cookie.

Листинг 6.7. Header.asp

<!-- Header.asp - файл включается в начало каждой страницы магазина. В нем определяется общий макет страницы и ссылки для перемещения между страницами.-->

<%

'Проверить, равен ли идентификатор покупателя 0. В этом случае мы должны создать идентификатор для дальнейшего отслеживания покупателя.
if session("idShopper") = "0" then

' Проверить наличие cookie с идентификатором покупателя.
if Request.Cookies("WWCD") = "" then

Чтобы создать запись покупателя, мы открываем подключение к базе данных и выполняем хранимую процедуру sp_InsertShopper, которая возвращает новый идентификатор покупателя. Полученное значение присваивается сеансовой переменной, и страница продолжает работу. Обратите внимание: идентификатор покупателя не записывается в cookie. Эта возможность будет предложена покупателю в процессе оформления заказа.

Листинг 6.8. Header.asp (продолжение)

'Создать объект подключения к базе данных
set dbShopper = server.createobject("adodb.connection")

'Создать набор записей
set rsShopper = server.CreateObject("adodb.recordset")

'Открыть подключение, используя файловый DSN ODBC
dbShopper.open("filedsn=WildWillieCDs")

'Построить команду вызова хранимой процедуры для создания нового покупателя, поскольку cookie не существует.
sql = "execute sp_InsertShopper"

'Выполнить команду SQL
set rsShopper = dbShopper.Execute(sql)

'Сохранить идентификатор в сеансовой переменной.
session("idShopper") = rsShopper("idShopper")

else

Если cookie существует, мы читаем из него идентификатор покупателя. Впрочем, это еще не все - мы также должны загрузить последнюю открытую корзину (если она существует), чтобы покупатель продолжил свою работу с того места, на котором она прекратилась. Идентификатор покупателя сохраняется в сеансовой переменной. После этого мы выполняем хранимую процедуру sp_RetrieveLastBasket, которая возвращает последнюю корзину. Если корзина была успешно возвращена, ее идентификатор сохраняется в сеансовой переменной idBasket

Листинг 6.9. Header.asp (продолжение)

'Прочитать идентификатор покупателя из cookie
session("idShopper") = Request.Cookies("WWCD")

'Создать объект подключения к базе данных
set dbShopperBasket = server.createobject("adodb.connection")

'Создать набор записей
set rsShopperBasket = server.CreateObject("adodb.recordset")

'Открыть подключение, используя файловый DSN ODBC
dbShopperBasket.open("filedsn=WildWillieCDs")

'Получить последнюю корзину, использовавшуюся покупателем.
'Возвращаются только те корзины, обработка которых не была завершена
sql = "execute sp_RetrieveLastBasket " & session("idShopper")

'Выполнить команду SQL
set rsShopperBasket = dbShopperBasket.Execute(sql)

'Проверить, была ли возвращена корзина.
if rsShopperBasket.EOF <> true then

'Присвоить идентификатор корзины сеансовой переменной
session("idBasket") = rsShopperBasket("idBasket")

end if

'Установить признак того, что профиль НЕ БЫЛ прочитан.
session("ProfileRetrieve") = "0"

end if

end if

%>

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

Первая секция представляет собой верхнюю строку, в которой выводится логотип с компакт-дисками и название страницы. За ней следует таблица, организующая перемещение по сайту. В первом столбце первой строки расположены ссылки для перемещения по основным страницам сайта. Во втором столбце выводится основное содержимое страниц. Закрывающие теги второго столбца, строки и таблицы находятся в файле Footer.asр.

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

Листинг 6.11. Хранимая процедура spJnsertShopper

/* Сохранение записи нового покупателя в базе данных. */

CREATE PROCEDURE sp_InsertShopper AS

/* Сохранить запись покупателя в базе, присвоив полям имени и фамилии пустые строки */

insert into shopper(chrusername, chrpassword) values('', '')

/* Вернуть значение столбца-счетчика, то есть идентификатор покупателя */

select idShopper = @@identity

Вторая хранимая процедура, sp_RetrieveLastBasket, возвращает последнюю активную корзину, использовавшуюся покупателем. Процедура возвращает только те корзины, которые не входят в оформленные заказы. Чтобы последняя корзина находилась в начале набора записей, ключевое слово DESC сортирует результаты выборки по убыванию.

Листинг 6.12. Хранимая процедура sp_RetrieveLastBasket

/* Загрузка из базы данных последней корзины, использованной покупателем. */
CREATE PROCEDURE sp_RetrieveLastBasket

/* При вызове процедуре передается идентификатор покупателя */
@idShopper int

AS

/* Выборка всех корзин покупателя, на которые не был оформлен заказ. Данные сортируются по убыванию, чтобы последняя запись стояла на первом месте. */
select * from basket
where idShopper = @idShopper and
intOrderPlaced = 0 and intTotal = 0
order by dtCreated DESC

Наряду cHeader.asp, мы должны включить Footer.asp в конец страницы. Эта страница закрывает теги, открытые в Header.asp. В нашем примере она также дает возможность вывести сообщение об авторских правах, адрес электронной почты службы поддержки и т. д. Страница Footer.asp приведена в листинге 6.13.

Листинг 6.13. Footer.asp

<!-- Footer.asp - страница включается в конец каждой страницы магазина.
Она завершает описание структуры страницы. -->
<!-- Закрыть столбец содержимого, открытый в header -->
</td>

<!-- Закрыть строку таблицы -->
</tr>

<!-- Начать новую строку для вывода нижнего колонтитула -->
<tr>
<!-- Начать новый столбец, распространяющийся поперек четырех столбцов -->
<td colspan="4" width="680">
<HR>
<!-- Вывести адрес электронной почты службы поддержки -->
Need help? Email
<a href="mailto:support@wildwillieinc.com">
support@wildwillieinc.com</a>
<BR><BR>
<!-- Вывести информацию об авторских правах -->
<font size="2">&copy;Copyright 1999 Wild Willie
Productions, Inc.</font>
</td>
</tr>

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

Построение домашней страницы

Домашняя страница представляет собой точку входа в магазин для покупателей. Как и положено, она содержит верхний и нижний колонтитул. Сразу же после открывающих тегов страницы мы включаем страницу Header.asp командой ASP #1nclude.

Затем мы выводим на странице основное содержимое, которое в нашем случае представляет собой простое приветственное сообщение. Страница закрывается включением файла Footer.asр. Код домашней страницы приведен в листинге 6.14.

Листинг 6.14. Default.asp

<%@ Language=VBScript %>
<HTML>
<!-- Default.asp - Домашняя страница магазина, на которой выводится
приветственное сообщение -->

<!-- #include file="include/header.asp" -->

<!-- Текст приветствия -->
Welcome to <font color="blue"><B>Wild Willie's CRAZY CD</b></font> store! We have some
of the wildest CDs that not even the biggest of the CD stores have. <br><br>

Select departments on the left to start your shopping experience!

<!-- #include file="include/footer.asp" -->

</BODY>
</HTML>

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

Просмотр разделов и товаров

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

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

Разделы

На странице Dept.asp выводится список разделов магазина. В нашем примере для каждого раздела выводится название и графическое изображение. Программный код страницы разделов приведен в листинге 6.15.

Страница начинается со стандартной структуры с включением файла заголовка.

Листинг 6.15. Dept.asp

<%@ Language=VBScript %>
<HTML>
<!--
Dept.asp - вывод списка разделов электронного магазина.
-->

<!-- #include file="include/header.asp" -->

<b>Select from a department below:</b><BR><BR>

Затем мы создаем подключение к базе данных и читаем все записи из базы данных разделов при помощи хранимой процедуры sp_retrieveDepts. Затем начинается цикл, в котором мы последовательно перебираем разделы и выводим их данные.

Листинг 6.16. Dept.asp (продолжение)

<%

' Create an ADO database connection
set dbDepts = server.createobject("adodb.connection")

' Create a record set
set rsDepts = server.CreateObject("adodb.recordset")

' Open the connection using our ODBC file DSN
dbDepts.open("filedsn=WildWillieCDs")

' Call the stored procedure to retrieve
' the departments in the store.
sql = "execute sp_RetrieveDepts"

' Execute the SQL statement
set rsDepts = dbdepts.Execute(sql)

' We will use a flag to rotate images
' from left to right
Flag = 0

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

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

Страница закрывается включением файла Footer.asp и завершающими тегами.

Листинг 6.17. Dept.asp (продолжение)

' Loop through the departments
do until rsDepts.EOF

' Retrieve the field values to display the
' name, image and link to the ID of the
' department
chrDeptName = rsDepts("chrDeptName")
chrDeptImage = rsDepts("chrDeptImage")
idDepartment = rsDepts("idDepartment")

' Check the flag
If Flag = 0 then

' Flip the flag
Flag = 1

%>

<!-- Display the image and the name of the department
In this case the image is on the left and the
name on the right.
-->
<a href="products.asp?idDept=<%=idDepartment%>">
<img src="images/<%=chrDeptImage%>" align="middle" border=0>
<%=chrDeptName%></a><BR><BR>

<% else %>

<!-- Display the image and the name of the department
In this case the image is on the right and the
name on the left.
-->
<a href="products.asp?idDept=<%=idDepartment%>">
<%=chrDeptName%>
<img src="images/<%=chrDeptImage%>" align="middle" border=0>
</a><BR><BR>

<%

' Reset the flag
Flag = 0

end if

' Move to the next row.
rsDepts.MoveNext

loop

%>

<!-- #include file="include/footer.asp" -->

</BODY>
</HTML>

В странице Dept.asp используется хранимая процедура sp_RetrieveDepts, возвращающая список разделов. В нашем примере она состоит из простой команды SELECT.

Листинг 6.18. Хранимая процедура sp_RetrieveDepts

/* Загрузка информации обо всех разделах магазина из базы данных */

CREATE PROCEDURE sp_RetrieveDepts AS

/* Выборка всех записей разделов */

На этом построение первой интерактивной страницы нашего магазина завершается. На рис. 6.4 изображена страница с примерами данных. С каждым разделом связана страница, на которой выводится список товаров данного раздела.

СОВЕТ
При большом количестве разделов список лучше построить не в один столбец: а в несколько. В этом случае вам придется реализовать дополнительную логику перехода между столбцами списка.

На следующей странице, Products.asp, отображается список товаров раздела, выбранного на странице Dept.asp. Идентификатор раздела передается в URL при вызове страницы Products.asp (см. листинг 6.19).

Работа страницы начинается с создания набора записей, используемого при запросах к базе данных. Хранимая процедура sp_RetrieveDept возвращает данные конкретного раздела. При ее вызове передается идентификатор раздела, переданный в URL.

Листинг 6.19. Products.asp

<%@ Language=VBScript %>
<HTML>
<!-- Products.asp - вывод товаров конкретного раздела. -->

<!-- #include file="include/header.asp" -->

<%

' Create an ADO database connection
set dbDepartment = server.createobject("adodb.connection")

' Create the record set
set rsDepartment = server.CreateObject("adodb.recordset")

' Open the connection using our ODBC file DSN
dbDepartment.open("filedsn=WildWillieCDs")

' Build the SQL statement. We are calling the
' stored procedure to retrieve the department
' information and passing in the ID of the
' department
sql = "execute sp_RetrieveDept " & request("idDept")

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

Идентификатор раздела сохраняется в сеансовой переменной для дальнейшего использования. Когда покупатель перейдет к странице корзины, мы сможем построить ссылку для возвращения к разделу, в котором он работал. Скажем, если покупатель интересуется джазом, он сможет быстро вернуться к разделу "Джаз" и продолжить покупки.

Листинг 6.20. Products.asp (продолжение)

'Прочитать данные раздела
set rsDepartment = dbDepartment.Execute(sql)

'Получить значения полей
txtDescription = rsDepartment("txtDeptDesc")
chrDeptImage = rsDepartment("chrDeptImage")
chrDeptName = rsDepartment("chrDeptName")

' Store the ID of the deparment being referenced in
' the LastIDDept session variable. This will allow us
' to build a link on the basket back to the department
' for further shopping.
session("LastIDDept") = request("idDept")

%>

<!-- Display the department image and name -->
<CENTER>
<img src="images/<%=chrDeptImage%>" align="middle">
<FONT size="4"><B><%=chrDeptName%></b></font><BR><BR>
</CENTER>

<!-- Display the description -->
<%=txtDescription%> Select a product:<BR><BR>

Переходим к получению списка товаров раздела. Мы снова создаем подключение к базе данных и используем хранимую процедуру для получения списка товаров. Идентификатор раздела передается хранимой процедуре в качестве параметра.

Листинг 6.21. Products.asp (продолжение)

<%

' Создать объект подключения к базе данных
set dbProducts = server.createobject("adodb.connection")

'Создать набор записей
set rsProducts = server.CreateObject("adodb.recordset")

'Открыть подключение, используя файловый DSN ODBC
dbProducts.open("filedsn=WildWillieCDs")

'Построить команду SQL для получения списка всех товаров раздела.
'Идентификатор раздела передается процедуре в качестве параметра.
sql = "execute sp_RetrieveDeptProducts " & request("idDept")

'Выполнить команду SQL и получить набор записей
set rsProducts = dbProducts.Execute(sql)

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

Для каждого товара выводится его название и графическое изображение. Как и на странице разделов, мы используем их для построения ссылки на страницу с описанием конкретного товара и включаем в URL идентификатор товара. Страница завершается включением стандартного файла Footer.asp и парой завершающих тегов.

Листинг 6.22. Products.asp (продолжение)

' We are going to rotate the images from left
' to right.
Flag = 0

' Loop through the products record set
do until rsProducts.EOF

' Retrieve the product information to be displayed.
chrProductName = rsProducts("chrProductName")
chrProductImage = rsProducts("chrProductImage")
idProduct = rsProducts("idProduct")

' Check the display flag. We will rotate the
' product images from left to right.
If flag = 0 then

' Set the flag
flag = 1

%>

<!-- Build the link to the product information. -->
<a href="product.asp?idProduct=<%=idProduct%>">
<img src="images/products/sm_<%=chrProductImage%>"
align="middle" border="0">
<%=chrProductName%></a><BR><BR>

<% else %>

<!-- Build the link to the product information. -->
<a href="product.asp?idProduct=<%=idProduct%>">
<%=chrProductName%>
<img src="images/products/sm_<%=chrProductImage%>"
align="middle" border="0"></a><BR><BR>
<%

' Reset the flag
Flag = 0

end if

' Move to the next row
rsproducts.movenext

loop

%>

<!-- #include file="include/footer.asp" -->

</BODY>
</HTML>

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

Листинг 6.23. Хранимая процедура sp_RetrieveDept

/* Загрузка информации об одном разделе */
CREATE PROCEDURE sp_RetrieveDept

/* Pass in the ID of the department */
@idDepartment int

AS

/* Select all of the data on the
department */
select * from department
where idDepartment = @idDepartment

Вторая хранимая процедура возвращает список товаров, относящихся к заданному разделу. Для получения списка необходимо связать таблицы Department, DcpartmentProducts и Products. Мы возвращаем все товары, ассоциируемые с заданным идентификатором раздела в таблице DepartmentProducts.

Листинг 6.24. Хранимая процедура sp_RetrieveDeptProducts

/* Загрузка списка товаров, относящихся к заданному разделу */
CREATE PROCEDURE sp_RetrieveDeptProducts

/* При вызове процедуре передается идентификатор раздела */
@idDept int

AS

/* Выборка товаров заданного раздела из таблицы products */
select * from products, departmentproducts

where products.idproduct = departmentproducts.idproduct and
departmentproducts.iddepartment = @idDept

Теперь при щелчке на любом разделе на странице Dept.asp загружается страница Products.asp со списком товаров. Ссылка Departments в списке слева позволяет быстро просмотреть любой раздел.

В приведенном примере раздел состоит из двух товаров: Joe Bob's Thimble Sounds и The Sounds of Silence (for real). Обратите внимание на информацию о разделе, отображаемую наверху справа непосредственно под заголовком страницы.

Если щелкнуть на ссылке Department, можно выбрать новый раздел - например, Crying Westerns. Сохраняется чередование графики и текста на странице.

Товары

На странице Product.asp (см. листинг 6.25) выводятся сведения о товаре, выбранном на странице Products.asp. Именно здесь покупатель получает более подробную информацию и принимает решение о покупке.

Страница начинается стандартно, с включения файла Header.asp и создания объекта подключения к базе данных для чтения информации о товаре.

Листинг 6.25. Product.asp

<%@ Language=VBScript %>
<HTML>
<!-- Product.asp - вывод информации о товаре. -->

<!-- #include file="include/header.asp" -->

<%

' Create an ADO database connection
set dbProduct = server.createobject("adodb.connection")

' Create a record set
set rsProduct = server.CreateObject("adodb.recordset")

' Open the connection using our ODBC file DSN
dbProduct.open("filedsn=WildWillieCDs")

Команда SQL использует хранимую процедуру sp_RetrieveProduct для чтения данных товара, идентификатор которого передается в URL страницы. Из полученных данных извлекаются основные сведения о товаре - описание, графическое изображение, название, цена и идентификатор товара.

Листинг 6.26. Product.asp (продолжение)

' При вызове хранимой процедуры для получения данных товара
' передается идентификатор товара
sql = "execute sp_RetrieveProduct " & request("idProduct")

' Выполнить команду SQL
set rsProduct = dbProduct.Execute(sql)

' Получить основные сведения о товаре
txtDescription = rsProduct("txtDescription")
chrProductImage = rsProduct("chrProductImage")
chrProductName = rsProduct("chrProductName")
intPrice = rsProduct("intPrice")
idProduct = rsProduct("idProduct")
%>

Тег FORM создает форму, данные которой передаются странице AddItem.asp. На этой странице товар включается в корзину и отслеживается вплоть до окончательного оформления заказа.

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

Обратите внимание на скрытые поля. Они предназначены для получения быстрого доступа к данным корзины со страницы AddItem.asp.

Листинг 6.27. Product.asp (продолжение)

<!-- The additem.asp page will be called to
add the product to the basket -->
<form method="post" action="additem.asp">

<!-- The table will provide the layout
structure for the product. -->
<table border="0" cellpadding="3" cellspacing="3">

<!-- Row to display the product image, name and
description. -->
<TR>
<!-- Display the image -->
<td><img src="images/products/<%=chrProductImage%>"></td>

<!-- Show the product name and description -->
<td valign="top">
<CENTER><b><font size="5"><%=chrProductName%></font></b></center>
<BR><BR>
<%=txtDescription%><BR><BR>
</td>
</TR>

<!-- Show the product price. An input quantity box is
created. Also, several hidden variables will hold
key data for adding the product to the database. -->
<TR>
<TD align="center"><B>Price:
<%=formatcurrency(intPrice/100, 2)%></b>
</td>

<TD align="center">
<B>Quantity:
<input type="text" value="1" name="quantity" size="2"></b>
<input type="hidden" value="<%=idProduct%>" name="idProduct">
<input type="hidden" value="<%=chrProductName%>" name="ProductName">
<input type="hidden" value="<%=intPrice%>" name="ProductPrice">
</td>
</TR>

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

Загрузка атрибутов осуществляется при помощи хранимой процедуры sp_Attributes. При вызове процедуре передается идентификатор товара. Затем мы проверяем, вернула ли процедура какие-либо атрибуты.

Листинг 6.28. Product.asp (продолжение)

<%

' Создать объект подключения к базе данных
set dbAttributes = server.createobject("adodb.connection")

' Создать набор записей
set rsAttributes = server.CreateObject("adodb.recordset")

' Открыть подключение, используя файловый DSN ODBC
dbAttributes.open("filedsn=WildWillieCDs")

' Execute the stored procedure to retrieve the attributes
' for the products.
sql = "execute sp_Attributes " & request("idProduct")

' Execute the SQL statement
set rsAttributes = dbProduct.Execute(sql)

' Loop through and display the attributes for the product.
if not rsAttributes.EOF then

%>
<TR>
<!-- Color column -->
<TD>

Сначала обрабатывается атрибут "цвет". В форме создается список, содержащий все значения данного атрибута.

ПРИМЕЧАНИЕ
В различных областях бизнеса используются разные единицы учета запасов. Иногда основной единицей является базовый идентификатор товара, а атрибуты всего лишь уточняют данные заказа. Именно такой подход к атрибутам использован в нашем магазине. Однако во многих магазинах в качестве единицы учета запасов используется комбинация идентификатора товара, всех атрибутов и т. д. При хранении атрибутов и данных товара вообще следует принимать во внимание некоторые обстоятельства. Так, при частом изменении ассортимента крайне важно, чтобы информация о ценах, атрибутах и т. д. сохранялась в заказе. Мы не хотим, чтобы цена товара (цвет и т. д.) неожиданно изменилась после оформления заказа, и клиенту пришлось рассчитываться по новой цене. Стандартное решение, использованное в нашем примере, заключается в фиксировании данных товара в момент покупки. При больших объемах товара на складе необходимость в этом может отпасть.

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

Список размеров заполняется по тем же правилам, что и список цветов. На этом вывод сведений о товаре завершается, и пользователь может сделать свой выбор.

Листинг 6.29. Product.asp (продолжение)

Color:
<!-- Список вариантов цвета -->
<SELECT name="color">

<%

' В цикле перебирать атрибуты.
do until rsAttributes.EOF

' Проверить, не перешли ли мы к другому атрибуту в списке.
if rsAttributes("chrCategoryName") <> "Color" then

' Выйти из цикла
exit do

end if

%>

<!-- Построить очередную строку списка цветов. Атрибут value равен идентификатору цвета-->
<option value="<%=rsAttributes("chrAttributeName")%>">
<%=rsAttributes("chrAttributeName")%>

<%

' Перейти к следующей записи
rsAttributes.MoveNext

loop

%>

</select>
</TD>

<!-- Size column -->
<TD>
Size:

<!-- Start the size select box -->
<SELECT name="size">

<%

' Loop through the size attributes
do until rsAttributes.EOF

%>

<!-- Display the options -->
<option value="<%=rsAttributes("chrAttributeName")%>">
<%=rsAttributes("chrAttributeName")%>

<%

' Move to the next row
rsAttributes.MoveNext

loop

%>

</select>

</TD>

</TR>

<%

end if

%>

Страница завершается кнопкой отправки данных, закрывающим тегом формы, включением файла Footer.asp и тегами конца страницы.

Листинг 6.30. Product.asp (продолжение)

<!-- Вывести кнопку отправки данных -->
<TR>
<td colspan="2" align="center">
<input type="submit" value="Order" name="Submit">
</td>
</tr>

</table>

</form>

<!-- #include file="include/footer.asp" -->

</BODY>
</HTML>

На странице используются две хранимые процедуры. Первая, sp_RetrieveProduct, просто возвращает сведения о товаре с переданным идентификатором.

Листинг 6.31. Хранимая процедура sp_RetrieveProduct

/* Загрузка данных товара */
CREATE PROCEDURE sp_RetrieveProduct

/* При вызове процедуре передается идентификатор товара */
@idProduct int

AS

/* Выборка сведений о товаре */
select * from products
where idProduct = @idProduct

Хранимая процедура sp_Attributes возвращает все атрибуты заданного товара. Для этого мы объединяем четыре таблицы Products, ProductAttribute, Attribute и AttributeCategory. В таблице AttributeCategory хранятся названия категорий Size и Color. Таблица AttributeName определяет названия цветов и размеров, входящих в категории. Таблица ProductAttribute содержит набор комбинаций, связывающих товары с атрибутами.

Данные, полученные при вызове хранимой процедуры, упорядочиваются по названию категории. Это сделано для того, чтобы мы могли последовательно перебирать все атрибуты и завершать построение списка при переходе к следующей категории.

Листинг 6.32. Хранимая процедура sp_Attributes

/* Загрузка атрибутов заданного товара из базы данных */
CREATE PROCEDURE sp_Attributes

/* При вызове процедуре передается идентификатор товара */
@idProduct int

AS

/* Выборка атрибутов товара. */
select products.idproduct,
attribute.idattribute,
attribute.chrattributename,
attributecategory.chrcategoryname,
productattribute.idproductattribute

from products, productattribute, attribute, attributecategory

where

products.idproduct = @idProduct and
productattribute.idproduct = @idProduct and
productattribute.idattribute = attribute.idattribute and
attribute.idattributecategory = attributecategory.idattributecategory

order by chrcategoryname

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

На рис. 6.7 изображена страница с информацией об одном из товаров нашего магазина. Графическое изображение расположено слева, а текстовая информация - справа. Цена выводится непосредственно под графикой. Чтобы заказать этот товар, покупатель щелкает на кнопке Order, что приводит к загрузке страницы AddItem.asp.

Рис. 6.7. Страница с информацией о товаре

Товар с атрибутами - симпатичная и неповторимая футболка стоимостью $20. К радости покупателя имеется четыре варианта расцветки и четыре размера. Пользователь выбирает нужное сочетание цвета и размера и включает товар в корзину.

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

Поиск

Средства поиска на Web-сайте необходимы для того, чтобы пользователи могли находить товары по своим специфическим критериям. В нашем примере реализованы два основных варианта поиска. Первый - стандартный поиск по ключевым словам. Поиск будет осуществляться в полях имени и описания товара средствами синтаксиса SQL.

Во втором варианте поиска ищутся товары, относящиеся к определенному интервалу цены. Например, можно провести поиск товаров стоимостью от $10 до $20, в название или описание которых входит слово "jazz".

Начало страницы выглядит вполне стандартно (см. листинг 6.33). Впрочем, форма для передачи критериев поиска устроена несколько необычно. Вместо того чтобы отправлять их другой странице для обработки результатов, она отправляет их самой себе, то есть Search.asр. Проверка переданных данных осуществляется в процессе работы страницы.

Листинг 6.33. Search.asp

<%@ Language=VBScript %>
<HTML>
<!--
Search.asp - Provides searching capabilities for finding
products.
-->

<!-- #include file="include/header.asp" -->

<BR>

<!-- Build the search form. Note we post
to this page.
-->
<form method="post" action="search.asp">

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

Листинг 6.34. Search.asp (продолжение)

<!-- Таблица для вывода критериев поиска -->
<table border="0">

<!-- Поле для ввода искомого текста -->
<tr>
<td align="right"><b>Enter your search text:</b></td>
<!-- Input text box -->
<td align="right"><input type="text"
value="<%=request("search")%>" name="Search">
</td>
</tr>

<!-- Поиск товаров с ценой в заданном интервале -->
<tr><td><b>Price Range:</b></td>
<td align="right">Low:
<input type="text" value="<%=request("low")%>" name="Low"></td>
</tr>

<!-- Верхняя граница цены -->
<tr><td></td>
<td align="right">High: <input type="text"
value="<%=request("high")%>" name="High"></td>
</tr>

<!-- Промежуток -->
<tr><td colspan="2">&nbsp;</td></tr>

<!-- Кнопка отправки данных -->
<tr><td colspan="2" align="center">
<input type="submit" value="Submit" name="Submit">
</td></tr>

</table>

</form>

Следующий фрагмент проверяет, был ли отправлен странице запрос на проведение поиска. Мы проверяем состояние трех переменных - search (текст), low (нижняя граница цены) и high (верхняя граница цены). Если проверка дает положительный результат, страница открывает подключение к базе данных для проведения поиска.

Листинг 6.35. Search.asp (продолжение)

<%

' Проверить, был ли отправлен странице запрос на проведение поиска.
if request("search") <> "" or _
request("low") <> "" or _
request("high") <> "" then

' Создать объект подключения к базе данных
set dbSearch = server.createobject("adodb.connection")

' Создать объект набора данных
set rsSearch = server.CreateObject("adodb.recordset")

' Открыть подключение, используя файловый DSN ODBC
dbSearch.open("filedsn=WildWillieCDs")

Сначала мы проверяем, содержит ли переменная low какое-либо значение и является ли оно числовой величиной. Если данные отсутствуют, нижняя граница цены устанавливается равной 0. Если переменная содержит число, мы читаем его и умножаем на 100.

Умножение выполняется для сравнения с ценами в базе данных, хранящимися в виде целых чисел.

Листинг 6.36. Search.asp (продолжение)

' Проверить наличие нижней границы и числовой тип ее значения.
If request("Low") = "" or _
isnumeric(request("low")) = false then
' Default to 0
Low = 0
else
' Присвоить переменной введенное число. Поскольку цены хранятся
' в базе в виде целых чисел, значение умножается на 100
Low = request("Low") * 100
end if

Аналогичная проверка выполняется и для верхней границы (переменная high). Если значение отсутствует, переменной присваивается очень большая величина в долларах. Если значение задано, оно, как и в случае с нижней границей, умножается на 100.

Листинг 6.37. Search.asp (продолжение)

' Проверить наличие верхней границы и числовой тип ее значения.
if request("High") = "" or _
isnumeric(request("High")) = false then

' Присвоить очень большое число
High = 99999999

else

' Присвоить значение, умноженное на 100
High = Request("High") * 100

end if

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

Листинг 6.38. Search.asp (продолжение)

'Построить запрос SQL для отбора товаров по заданным критериям.
' Искомый текст и границы интервала цены передаются
' в качестве параметров
sql = "execute sp_SearchProducts '" & _
request("search") & "', " & Low & ", " & High

' Выполнить команду SQL
set rsSearch = dbSearch.Execute(sql)

%>

<!-- Начало списка -->
<UL>

Хранимая процедура sp_SearchProducts получает три параметра: искомый текст, нижнюю границу и верхнюю границу цены. Поиск текста в названии и описании товара в запросе SQL выполняется при помощи конструкции LIKE. Наконец, цена товара сравнивается с нижней и верхней границей интервала.

СОВЕТ
Поиск на Web-сайтах - весьма интересная тема. Обычно на сайтах реализуется два типа поиска. Первый, неструктурированный поиск, поддерживается большинством поисковых систем. Такие инструменты, как Index Server, просматривают файлы содержимого сайта и индексируют ключевые слова в специальной базе данных. Существует специальный язык для запросов к этой базе. Второй тип, поиск в базе данных, обычно реализуется на таких языках, как SQL. Примером является поиск товаров в электронном магазине.

На коммерческом Web-сайте, особенно с большим ассортиментом, поисковая страница может оказаться едва ли не самой посещаемой. Нередко в ход идут нестандартные возможности - например, отслеживание ключевых слов, по которым проводят поиск покупатели, и включение в базу данных специальных полей, повышающих вероятность успешного поиска. Иногда фантазия разработчика доходит до поисковых механизмов с выдачей рекомендаций, как на ряде крупных Web-магазинов (например, Amazon, CD Now, EBWord и других).

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

Листинг 6.39. Хранимая процедура sp_SearchProducts

/* Поиск товаров по переданным параметрам. */
CREATE PROCEDURE sp_SearchProducts

/* При вызове процедуре передается искомый текст, нижняя и верхняя граница цены */
@SearchText varchar(255),
@Low int,
@High int

AS


/* Select products from the data base where the
product name or description contain the search
text. And where the price falls in the given
parameters. The products are ordered by the
product name. */
select * from products
where (chrProductName like '%' + @SearchText+ '%' or
txtDescription like '%' + @SearchText + '%') and
(intPrice >= @low and intPrice <= @High)
order by chrProductName

Поисковая страница нашего сайта. Обратите внимание на три текстовых поля. Для примера введите в первом поле текст jazz, в поле нижней границы - 10, а в поле верхней границы - 20.

Рис. 6.9. Поисковая страница

Результаты поиска. В нашем примере они состоят из одного товара, Alley Jazz. Покупатель щелкает на ссылке и переходит к найденному товару.

Итоги

Страницы, описанные в этой главе, образуют основную инфраструктуру для представления сайта покупателю. Обычно именно на этих страницах происходит большинство всех маркетинговых событий на Web-сайтах. В части 4 "Реклама" основное внимание уделяется продвижению товаров на Web-сайте.

Рис. 6.10. Результаты поиска

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

Вторая концепция заключается в структурном подходе к построению базы данных. При помощи хранимых процедур мы реализуем всю основную структуру данных разделов и товаров, включая атрибуты товаров и т. д.

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

Глава 7. Корзина

В предыдущей главе мы предоставили покупателям средства для просмотра и поиска товаров. Теперь можно переходить к процессу управления корзиной.

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

Проектирование корзины

Благодаря некоторым ключевым функциям корзина является одним из самых динамичных аспектов Web-сайта. В табл. 7.1 перечислены основные функции, которые мы реализуем на странице, предназначенной для операций с корзиной.

Таблица 7.1. Основные функции корзины
Функция
Описание
Добавление новых позиций в корзину Когда покупатель нажимает кнопку Order на странице товара, мы выполняем действия по включению товара в корзину. При этом необходимо соблюдать несколько ключевых правил (см. ниже)
Отображение корзины Когда покупатель находится на странице корзины, часто возникает потребность вывода всех товаров, находящихся в корзине, с количеством заказанных единиц
Обновление содержимого корзины Если пользователь захочет изменить количество заказанных единиц товара в любой позиции корзины, мы должны предоставить ему соответствующие средства
Удаление позиций из корзины Если пользователь решает удалить некоторую позицию из корзины, мы должны предоставить средства для удаления выбранной позиции
Очистка корзины Если по какой-либо причине пользователь захочет удалить из корзины все отобранные товары и начать покупки заново, мы должны предоставить ему функцию для полной очистки корзины

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

Добавление новых позиций

Изображенный на рисунке процесс начинается с добавления новых позиций. Страница AddItem.asp вызывается при нажатии пользователем кнопки Order на странице товара. Обратите внимание - страница выполняет только обработку данных. Она не выводит никакой информации для пользователя и немедленно передает управление странице Basket.asp для вывода содержимого корзины. Страница начинается с проверки идентификатора покупателя. Если идентификатор не был присвоен, по умолчанию присваивается 0, чтобы страница могла продолжить работу. Код страницы AddItem.asp начинается в листинге 7.1.

Листинг 7.1. AddItem.asp

<%@ Language=VBScript %>
<%
' ****************************************************
' AddItem.asp - включение выбранного товара в корзину
' ****************************************************

' Проверить, установлен ли идентификатор пользователя.
' Если нет, по умолчанию установить его равным нулю.
if session("idShopper") = "" then

' По умолчанию присваивается 0
session("idShopper") = 0

end if

Затем мы убеждаемся в том, что количество заказанных единиц товара отлично от 0. Если оно равно 0, добавлять товар в корзину незачем; вместо этого следует вернуть покупателя на страницу с информацией о товаре. Выбор страницы обеспечивается включением идентификатора товара в ссылку.

Листинг 7.2. AddItem.asp (продолжение)

' Убедиться в том, что количество единиц не равно 0.
if request("quantity") = "0" then

' Вернуть пользователя на страницу товара.
Response.Redirect("product.asp?idProduct=" & _
request("idProduct"))

End If

После завершения проверки мы получаем данные со страницы Product.asр. Как упоминалось выше, основные параметры товара сохраняются в скрытых полях этой страницы. Теперь мы читаем их для использования в программе. Апострофы в названии товара удваиваются перед занесением в базу данных.

Листинг 7.3. AddItem.asp (продолжение)

' Получить количество единиц
intQuantity = request("quantity")

' Получить идентификатор товара
idProduct = request("idProduct")

' Получить название товара
chrName = replace(request("productname"), "'", "''")

' Получить цену товара
intPrice = request("productprice")

' Получить размер
chrSize = request("size")

' Получить цвет
chrColor = request("color")

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

Если переменная не была инициализирована, мы открываем подключение к базе данных и создаем новую корзину. Задача решается при помощи хранимой процедуры sp_CreateBasket. При создании новой корзине присваивается идентификатор покупателя, который передается хранимой процедуре в качестве параметра.

После создания корзины мы читаем из базы данных ее идентификатор (столбец-счетчик). Прочитанное значение сохраняется в сеансовой переменной.

Листинг 7.4. AddItem.asp (продолжение)

' Проверить, существует ли корзина
if session("idBasket") = "" then

' Создать объект подключения к базе данных
set dbBasket = server.createobject("adodb.connection")

' Создать набор записей
set rsBasket = server.CreateObject("adodb.recordset")

' Открыть подключение, используя файловый DSN ODBC
dbBasket.open("filedsn=WildWillieCDs")

' Построить команду вызова хранимой процедуры для создания
' новой корзины у покупателя с заданным идентификатором
sql = "execute sp_CreateBasket " & session("idShopper")

' Выполнить команду SQL
set rsBasket = dbBasket.Execute(sql)

' Получить идентификатор корзины, сгенерированный при создании записи
idBasket = rsBasket("idBasket")

' Сохранить идентификатор корзины в сеансовой переменной
session("idBasket") = idBasket

else

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

Мы открываем подключение к базе данных и вызываем хранимую процедуру sp_CheckBasketItemQuantity, передавая ей идентификаторы товара и корзины.

Листинг 7.5. AddItem.asp (продолжение)

' Создать объект подключения к базе данных
set dbBasket = server.createobject("adodb.connection")

' Создать набор записей
set rsBasket = server.CreateObject("adodb.recordset")

' Открыть подключение, используя файловый DSN ODBC
dbBasket.open("filedsn=WildWillieCDs")

' Построить команду вызова хранимой процедуры для проверки наличия товара в корзине.
' Идентификаторы проверяемого товара и корзины передаются в качестве параметров.
sql = "execute sp_CheckBasketItemQuantity " & _
idProduct & ", " & _
session("idBasket")

' Выполнить команду SQL
set rsBasket = dbBasket.Execute(sql)

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

Обновление выполняется при помощи хранимой процедуры sp_UpdateBasket -ItemsQuantity. При вызове процедуре передаются идентификаторы корзины и товара. Кроме того, передается новое количество единиц товара. Для этого мы читаем текущее количество, полученное из предыдущего запроса, и суммируем его с количеством единиц, указанным покупателем на странице Product.asp. Затем пользователь перенаправляется на страницу Basket.asр.

Листинг 7.6. AddItem.asp (продолжение)

' Проверить, присутствует ли товар в корзине

if rsbasket.eof = false then

' Если товар присутствует, вместо добавления новой позиции
' следует обновить количество единиц в существующей записи.
sql = "execute sp_UpdateBasketItemsQuantity " & _
session("idBasket") & ", " & _
intQuantity + rsBasket("intQuantity") & ", " & _
idProduct

' Выполнить команду SQL
set rsBasket = dbBasket.Execute(sql)

' Перейти к выводу содержимого корзины
Response.Redirect "basket.asp"

end if

end if

Если товар отсутствует в корзине, значит в таблицу BasketItems следует добавить новую запись. Для добавления новой позиции в корзину используется хранимая процедура sp_InsertBasketItem.

При ее вызове передаются все основные параметры, включая идентификатор корзины, количество единиц, цену, название и идентификатор товара, размер и цвет. После создания новой позиции покупатель направляется на страницу Basket.asp.

ПРИМЕЧАНИЕ
Многие из наших товаров не обладают атрибутами размера и цвета. Если эти атрибуты не существуют, Request не возвращает никакой информации, и соответствующие поля в базе данных остаются пустыми.

Листинг 7.7. AddItem.asp (продолжение)

' Создать объект подключения к базе данных
set dbBasketItem = server.createobject("adodb.connection")

' Создать набор записей
set rsBasketItem = server.CreateObject("adodb.recordset")

' Открыть подключение, используя файловый DSN ODBC
dbBasketItem.open("filedsn=WildWillieCDs")

' Построить команду вызова хранимой процедуры для вставки новой позиции в корзину
sql = "execute sp_InsertBasketItem " & _
session("idBasket") & ", " & _
intQuantity & ", " & _
intPrice & ", '" & _
chrName & "', " & _
idProduct & ", '"& _
chrSize & "', '" & _
chrColor & "'"

' Выполнить команду SQL
set rsBasketItem = dbBasketItem.Execute(sql)

' Send the user to the basket page
Response.Redirect "basket.asp"

%>

Отправить пользователя на страницу вывода содержимого корзины Response.Redirect "basket.asp"

Хранимая процедура sp_InsertBasketItem добавляет в корзину новую позицию. Вся необходимая информация передается при вызове процедуры.

Листинг 7.8. Хранимая процедура sp_InsertBasketItem

/* Stored procedure to insert a new basket
item */
CREATE PROCEDURE sp_InsertBasketItem

/* При вызове процедуре передается идентификатор корзины, количество единиц товара, .цена, название товара, идентификатор товара, размер , и цвет */
@idBasket int,
@intQuantity int,
@intPrice int,
@chrName varchar(255),
@idProduct int,
@chrSize varchar(50),
@chrColor varchar(50)

AS

/* Создать запись в таблице */
insert into basketitem(idBasket, intQuantity,
intPrice, chrName,
idProduct, chrSize,
chrColor)

values(@idBasket, @intQuantity,
@intPrice, @chrName,
@idProduct, @chrSize,
@chrColor)

Хранимая процедура sp_CreateBasket создает новую корзину для заданного покупателя. Это происходит, когда в базу данных заносится информация о новом покупателе или когда существующий покупатель оформил заказы на все существующие корзины.

В качестве параметра хранимой процедуре передается идентификатор покупателя. Затем команда INSERT добавляет запись корзины в базу данных. Процедура возвращает идентификатор корзины при помощи переменной @@Identity, которой присваивается последнее значение столбца-счетчика, возвращенное при вызове INSERT.

Листинг 7.9. Хранимая процедура sp_CreateBasket

/* При вызове процедуре передается идентификатор покупателя,
которому будет принадлежать данная корзина */
CREATE PROCEDURE sp_CreateBasket

/* Создать в таблице новую запись и присвоить значение идентификатору покупателя */
@idShopper int

AS

/* Insert a new role into the basket and
set the shopper ID
*/
insert into basket(idShopper) values(@idShopper)

/* Retrieve the ID of the basket which will be in the
@@identity variable value
*/
select idbasket = @@identity

Хранимая процедура sp_CheckBasket ItemQuantity возвращает количество единиц заданного товара в заданной корзине.

Листинг 7.10. Хранимая процедура sp_CheckBasketItemQuantity

/* Checks the quantity of items in the basket for
the specified product.
*/
CREATE PROCEDURE sp_CheckBasketItemQuantity

/* Pass in the ID of the product and
the ID of the basket
*/
@idProduct int,
@idBasket int

AS

/* Retrieve the quantity value */
select intQuantity from basketitem
where idProduct = @idProduct and
idBasket = @idBasket

Хранимая процедура spUpdateBasketItemsQuantity обновляет количество единиц заданного товара в корзине новым значением, переданным в качестве параметра. Нужная запись определяется идентификатором корзины и идентификатором товара.

Листинг 7.11. Хранимая процедура sp_UpdateBasketItemsQuantity

/* Stored procedure to update the
basket item quantity. */
CREATE PROCEDURE sp_UpdateBasketItemsQuantity

/* Pass in the ID of the basket, the quantity
and the ID of the product (basket item). */
@idBasket int,
@intQuantity int,
@idProduct int

as

/* Update the basketitem table wit the new
quantity for the product. */
update basketitem set intQuantity = @intQuantity
where idBasket = @idbasket and
idProduct = @idProduct

Страница с информацией о товаре Joe Bob's Thimble Sounds. Если щелкнуть на кнопке Order, товар включается в корзину.

На рис. 7.3 показано состояние корзины после добавления товара (вывод содержимого корзины рассматривается в следующей главе). Обратите внимание: в корзине находится 1 единица товара, поскольку именно это количество было указано при добавлении товара.

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

Познакомившись с работой корзины, мы переходим к рассмотрению ее программного кода.

Вывод корзины

Корзина представляет собой список позиций, которые необходимо вывести на странице. Страница начинается со стандартного включения файла Header.asp, после чего начинается форма, предназначенная для обновления количества единиц прямо на странице. Начало страницы Basket.asp показано в листинге 7.12.

Листинг 7.12. Basket.asp

<%@ Language=VBScript %>
<HTML>
<!--
Basket.asp - вывод содержимого корзины.
-->

<!-- #include file="include/header.asp" -->

<!-- Форма позволяет обновлять количество единиц
товара непосредственно в окне корзины -->
<form method="post" action="UpdateBasket.asp">

Как и на странице AddItem.asp, необходимо проверить, была ли создана корзина у текущего пользователя. Если корзина отсутствует, мы создаем в базе данных новую корзину при помощи хранимой процедуры sp_CreateBasket.

Листинг 7.13. Basket.asp (продолжение)

<%

' Check to see if the ID of the basket is blank
if session("idBasket") = "" then

' Create an ADO database connection
set dbBasket = server.createobject("adodb.connection")

' Create a record set
set rsBasket = server.CreateObject("adodb.recordset")

' Open the connection using our ODBC file DSN
dbBasket.open("filedsn=WildWillieCDs")

' Create a new shopping basket
sql = "execute sp_CreateBasket " & session("idShopper")

' Execute the SQL statement
set rsBasket = dbBasket.Execute(sql)

' Retrieve the id of the basket returned from the insert
idBasket = rsBasket("idBasket")

' Set the basket id in the session variable
session("idBasket") = idBasket

end if

Переходим к чтению содержимого текущей корзины. Хранимая процедура sp_RetrieveBasketItem читает записи всех позиции активной корзины текущего покупателя.

Листинг 7.14. Basket.asp (продолжение)

' Create an ADO database connection
set dbBasketItem = server.createobject("adodb.connection")

' Create a record set
set rsBasketItem = server.CreateObject("adodb.recordset")

' Open the connection using our ODBC file DSN
dbBasketItem.open("filedsn=WildWillieCDs")

' Execute the stored procedure to retrieve the basket
' items for the shopper.
sql = "execute sp_RetrieveBasketItem " & session("idBasket")

' Execute the SQL statement
set rsBasketItem = dbBasketItem.Execute(sql)

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

Листинг 7.15. Basket.asp (продолжение)

' Check to ensure a basket has been created and that
' there are items in the basket
if session("idBasket") = "" or rsBasketItem.EOF = true then

%>

<!-- Show the empty basket message -->
<center>
<BR><BR>
<font size="4">Your basket is empty.</font>
</center>

<%

else

ПЕРЕМЕЩЕНИЕ ПО ЭЛЕКТРОННОМУ МАГАЗИНУ

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

Обратная ссылка на раздел, находящаяся на странице с содержимым корзины, создается для удобства покупателя. В настоящем магазине никто не заставляет покупателя относить товар в корзину, а затем через весь магазин возвращаться обратно в отдел. Таким образом, ссылка упрощает продолжение закупок.

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

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

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

Прежде чем входить в цикл, мы создаем ссылку на раздел, в котором был заказан последний товар. Вспомните - на странице Products.asp мы создали сеансовую переменную для хранения идентификатора последнего раздела.

В нашем случае мы проверяем, было ли присвоено значение переменной LastIDDept. Если значение - не пустая строка, мы создаем ссылку для ускоренного возвращения к последнему разделу, в котором покупатель заказывал товары.

Листинг 7.16. Basket.asp (продолжение)

' Check to see if the last department is set
' based on where they ordered from.
if session("LastIDDept") <> "" then

%>

<!-- Show the link to go back to the department to
make navigation easier.
-->
<BR>
Click <a href="products.asp?idDept=<%=session("LastIDDept")%>">
here</a> to continue shopping.
<BR><BR>

<%end if%>

Затем на странице создается таблица для вывода содержимого корзины. В первой строке таблицы выводятся названия столбцов данных, отображаемых в корзине.

Листинг 7.17. Basket.asp (продолжение)

<!-- Build the basket table -->
<table border="0" cellpadding="3" cellspacing="2" width="500">

<!-- Build the header row -->
<tr>
<th>Item Code</th>
<th>Name</th>
<th>Attributes</th>
<th>Quantity</th>
<th>Price</th>
<th>Total</th>
<th>Remove</th>
</tr>

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

При выводе количества единиц атрибуту VALUE текстового поля присваивается идентификатор товара. Это сделано для того, чтобы на странице обновления корзины можно было определить, для какого товара покупатель изменяет количество единиц.

Вслед за количеством выводится цена одной единицы товара и стоимость всей позиции в целом. Как говорилось выше, цены хранятся в базе данных в целочисленном формате. Для правильного вывода цены необходимо разделить значение поля на 100, чтобы вывести дробную часть. Форматирование цены с добавлением символа доллара и т. д. осуществляется функцией FormatCurrency. Стоимость позиции вычисляется умножением количества единиц на цену товара.

Наконец, на форме строится ссылка для удаления текущей позиции из корзины. В нашем примере ссылка указывает на страницу DeleteItem.asp. В URL передается идентификатор товара, удаляемого из корзины.

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

Листинг 7.18. Basket.asp (продолжение)

<%

' Loop through the basket items.
do until rsBasketItem.EOF

%>

<!-- Show the row -->
<tr>
<!-- Show the product id -->
<td align="center"><%=rsBasketItem("idProduct")%></td>
<!-- Show the product name -->
<td><%=rsBasketItem("chrName")%></td>
<!-- Show the product attributes -->
<td>
<% if rsBasketItem("chrColor") <> " " then %>
<%=rsBasketItem("chrSize")%>,
<%=rsBasketItem("chrColor")%>
<% end if %>
</td>
<!-- Show the product quantity -->
<td align="center">
<input type="text" value="<%=rsBasketItem("intQuantity")%>"
name="<%=rsBasketItem("idProduct")%>" size="2">
</td>
<!-- Show the product price -->
<td><%=formatcurrency(rsBasketItem("intPrice")/100, 2)%></td>

<!-- Show the product total cost -->
<td>
<%=formatcurrency(rsBasketItem("intPrice")/100 * rsBasketitem("intQuantity"), 2)%>
</td>
<!-- Show the remove option. -->
<td>
<a href="deleteitem.asp?idBasketItem=<%=rsBasketItem("idBasketItem")%>">
Remove</a>
</td>
<!-- Continue to calculate the subtotal -->
<% subtotal = subtotal + (rsBasketItem("intPrice") * rsBasketitem("intQuantity")) %>

</tr>

<%

' Move to the next row
rsBasketItem.MoveNext

' Loop back
loop

%>

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

Листинг 7.19. Basket.asp (продолжение)

<!-- Build a break -->
<tr>
<td colspan="7"><HR></td>
</tr>

<!-- Show the sub total of the basket -->
<tr>
<td colspan="5" align="right"><b>Subtotal:</b></td>
<td><% = formatcurrency(subtotal/100, 2) %></td>
<td>&nbsp;</td>
</tr>

</table>

СОВЕТ
Чтобы интерфейс выглядел более последовательным, кнопку отправки данных и текстовые ссылки можно заменить графическими изображениями.

Кнопка Update Basket вносит в корзину все изменения в количестве заказанных единиц товара. Вслед за кнопкой выводятся ссылки на страницы Empty-Basket.asp и Shipping.asp. Страница завершается включением файла Footer.asp и закрывающими тегами.

Листинг 7.20. Basket.asp (продолжение)

<!-- Show the submit button for the quantity update action -->
<table width="100%">

<td><input type="submit" value="Update Basket" name="Submit"></td>

</form>

<!-- Show the empty basket and check out links -->
<td><a href="emptybasket.asp">Empty Basket</a></td>
<td><a href="shipping.asp">Check Out</a></td>

</tr>

</table>

<% end if %>

<!-- #include file="include/footer.asp" -->

</BODY>
</HTML>

На рис. 7.5 изображена корзина, состоящая из нескольких позиций. Обратите внимание на разное количество единиц и на вывод стоимости каждой позиции в столбце Total. В нижней части страницы выводится общая стоимость всей корзины. Также обратите внимание на ссылку Click here to continue shopping - она возвращает покупателя к последнему разделу, в котором он производил покупки.

ПРИМЕЧАНИЕ
В корзине выводится только общая стоимость корзины. Налоги и расходы на доставку вычисляются в процессе оформления заказа. Если в вашем магазине доставка обходится дороже обычного или действуют особые налоговые условия, возможно, эти затраты следует отображать на уровне корзины, однако для их вычисления покупатель обычно должен ввести по крайней мере почтовый индекс места доставки и/или выписки счета.

Рис. 7.5. Корзина

Итак, мы разобрались с выводом корзины и создали все ссылки, при помощи которых покупатель выполняет операции с корзиной.

Операции с корзиной

Наш покупатель получил возможность добавлять новые позиции в корзину и просматривать ее текущее содержимое. Перейдем к средствам управления корзиной - страницам UpdateBasket.asp, DeleteItem.asp и EmptyBasket.asp.

Обновление корзины

Первая страница, UpdateBasket.asp, не выводит никаких данных. Она обрабатывает запросы на обновление позиций заказа. В самом начале своей работы страница открывает подключение к базе данных.

Установив связь с базой, мы читаем содержимое корзины текущего покупателя. Страница в цикле перебирает позиции корзины и обновляет запись каждого товара. Второй набор записей обеспечивает подключение к базе данных для обновления корзины. Код страницы UpdateBasket.asp приведен в листинге 7.21.

Листинг 7.21. UpdateBasket.asp

<%@ Language=VBScript %>
<%
' ****************************************************
' страница читает новые количества единиц для каждой позиции
' корзины и вносит соответствующие изменения в базу данных.
' ****************************************************

' Создать объект подключения к базе данных
set dbBasketItem = server.createobject("adodb.connection")

' Создать набор записей
set rsBasketItem = server.CreateObject("adodb.recordset")

' Открыть подключение, используя файловый DSN ODBC
dbBasketItem.open("filedsn=WildWillieCDs")

' Прочитать содержимое корзины
sql = "execute sp_RetrieveBasketItem " & session("idBasket")

' Выполнить команду SQL
set rsBasketItem = dbBasketItem.Execute(sql)

' Создать объект подключения к базе данных
set dbUpdateItem = server.createobject("adodb.connection")

' Создать набор записей
set rsUpdateItem = server.CreateObject("adodb.recordset")

' Открыть подключение, используя файловый DSN ODBC
dbUpdateItem.open("filedsn=WildWillieCDs")

' В цикле перебирать позиции корзины
do until rsBasketItem.EOF

При каждой итерации мы получаем количество единиц в текущей позиции, определяемой идентификатором товара из открытого набора записей. Как упоминалось выше, атрибут VALUE каждого текстового поля Quantity на странице Basket.asp содержит идентификатор товара. Таким образом, при вызове Request мы должны передать идентификатор товара, обрабатываемого в текущей итерации.

Хранимая процедура sp_UpdateBasketItemQuantity обновляет запись новым количеством единиц. Обратите внимание: в качестве параметров хранимой процедуре передаются идентификаторы корзины и товара.

После завершения цикла покупатель отправляется на страницу Basket.asp. На этой странице выводятся новые количества единиц товаров, а также новые стоимости по всем позициям и новая общая стоимость.

Листинг 7.22. UpdateBasket.asp (продолжение)

' Retrieve the quantity. We use the ID of the
' basket item from the database to retrieve the
' datan from the correct input box.
intQuantity = request(cstr(rsBasketItem("idProduct")))

' Call the stored procedure to update the quantity
sql = "execute sp_UpdateBasketItemsQuantity " & _
session("idBasket") & ", " & intQuantity & ", " & _
rsBasketItem("idProduct")

' Execute the SQL statement
set rsUpdateItem = dbUpdateItem.Execute(sql)

' Move to the next item
rsBasketItem.MoveNext

loop

' Send the user to the basket page
Response.Redirect "basket.asp"

%>

Хранимая процедура sp_RetrieveBasketItem используется для чтения содержимого заданной корзины из базы данных. Идентификатор корзины передается при вызове процедуры в качестве параметра.

Листинг 7.23. Хранимая процедура sp_RetrieveBasketItem

/* Загрузка содержимого корзины из базы данных */
CREATE PROCEDURE sp_RetrieveBasketItem

/* При вызове процедуре передается идентификатор корзины */
@idBasket int

AS

/* Выборка записей, относящихся к заданной корзине */
select * from basketitem where idBasket = @idBasket

Примерный вид страницы корзины. В корзину входит два товара, Circle Sax и Joe Bob's Thimble Sounds. Первый товар приобретается в количестве 6 единиц, а второй - в количестве 9 единиц. Давайте изменим количество единиц каждого товара на 3.

Обновленная корзина. Обратите внимание - изменилось не только количество единиц, но и стоимость по каждой позиции, а также общая стоимость всей корзины.

Удаление товаров из корзины

Следующая страница также не выводит данных для пользователя. Она предназначена для обработки запросов на удаление позиций из корзины. В начале своей работы страница открывает подключение к базе данных, используемое в дальнейшем для удаления записи. Задача решается страницей Deleteltem.asp, приведенной в листинге 7.24.

Листинг 7.24. DeleteItem.asp

<%@ Language=VBScript %>
<%

' ****************************************************
' DeleteItem.asp - удаление заданной позиции из корзины.
' ****************************************************

' Создать подключение ADO к базе данных
set dbBasketItem = server.createobject("adodb.connection")

' Создать набор записей
set rsBasketItem = server.CreateObject("adodb.recordset")

' Open the connection using our ODBC file DSN
dbBasketItem.open("filedsn=WildWillieCDs")

Идентификатор товара, удаляемого из корзины, включается в URL страницы. При вызове хранимой процедуры sp_RemoveBasketItem передаются идентификаторы корзины и позиции. После выполнения процедуры покупатель возвращается на страницу Basket.asp.

Листинг 7.25. DeleteItem.asp (продолжение)

' Call the delete item SQL statement and
' pass in the ID of the basket and the id of
' the item.
sql = "execute sp_RemoveBasketItem " & session("idBasket") & _
", " & request("idBasketItem")

' Execute the SQL statement
set rsBasketItem = dbBasketItem.Execute(sql)

' Send the user back to the baket page
Response.Redirect "Basket.asp"

%>

Хранимая процедура sp_RemoveBasketItem удаляет из таблицы запись указанной позиции. Параметры корзины и позиции передаются в качестве параметров. Удаление записей осуществляется командой SQL DELETE.

Листинг 7.26. Хранимая процедура sp_RemoveBasketItem

/* Удаление позиций из корзины */
CREATE PROCEDURE sp_RemoveBasketItem

/* При вызове процедуре передаются идентификаторы корзины и удаляемой позиции */
@idBasket int,
@idBasketItem int

AS

/* Удаление позиции из базы данных */
delete from basketitem
where idBasket = @idBasket and
idBasketItem = @idBasketItem

Чтобы проверить новую возможность, попробуйте создать корзину, состоящую из 5 позиций. Щелкните на ссылке Remove рядом с товаром Т-Shirt Rip.

Корзина после удаления позиции. Обратите внимание: общая стоимость корзины изменилась, а оставшаяся часть корзины осталась без изменений.

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

Очистка корзины

Наконец, покупатель должен иметь возможность полностью очистить корзину. Начало программного кода страницы EmptyBasket.asp приведено в листинге 7.27. Конечно, мы надеемся, что покупателю эта возможность не понадобится, однако сознание того, что все отобранные товары можно в любой момент убрать из корзины и отказаться от покупки, придаст ему уверенности.

Работа страницы начинается с открытия базы данных. При вызове этой странице не передаются никакие параметры. Все, что необходимо знать - идентификатор корзины. Страница вызывает хранимую процедуру sp_ClearBasketItems и передает ей идентификатор корзины. После этого покупатель направляется на страницу Basket.asр.

Листинг 7.27. EmptyBasket.asp

<%@ Language=VBScript %>
<%
' ****************************************************
' EmptyBasket.asp - очистка корзины
' ****************************************************

' Создать объект подключения к базе данных
set dbBasketItem = server.createobject("adodb.connection")

' Создать набор записей
set rsBasketItem = server.CreateObject("adodb.recordset")

' Открыть подключение, используя файловый DSN ODBC
dbBasketItem.open("filedsn=WildWillieCDs")

' Построить команду вызова хранимой процедуры для очистки корзины
sql = "execute sp_ClearBasketItems " & session("idBasket")

' Выполнить команду SQL
set rsBasketItem = dbBasketItem.Execute(sql)

' Направить пользователя на страницу Basket
Response.Redirect "basket.asp"

%>

Хранимая процедура sp_ClearBasketItems получает идентификатор корзины и выполняет команду удаления всех позиций заданной корзины.

Листинг 7.28. Хранимая процедура sp_ClearBasketItems

/* Clear the items in the basket */
CREATE PROCEDURE sp_ClearBasketItems

/* Pass in the ID of the basket */
@idBasket int

AS

/* Delete all items in the specified basket */
delete from basketitem where idBasket = @idBasket

Итоги

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

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

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

Глава 8. Оформление заказа

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

Последовательность оформления заказа

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

Таблица 8.1. Процесс оформления заказа
Стадия
Описание
Стоимость доставки Прежде всего покупатель вводит информацию, необходимую для доставки заказа. На основании этих данных вычисляется стоимость доставки
Проверка реквизитов доставки Данные, отправленные покупателем, необходимо немедленно проверить. Мы должны убедиться в наличии всей необходимой информации
Выписка счета На этой странице покупатель вводит данные для выписки счета, а также данные кредитной карты. Кроме того, здесь выводятся суммы налога и стоимости доставки
Проверка данных для выписки счета Данные выписки счета, как и данные доставки, необходимо проверить
Подтверждение После успешного оформления заказа выводится подтверждающее сообщение и номер оформленного заказа

Процесс оформления заказа имеет весьма прямолинейную структуру, однако в нем используется вся основная бизнес-логика, на которой строится Web-сайт. Общая схема процесса изображена на рис. 8.1.

Рис. 8.1. Процесс оформления заказа

Оформление заказа начинается с корзины. Как видно из рисунка, на следующем этапе выполняется ввод данных доставки и их последующая проверка. Если в данных допущена ошибка, покупатель возвращается на предыдущую страницу с сообщением об ошибке.

Когда пользователь переходит к процессу выписки счета, мы вычисляем налог и стоимость доставки. В нашем примере для этого на Visual Basic будет построен объект СОМ, вызываемый из страниц ASP.

ПРИМЕЧАНИЕ
В нашем примере смоделирован относительно стандартный процесс. Впрочем, на эту тему существует много разных вариаций. Например, мы поддерживаем лишь пару дополнительных параметров для международной доставки, но во многих магазинах предусмотрены разные процессы оформления заказа в зависимости от того, где находится покупатель - в этой же стране или за рубежом.

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

В некоторых магазинах (например, amazon.com) используются сложные варианты оформления заказов с сохранением различных адресов и платежных реквизитов. Это сделано для того, чтобы по возможности упростить процесс оплаты покупок. Кроме того, в магазинах типа amazon.com предусматриваются и такие возможности, как оформление заказа одним щелчком мыши.

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

Определение профиля покупателя

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

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

Таблица 8.2. Основные функции профилей
Функция
Описание
Редактирование профиля Пользователь может отредактировать созданный профиль, щелкнув на ссылке, расположенной на панели ссылок. Редактирование профиля также осуществляется при каждом размещении заказа
Отправка пароля по электронной почте Чтобы прочитать информацию профиля (или историю заказов), пользователь должен ввести имя и пароль. Если пользователь забудет пароль, он может запросить его по электронной почте
Создание профиля Профиль создается при размещении пользователем заказа. На странице выписки счета пользователю предоставляется возможность выбора между созданием нового и обновлением существующего профиля
Автоматическая загрузка профиля При установке cookie профиль пользователя будет автоматически загружен при возвращении в магазин. Кроме того, для загрузки профиля пользователь может ввести свое имя и пароль

На рис. 8.2 показаны стадии процесса покупки, на которых используется профиль пользователя.

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

Как будет видно из программного кода, обращение к данным профиля происходит примерно в тех же местах, что и на диаграмме. Страница Header.asp (см. главу 6) ищет на компьютере покупателя cookie для получения информации о нем. В табл. 8.3 описаны основные сценарии загрузки данных профиля.

Рис. 8.2. Использование профиля пользователя в процессе покупки

Таблица 8.3. Сценарии загрузки профиля
Сценарий Описание
Cookie отсутствует Если cookie отсутствует на компьютере, пользователь завершает процесс оформления заказа и создает новый профиль (если он не желает загрузить старый профиль посредством ввода имени и пароля)
Cookie присутствует Страница Header.asp читает из cookie идентификатор покупателя. Затем она проверяет, существует ли у данного покупателя необработанная корзина
Cookie отсутствует - пользователь вводит имя и пароль Если на компьютере покупателя нет cookie, но пользователь загружает профиль по имени и паролю, читается только информация о покупателе. Мы не должны стирать существующую корзину, загружая предыдущую, необработанную корзину
Предыдущий профиль не существует Если профиль не существует, он создается при оформлении заказа независимо от того, вводится ли при этом пароль или нет. Впрочем, последующая загрузка профиля возможна лишь при наличии пароля

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

СОВЕТ
Если вы захотите проверить различные сценарии чтения данных профиля покупателя, найдите cookie магазина Wild Willie в соответствующей локальной папке. Удаление cookie позволяет отменить автоматическую загрузку профиля. Способ хранения cookie зависит от типа броузера и его версии - за конкретной информацией обращайтесь к документации или справочным файлам, прилагаемым к броузеру.

Загрузка данных для вычисления налога и стоимости доставки

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