В предыдущей части был заложен тот фундамент, на котором строится все взаимодействие пользователя с сайтом. Предполагалось, что все данные были заранее загружены и готовы к использованию. Настало время перейти к базовому интерфейсу управления магазином. Эта тема может оказаться очень обширной, а иногда и чрезвычайно сложной в зависимости от конкретной ситуации.
Основное внимание в этой главе уделяется основам управления данными товаров, разделов, заказов, налогов и стоимости доставки. Эти ключевые средства нужны практически в любом магазине. Даже если данные в конечном счете поставляются вспомогательной системой, скорее всего, вам все же потребуется вносить оперативные изменения в базы данных работающего магазина.
Глава 10. Управление информацией о товарах
Работа над программой управления магазином начнется с рассмотрения проблем безопасности, а также общей схемы операций с данными товаров и разделов. Мы должны реализовать возможности создания, обновления и удаления товаров и разделов в базах данных магазина. В частности, нам придется решить вопросы классификации товаров по разделам, назначения атрибутов, работы с графическими изображениями и прочих общих аспектов управления данными магазина.
Кроме того, необходимо определить интерфейс работы с управляющей программой по аналогии с внешним интерфейсом, предназначенным для покупателей. Наконец, мы должны создать систему безопасности, чтобы оградить управляющую программу от посторонних пользователей.
Проектирование программы управления магазином
Программа управления магазином представляет собой сложное приложение для работы с базой данных, лежащей в основе нашего магазина. В этой главе мы реализуем ряд функциональных возможностей, обеспечивающих выполнение основных операций с товарами и разделами. Иерархическая структура этих возможностей изображена на рис. 10.1.
На самом верхнем уровне иерархии пользователь регистрируется для работы в программе управления магазином. Ему на выбор предоставляется несколько возможностей - список товаров, список разделов, налоги, доставки и отчеты по заказам. С этого уровня структура разделяется на отдельные операции для каждого варианта. В табл. 10.1 перечислены основные операции, описанные в этой главе.
Таблица 10.1. Основные операции с товарами и разделами
| |
|
Административный
вход |
Проверка
данных пользователя при входе в управляющую программу |
Создание
нового товара |
Добавить
новый товар в базу данных магазина |
Список товаров |
Вывести
список товаров в базе данных |
Поиск товаров |
Найти товары
в базе данных |
Удаление
товаров |
Удалить
товар из базы данных |
Обновление
товаров |
Обновляет
данные товара |
Операции
с атрибутами товара |
Создание,
обновление и удаление атрибутов товара |
Список разделов |
Вывести
список разделов в базе данных |
Обновление
разделов |
Обновить
данные раздела |
Создание
нового раздела |
Создать
новый раздел в базе данных |
Обновление
раздела |
Обновляет
данные раздела в базе данных |
Для построения одинаковой панели ссылок на всех страницах сайта будет использован тот же способ, как и в интерфейсе покупателя. Содержимое заголовочного файла NavInclude.asp, создающего панель ссылок, приведено в листинге 10.1.
Листинг 10.1. NavInclude.asp
<!-- NavInclude.asp -
заголовочный файл программы управления электронным магазином.-->
<hr>
<center>
<!-- Link to the listing
of products -->
<a href="ListProducts.asp">
Manage Products<a> |
<!-- Link to the listing of departments -->
<a href="ListDepts.asp">
Manage Departments</a> |
<!-- Link to the management of the tax settings -->
<a href="ManageTax.asp">
Manage Tax</a> |
<!-- Link to the management of the shipping settings. -->
<a href="ManageShipping.asp">
Manage Shipping</a> |
<!-- Link to the management of the orders. -->
<a href="ManageOrders.asp">
Manage Orders</a> |
</center>
<hr>
В сущности, панель представляет собой набор ссылок на основные страницы, относящиеся к различным аспектам управления работой сайта. Панель размещается в верхней части каждой страницы в виде заголовочного файла ASP. Такое решение позволяет легко обновить навигационный интерфейс в случае расширения функциональных возможностей программы.
Безопасность
В программе управления электронным магазином необходимо реализовать систему безопасности. Нельзя допустить, чтобы доступ к управлению магазином был открыт для посторонних. Первое, что для этого понадобится, - страница для регистрации пользователей (см. рис. 10.3).
Рис. 10.3. Страница ввода регистрационных данных
Страница регистрации Login.asp представляет собой простую форму с текстовыми полями для ввода имени пользователя и пароля. Введенные данные передаются основной странице управляющей программы.
Листинг 10.2. Login.asp
<%@ Language=VBScript
%>
<HTML>
<!--
Login.asp - Login in page for the site
administrator.
-->
<HEAD>
<META NAME="GENERATOR" Content="Microsoft Visual Studio 6.0">
</HEAD>
<BODY>
<B>Please login:</b><BR><BR>
<!-- Start the form for
the user to enter in
their username and password. -->
<form method="post" action="ManagerMenu.asp">
<table>
<tr>
<td align="right">Username:</td>
<td>
<!-- The input text box for the username. -->
<input type="text" value="" name="username">
</td>
</tr>
<tr>
<td align="right">Password:</td>
<td>
<!-- The input text box for the password. -->
<input type="password" value="" name="password">
</td>
<tr>
<tr>
<td colspan="2">
<!-- The submit button for the form. -->
<input type="Submit" value="Submit" name="Submit">
</td>
</tr>
</table>
</form>
</BODY>
</HTML>
Данные, введенные пользователем при заполнении формы, передаются странице ManagerMenu.asp. Страница проверяет правильность имени пользователя и пароля. Фрагмент страницы, связанный с проверкой данных, приведен в листинге 10.3.
Листинг 10.3. ManagerMenu.asp
ManagerMenu.asp -
<%@ Language=VBScript
%>
<%
' ****************************************************
' ManagerMenu.asp - меню управляющих операций.
' ****************************************************
' Убедиться в том, что введенные
данные соответствуют
' данным администратора.
if request("username") <> "Admin" and _
request("password") <> "Password" then
' Вернуться на страницу login.asp.
Response.Redirect "login.asp"
else
' Установить признак успешной
проверки пользователя
Session("Validated") = true
end if
%>
Этот фрагмент проверяет, совпадают ли введенные данные со строками Admin и Password. Если данные оказываются неправильными, пользователь возвращается на страницу Login.asp. Если данные совпали, мы устанавливаем сеансовую переменную - признак успешной регистрации пользователя. Значение этой переменной будет проверяться на других страницах сайта.
Листинг 10.4. ManagerMenu.asp (продолжение)
<HTML>
<HEAD>
<META NAME="GENERATOR" Content="Microsoft Visual Studio 6.0">
</HEAD>
<BODY>
<!-- Welcome the user
-->
<center>
<BR><BR><b>
Welecome to Wild Willie's CD Store Order Manager.
Select a function below:
</b><br></br>
<!-- Build a table to
show the management opitons. -->
<table border="1" cellpadding="3" cellspacing="3">
<tr>
<th>Function</th>
</tr>
<tr>
<!-- Manage products -->
<td><a href="ListProducts.asp">
Manage Products<a></td>
</tr>
<tr>
<!-- Manage departments. -->
<td><a href="ListDepts.asp">
Manage Departments</a></td>
</tr>
<tr>
<!-- Manage tax -->
<td><a href="ManageTax.asp">
Manage Tax</a></td>
</tr>
<tr>
<!-- Manage shipping -->
<td><a href="ManageShipping.asp">
Manage Shipping</a></td>
</tr>
<tr>
<!-- Manage orders -->
<td><a href="ManageOrders.asp">
Manage Orders</a></td>
</tr>
</table>
</center>
</BODY>
</HTML>
Вторая часть страницы представляет собой обычное меню для вызова различных функций управляющей программы. В дальнейшем ссылки этого меню выводятся на панели, расположенной в верхней части страницы. Страница ManagerMenu.asp изображена на рис. 10.4.
Рис. 10.4. Основная страница управляющей программы
Последний заголовочный файл ValidateCheck.asp (см. листинг 10.5) содержит простую проверку, выполняемую в начале каждой страницы.
Листинг 10.5. ValidateCheck.asp
<%
' ****************************************************
' ValidateCheck.asp - проверка регистрации пользователя.
' ****************************************************
' Check our session variable
to see if the user has
' been validated. This will help to ensure that
' none of the admin pages are accessed with out
' authorization.
if Session("Validated") <> true then
' Redirect back to the
login page.
Response.Redirect("login.asp")
end if
%>
Проверить сеансовую переменную и убедиться в том, что 1 пользователь прошел регистрацию. Это делается для того, ' чтобы пользователи не могли загружать страницы ' управляющей программы без предварительной проверки,
Вернуться на страницу login.asp. Response.Redirect("login.asp")
Мы просто проверяем, что сеансовой переменной было присвоено значение true. В противном случае пользователь возвращается на страницу ввода регистрационных данных.
ПРИМЕЧАНИЕ
Если во время сеанса работы пользователя произойдет тайм-аут, то значение сеансовой переменной будет потеряно, и пользователю придется регистрироваться заново. По умолчанию интервал тайм-аута равен 20 минутам, но это значение можно изменить в административных утилитах IIS или установить тайм-аут в коде ASP.ВЫБОР СИСТЕМЫ БЕЗОПАСНОСТИ
В нашем примере для аутентификации пользователя и защиты страниц использована очень простая модель безопасности. Однако существует немало других, более совершенных вариантов. Например, одним пользователям можно разрешить операции с товарами и разделами, а других пользователей ограничить операциями с заказами, то есть реализовать различные уровни доступа к системе. Для этого можно воспользоваться базой данных, содержащих имена пользователей и перечни разрешенных операций.
Возможны и другие меры по укреплению безопасности системы. Например, страницы управляющей программы могут находиться на другом сервере, принадлежащему другому домену. Для каталога управляющей программы можно применить средства безопасности уровня каталогов и регистрировать пользователя средствами NT Authentication.
Операции с товарами
Перейдем к выполнению управляющих операций с товарами. Первым шагом будет построение удобной системы вывода списка товаров. Кроме того, нам понадобятся средства для поиска товаров в базе.
В листинге 10.6 приведено начало страницы Li stProducts.asp, отображающей фиксированное количество товаров. Страница начинается с включения стандартных элементов: заголовочного файла ValidateCheck.asp, проверяющего аутентификацию пользователя, и файла NavInclude.asp, отображающего панель ссылок в верхней части страницы.
Листинг 10.6. ListProducts.asp
<%@ Language=VBScript
%>
<!-- #Include file="include/validatecheck.asp" -->
<HTML>
<!--
ListProducts.asp - Lists the products in the
store.
-->
<HEAD>
<META NAME="GENERATOR" Content="Microsoft Visual Studio 6.0">
</HEAD>
<BODY>
<!-- #include file="include/navinclude.asp"
-->
Взаимодействие с базой данных начинается в листинге 10.7. Мы создаем подключение к базе данных и проверяем, было ли присвоено значение сеансовой переменной ProdInc. Эта переменная определяет количество товаров, выводимых на каждой странице списка.
Листинг 10.7. ListProducts.asp (продолжение)
<%
' Create an ADO database
connection
set dbProducts = server.createobject("adodb.connection")
' Create the record set
set rsProducts = server.CreateObject("adodb.recordset")
' Open the connection using
our ODBC file DSN
dbProducts.open("filedsn=WildWillieCDs")
' The products will not
all be displayed at once. We
' want to set the Product Increment in a session variable.
' If the product increment is not set, then we will set
' it. In this case, we are defaulting it to 4.
if session("ProdInc") = "" then
' Default it to 4
session("ProdInc") = "4"
end if
Затем, как показано в листинге 10.8, из URL извлекается значение переменной StartProd, определяющей начальную позицию выводимого списка. Значение переменной задается при выборе ссылок, расположенных в конце страницы, в зависимости от того, к какой позиции списка пожелал перейти пользователь. Мы также убеждаемся в том, что переменной StartProd было присвоено значение и что оно не меньше 1.
Листинг 10.8. ListProducts.asp (продолжение)
' Определить начальную позицию
для вывода списка.
StartProd = request("StartProd")
' If there is no starting
point, then we will
' default it to 1.
if StartProd = "" then
StartProd = 1
end if
' If the user tries to decrement
past the first product,
' we default back to 1 for the first product.
if StartProd < 1 then
StartProd = 1
end if
Далее выполняется хранимая процедура sp_ManagerRetrieveProducts, которая возвращает несколько записей товаров, начиная с заданной (см. листинг 10.9).
Если полученный набор не содержит ни одной записи, мы возвращаемся в начало списка.
Листинг 10.9. ListProducts.asp (продолжение)
' Построить команду вызова
хранимой процедуры для чтения
' списка товаров, начиная с заданной начальной позиции
' с заданным приращением.
sql = "execute sp_ManagerRetrieveProducts " & _
StartProd & ", " & session("ProdInc")
' Execute the statement
set rsProducts = dbProducts.Execute(sql)
' Ensure some products are
returned.
if rsProducts.EOF then
' If none were, then lets
return to the
' beginning of the list.
StartProd = 1
' Build the stored procedure
sql = "execute sp_ManagerRetrieveProducts " & _
StartProd & ", " & session("ProdInc")
' Execute the statement
set rsProducts = dbProducts.Execute(sql)
end if
%>
Теперь все готово к построению списка товаров (см. листинг 10.10). Первая ссылка на странице предназначена для создания в базе данных записи нового товара. После нее начинается таблица, содержащая список товаров.
Листинг 10.10. ListProducts.asp (продолжение)
<!-- Ссылка на страницу
создания нового товара. -->
<BR><b>Click <a href="NewProduct.asp">here</a>
to add a new product.</b>
<!-- Start the display
of the product listing. -->
<BR><BR>
<b>To edit a product, select from the list below:</b>
<BR><BR>
<table cellpadding="3"
cellspacing="3">
<tr>
<th>Product ID</th>
<th>Name</th>
<th>Price</th>
</tr>
Далее мы в цикле перебираем полученные записи списка (см. листинг 10.11). Для каждого продукта создается ссылка на страницу ManageProduct.asp, где выполняются операции с конкретным товаром. Название товара также оформляется в виде ссылки. В третьем столбце таблицы выводится цена товара.
Листинг 10.11. ListProducts.asp (продолжение)
<%
' Перебор возвращенных записей.
do until rsProducts.EOF
%>
<!-- Build a row to display
the list of products. -->
<tr>
<!-- A link is built to the ManageProduct.asp page.
The id of the product is passed on the URL and
the ID of the product is displayed.
-->
<td>
<a href="ManageProduct.asp?idProduct=<%=rsProducts("idProduct")%>">
<%=rsProducts("idProduct")%></a></td>
<!-- A link is built to the ManageProduct.asp page.
The id of the product is passed on the URL and
the name of the product is displayed.
-->
<td>
<a href="ManageProduct.asp?idProduct=<%=rsProducts("idProduct")%>">
<%=rsProducts("chrProductName")%></a></td>
<!-- Display the product price. NOte that the price is
stored as an integer. -->
<td><%=formatcurrency(rsProducts("intPrice")/100, 2)%></td>
</tr>
<%
' Move to the next row
rsProducts.MoveNext
' Loop back
Loop
%>
</table>
В конце страницы создается панель со ссылками first, previous и next. Ссылка first присваивает переменной StartProd значение 1. Ссылка previous возвращает список товаров на один экран назад. Наконец, ссылка next увеличивает сеансовую переменную StartProd на количество записей товаров, одновременно выводимых на экране (см. листинг 10.12).
Листинг 10.12. ListProducts.asp (продолжение)
<BR>
<!-- Панель ссылок, предназначенная для перемещения по списку товаров в прямом
и обратном направлении. В URL каждой ссылки передается новая начальная позиция
в списке. Значение переменной StartProd присваивается в соответствии со значением
сеансовой переменной ProdIlnc. -->
<a href="ListProducts.asp?StartProd=1">First Product</a>
|
<a href="ListProducts.asp?StartProd=
<%=StartProd - cint(Session("ProdInc"))%>">Previous</a>
|
<a href="ListProducts.asp?StartProd=
<%=StartProd + cint(Session("ProdInc"))%>">Next</a>
<BR><BR>
Страница завершается средствами поиска. В нижней части страницы расположено текстовое поле, в котором вводится искомый текст. Введенные данные передаются странице SearchProducts.asp (см. листинг 10.13).
Листинг 10.13. ListProducts.asp (продолжение)
<!-- Форма для поиска
конкретных товаров в базе данных. Данные формы передаются странице SearchProducts.asp.
-->
<form method="post" action="SearchProducts.asp">
<!-- The table is created
to display the search
option -->
<table>
<tr>
<td align="right">Search Text:</td>
<!-- Текстовое поле для ввода искомого текста. -->
<td><input type="text" value="" name="SearchText"></td>
</tr>
<tr>
<td colspan="2">
<!-- Кнопка отправки данных. -->
<input type="submit" value="Submit" name="Submit">
</td>
</tr>
</table>
</form>
</BODY>
</HTML>
В работе страницы ListProducts.asp используется одна хранимая процедура, sp_ManagerRetrieveProducts. Значение rowcount задается таким образом, чтобы возвращаемое количество записей совпадало с количеством одновременно отображаемых товаров. Вывод начинается с заданной начальной позиции (см. листинг 10.14).
Листинг 10.14. Хранимая процедура sp_ManagerRetrieveProducts
CREATE PROCEDURE sp_ManagerRetrieveProducts
@intStartProdID int,
@intRowCount int
AS
set rowcount @intRowCount
select idProduct, chrProductName,
intPrice
from products where idProduct >= @intStartProdID
На этом завершается построение основных средств работы со списком товаров. Эта страница является отправной точкой для операций с отдельными товарами. На рис. 10.5 показано исходное состояние страницы со списком товаров.
Рис. 10.5. Первая страница списка товаров
Щелкните на ссылке next - в списке отобразится следующая группа товаров.
Страница поиска работает точно так же, однако в данном случае отображаются только те товары, которые содержат искомые ключевые слова.
Страница SearchProduct.asp (см. листинг 10.15) начинается с включения стандартных заголовочных файлов.
Листинг 10.15. SearchProducts.asp
<%@ Language=VBScript
%>
<!-- #Include file="include/validatecheck.asp" -->
<HTML>
<!--
SearchProducts.asp - Provides a feature to search
for products from the list.
-->
<HEAD>
<META NAME="GENERATOR" Content="Microsoft Visual Studio 6.0">
</HEAD>
<BODY>
<!-- #include file="include/navinclude.asp"
-->
Программный код страницы начинается с подключения к базе данных. Из переданного запроса извлекается искомый текст. Если запрос не передавался, искомый текст читается из сеансовой переменной, значение которой было присно после предыдущей передачи данных (см. листинг 10.16).
Листинг 10.16. SearchProducts.asp (продолжение)
<%
' Create an ADO database
connection
set dbProducts = server.createobject("adodb.connection")
' Create the record set
set rsProducts = server.CreateObject("adodb.recordset")
' Open the connection using
our ODBC file DSN
dbProducts.open("filedsn=WildWillieCDs")
' Check to see if there
is any search text.
if request("SearchText") <> "" then
' Retrieve the search text
session("SearchText") = request("SearchText")
end if
Как и при выводе списка товаров, мы проверяем переменные Prodlnc и StartProd (см. листинг 10.17). Если значения этих переменных не заданы, им присваиваются значения по умолчанию.
Листинг 10.17. SearchProducts.asp (продолжение)
' Проверить значение переменной,
определяющей количество
' одновременно отображаемых товаров.
if session("ProdInc") = "" then
' По умолчанию количество
товаров на экране равно 4.
session("ProdInc") = "4"
end if
' Определить начальную позицию
для вывода списка
StartProd = request("StartProd")
' Проверить, присвоено ли
значение переменной.
if StartProd = "" then
' Если значение не присвоено,
начинать с первой позиции
StartProd = 1
end if
' Проверить, не пытается
ли пользователь выйти
' за пределы списка
if StartProd < 1 then
' Перейти к первому товару
StartProd = 1
end if
В следующем фрагменте (см. листинг 10.18) выполняется хранимая процедура sp_ManageRetrieveProdSearch. В отличие от процедуры sp_ManagerRetrieveProducts, в качестве параметра ей передается искомый текст. Процедура возвращает лишь те товары, которые удовлетворяют заданному критерию. Обратите внимание - на этот раз пустой набор записей является возможным результатом поиска, поэтому соответствующая проверка не выполняется.
Листинг 10.18. Search Products.asp (продолжение)
' Построить команду вызова
хранимой процедуры
' sp_ManagerRetrieveProdSearch для отбора записей
' товаров, удовлетворяющих критерию поиска.
sql = "execute sp_ManagerRetrieveProdSearch " & _
StartProd & ", " & session("ProdInc") & ",
'" & _
Session("SearchText") & "'"
' Execute the statement
set rsProducts = dbProducts.Execute(sql)
%>
Дальше начинается непосредственный вывод информации (см. листинг 10.19). Сначала на странице создается ссылка для ввода нового товара. Вторая ссылка возвращает пользователя к полному списку товаров, если он захочет прекратить поиск. Затем выводится список найденных товаров, причем каждый элемент списка содержит ссылку на страницу выполнения операций с отдельным товаром.
Листинг 10.19. SearchProducts.asp (продолжение)
<!-- Build a link to
the NewProduct.asp page in case the
user wants to add a new product. -->
<BR><b>Click <a href="NewProduct.asp">here</a>
to add a new product.</b>
<!-- Build a link to
list the full product selection -->
<BR><BR><b>Click <a href="ListProducts.asp">here</a>
to see the full listing.</b>
<!-- Start the display
of the search list. -->
<BR><BR><b>To edit a product, select from the
list below:</b><BR><BR>
<table cellpadding="3"
cellspacing="3">
<tr>
<th>Product ID</th>
<th>Name</th>
<th>Price</th>
</tr>
<%
' Loop through the products.
do until rsProducts.EOF
%>
<tr>
<td>
<!-- Display the product id. And, build a link to the
ManageProduct.asp with the product id -->
<a href="ManageProduct.asp?idProduct=<%=rsProducts("idProduct")%>">
<%=rsProducts("idProduct")%></a></td>
<td>
<!-- Display the product name. And, build a link to the
ManageProduct.asp with the product id -->
<a href="ManageProduct.asp?idProduct=<%=rsProducts("idProduct")%>">
<%=rsProducts("chrProductName")%></a></td>
<!-- Show the product price. Note it is stored as an integer. -->
<td><%=formatcurrency(rsProducts("intPrice")/100, 2)%></td>
</tr>
<%
' Move to the next row
rsProducts.MoveNext
' Loop back
Loop
%>
</table>
Как видно из листинга 10.20, страница завершается тем же набором ссылок, что и ListProducts.asp. Однако на этот раз данные возвращаются этой же странице, чтобы пользователь мог провести поиск заново и получить другой набор товаров.
Листинг 10.20. SearchProducts.asp (продолжение)
<BR>
<!-- Build the navigation to move backwards and forwards
between the product screens. -->
<a href="SearchProducts.asp?StartProd=1">First Product</a>
|
<a href="SearchProducts.asp?StartProd=
<%=StartProd - cint(Session("ProdInc"))%>">Previous</a>
|
<a href="SearchProducts.asp?StartProd=
<%=StartProd + cint(Session("ProdInc"))%>">Next</a>
<BR><BR>
<!-- Build the form to
execute a new search. -->
<form method="post" action="SearchProducts.asp">
<!-- Build the table
-->
<table>
Текстовое поле создается на случай, если пользователь захочет снова провести поиск, но уже с другим искомым текстом (см. листинг 10.21).
Листинг 10.21. SearchProducts.asp (продолжение)
<!-- Build the input
HTML element for the search text. -->
<tr>
<td align="right">Search Text:</td>
<td><input type="text" value="" name="SearchText"></td>
</tr>
<!-- Build a submit button
for the search. -->
<tr>
<td colspan="2">
<input type="submit" value="Submit" name="Submit">
</td>
</tr>
</table>
</form>
</BODY>
</HTML>
В работе страницы используется одна хранимая процедура, sp_ManagerRetrieveProdSearch. Она находит записи товаров, в названиях которых присутствует искомый текст, введенный пользователем (см. листинг 10.22).
Листинг 10.22. Хранимая процедура sp_ManagerRetrieveProdSearch
CREATE PROCEDURE sp_ManagerRetrieveProdSearch
@intStartProdID int,
@intRowCount int,
@chrSearchText varchar(100)
AS
set rowcount @intRowCount
select idProduct, chrProductName,
intPrice
from products
where idProduct >= @intStartProdID and
chrProductName like '%' + @chrSearchText+ '%'
Теперь вы можете проводить поиск на странице списка товаров. Введите в текстовом поле строку "Dog".
Перейдем к созданию новых товаров в базе данных. Чтобы создать запись нового товара, пользователь щелкает на верхней ссылке в списке товаров или на странице результатов поиска.
Начало страницы NewProduct.asp, предназначенной для ввода информации о новом товаре, приведено в листинге 10.23. В сущности, страница представляет собой простую форму ввода данных без непосредственного включения кода VBScript. Страница начинается со стандартной проверки пользователя и создания панели ссылок.
Листинг 10.23. NewProduct.asp
<%@ Language=VBScript
%>
<!-- #Include file="include/validatecheck.asp" -->
<html>
<!--
NewProduct.asp - Handles adding in a new product
into the store.
-->
<head>
<meta NAME="GENERATOR" Content="Microsoft Visual Studio 6.0">
</head>
<body>
<!-- #Include file="include/navinclude.asp" -->
Форма отправляет данные странице AddNewProduct.asp (см. листинг 10.24). На форме создается набор элементов HTML, предназначенных для ввода названия товара, описания, графического изображения и признака активности. Атрибуты и принадлежность товара к тем или иным разделам определяются не здесь, а на странице ManageProduct.asp.
Листинг 10.24. NewProduct.asp (продолжение)
<!-- Form to post the
new product to the database -->
<form method="post" action="AddNewProduct.asp">
<!-- Table build the
form for adding the new product -->
<table cellpadding="3" cellspacing="3">
<!-- Product Name Input
-->
<tr>
<td align="right"><b>Product Name:</b></td>
<td>
<input type="text" value="" name="chrProductName"
size="60">
</td>
</tr>
<!-- Product Description
Input -->
<tr>
<td align="right"><b>Product Description:</b></td>
<td>
<textarea cols="50" rows="10" name="txtDescription"></textarea>
</td>
</tr>
<!-- Product Image Input
-->
<tr>
<td align="right"><b>Product Image:</b></td>
<td><input type="text" value="" name="chrProductImage"></td>
</tr>
<!-- Product Price Input
-->
<tr>
<td align="right"><b>Product Price:</b></td>
<td><input type="text" value="" name="intPrice"></td>
</tr>
<!-- Check box to indicate
the product is active -->
<tr>
<td align="right"><b>Active:</b></td>
<td>
<input type="checkbox" value="1" name="intActive">
</td>
</tr>
<!-- Submit button to
add the product -->
<tr>
<td colspan="2" align="center">
<input type="submit" value="Add Product" name="Submit">
</td>
</tr>
<!-- Close out the page.
-->
</table>
</form>
</body>
</html>
Страница AddNewProduct.asp (см. листинг 10.25) включает новый товар в базу данных. Необходимые значения берутся из полей формы. Обратите внимание - цена товара умножается на 100 для того, чтобы значение хранилось в базе в виде целого числа.
Непосредственное занесение данных в базу осуществляется хранимой процедурой sp_InsertProduct. После создания записи процедура возвращает идентификатор нового товара, и пользователь направляется на страницу ManageProduct.asp для продолжения редактирования.
Листинг 10.25. AddNewProduct.asp
<%@ Language=VBScript
%>
<%
' ****************************************************
' AddNewProduct.asp - занесение нового товара в базу данных магазина.
' ****************************************************
' Получить название товара
и удвоить одиночные апострофы.
chrProductName = replace(request("chrProductName"), "'",
"''")
' Retrieve the product description
and ensure that
' any single quotes are doubled.
txtDescription = replace(request("txtDescription"), "'",
"''")
' Retrieve the product image.
chrProductImage = request("chrProductImage")
' Retrieve the price. Ensure
that we multiply
' times 100 to store as a whole integer.
intPrice = request("intPrice") * 100
' Retrieve the active setting.
intActive = request("intActive")
' We have to check and see if any setting is made.
' It will not be set if the box is not checked.
if intActive = "" then
' Set the flag to 0 so
it is not active.
intActive = 0
else
' Set the flag to 0 so
it is active.
intActive = 1
end if
' Create an ADO database
connection
set dbProduct = server.createobject("adodb.connection")
' Create the record set
set rsProduct = server.CreateObject("adodb.recordset")
' Open the connection using
our ODBC file DSN
dbProduct.open("filedsn=WildWillieCDs")
' Execute the sp_InsertProduct
stored procedure
' to add the product into the database.
sql = "execute sp_InsertProduct '" & _
chrProductName & "', '" & _
txtDescription & "', '" & _
chrProductImage & "', " & _
intPrice & ", " & _
intActive
' Execute the statement
set rsProduct = dbProduct.Execute(sql)
' Send the user to the ManageProduct.asp
page to allow
' the user to edit the new product.
Response.Redirect "ManageProduct.asp?idProduct=" & _
rsProduct("idProduct")
%>
Занесение нового товара в базу осуществляется хранимой процедурой sp_InsertProduct (см. листинг 10.26). Данные передаются в виде параметров. Процедура возвращает идентификатор нового товара.
Листинг 10.26. Хранимая процедура sp_InsertProduct
CREATE PROCEDURE sp_InsertProduct
@chrProductName varchar(255),
@txtDescription text,
@chrProductImage varchar(100),
@intPrice int,
@intActive int
AS
insert into products(chrProductName,
txtDescription, chrProductImage, intPrice, intActive)
values(@chrProductName, @txtDescription, @chrProductImage, @intPrice, @intActive)
select idProduct = @@identity
Страница ввода информации о новом товаре изображена на рис. 10.8. Заполните форму и нажмите кнопку Add Product, данные поступят в базу, после чего активизируется режим редактирования на странице ManageProduct.asp.
Длинная страница ManageProduct.asp, предназначенная для операций с товаром, начинается в листинге 10.27. На этой странице обеспечивается централизованное редактирование всех данных товара. Страница начинается со стандартного включения заголовочных файлов для проверки пользователя и создания панели ссылок.
Листинг 10.28. ManageProduct.asp
<%@ Language=VBScript
%>
<!-- #Include file="include/validatecheck.asp" -->
<html>
<!--
ManageProduct.asp - Provides the tools to manage
the product data.
-->
<head>
<meta NAME="GENERATOR" Content="Microsoft Visual Studio 6.0">
</head>
<body>
<!-- #Include file="include/navinclude.asp" -->
Непосредственная работа начинается с загрузки данных товара, идентификатор которого передается в составе URL. Страница читает идентификатор и вызывает хранимую процедуру sp_RetrieveProduct, загружающую данные товара (см. листинг 10.28).
Листинг 10.28. ManageProduct.asp (продолжение)
<%
' Create an ADO database
connection
set dbProduct = server.createobject("adodb.connection")
' Create the record set
set rsProduct = server.CreateObject("adodb.recordset")
' Open the connection using
our ODBC file DSN
dbProduct.open("filedsn=WildWillieCDs")
' The sp_RetrieveProduct
stored procedure is utilized to
' get the data for the specified product.
sql = "execute sp_RetrieveProduct " & request("idProduct")
' Execute the statement
set rsProduct = dbProduct.Execute(sql)
%>
Выводимые данные начинаются с формы, передающей основные данные товара странице UpdateProduct.asp (см. листинг 10.29). Данные оформляются в виде таблицы. Обратите внимание на ссылку для предварительного просмотра - при помощи этой ссылки можно оценить, как будет выглядеть товар в электронном магазине, перед установкой флага активности.
Листинг 10.29. ManageProduct.asp (продолжение)
<!-- Start the form to
update the product data. -->
<form method="post" action="UpdateProduct.asp">
<!-- Start the table
to display the product data. -->
<table cellpadding="3" cellspacing="3">
<tr>
<td align="right"><b>Preview Product:</b></td>
<!-- To preview the product a link is built to
the product.asp page in the live store.
The Id of the product is passed on the URL. -->
<td><a href="/ecstore/wildwilliecds/product.asp?idProduct=<%=request("idProduct")%>">Preview
</a></td>
</tr>
<tr>
<td align="right"><b>Delete Product:</b></td>
<!-- A link to the deleteproduct.asp page is
created to remove the product from the
database. The ID of the product is
passed. -->
<td><a href="../Manager/DeleteProduct.asp?idProduct=<%=request("idProduct")%>">Delete
</a></td>
</tr>
<tr>
<td colspan="2"><hr></td>
</tr>
<tr>
<!-- The product ID is displayed. To ensure the ID
can be retrieved for the update a hidden
HTML element is created. -->
<td align="right"><b>Product ID:</b></td>
<td><%=rsProduct("idProduct")%>
<input type="hidden"
value="<%=request("idProduct")%>" name="idProduct">
</td>
</tr>
<tr>
<td align="right"><b>Product Name:</b></td>
<!-- Display the product name. -->
<td><input type="text"
value="<%=rsProduct("chrProductName")%>"
name="chrProductName" size="60">
</td>
</tr>
<tr>
<td align="right"><b>Product Description:</b></td>
<!-- Display the product description. -->
<td><textarea cols="50" rows="10" name="txtDescription"><%=rsProduct("txtDescription")%></textarea></td>
</tr>
В текстовом поле HTML выводится имя файла, содержащего графическое изображение товара, а также само изображение (см. листинг 10.30).
Листинг 10.30. ManageProduct.asp (продолжение)
<tr>
<td align="right"><b>Product Image:</b></td>
<!-- Display the product image file name and display
the image as well. -->
<td><input type="text" value="<%=rsProduct("chrProductImage")%>"
name="chrProductImage">
<img src="../wildwilliecds/images/products/sm_
<%=rsProduct("chrProductImage")%>" align="center"></td>
</tr>
<tr>
<td align="right"><b>Product Price:</b></td>
<!-- The product price is displayed. -->
<td><input type="text" value="<%=rsProduct("intPrice")/100%>"
name="intPrice"></td>
</tr>
<tr>
<td align="right"><b>Active:</b></td>
<td>
Флажок активности товара по умолчанию восстанавливается в предыдущем состоянии. Мы проверяем поле активности товара в базе данных и затем генерируем код HTML для установленного или сброшенного флажка (см. листинг 10.31).
Листинг 10.31. ManageProduct.asp (продолжение)
<%
' Check to see if the product is active.
if rsProduct("intActive") = 1 then
%>
<!-- Display the check box checked if the
product is active. -->
<input type="checkbox" value="1" CHECKED name="intActive">
<%
else
%>
<!-- Display the check
box with out the check. -->
<input type="checkbox" value="1" name="intActive">
<% end if %>
</td>
</tr>
<tr>
<td colspan="2" align="center">
<!-- Submit button for the form update -->
<input type="submit" value="Update Product" name="Submit">
</td>
</tr>
</table>
</form>
<hr>
Следующий фрагмент страницы относится к классификации товаров по разделам. Не забывайте о том, что товар может принадлежать сразу к нескольким разделам. Информация обо всех разделах, к которым в настоящее время относится товар, возвращается хранимой процедурой sp_RetrieveDeptByProd (см. листинг 10.32).
Листинг 10.32. ManageProduct.asp (продолжение)
<%
' Create an ADO database
connection
set dbDeptProd = server.createobject("adodb.connection")
' Create the record set
set rsDeptProd = server.CreateObject("adodb.recordset")
' Open the connection using
our ODBC file DSN
dbDeptProd.open("filedsn=WildWillieCDs")
' Execute the sp_RetrieveDeptByProd
to retrieve
' the departments for the product being edited.
sql = "execute sp_RetrieveDeptByProd " & request("idProduct")
' Execute the statement
set rsDeptProd = dbDeptProd.Execute(sql)
%>
Список разделов выводится в таблице. В каждой строке таблицы имеется ссылка для исключения товара из соответствующего раздела. Идентификатор товара включается в URL и передается странице RemoveProdDept.asp (см. листинг 10.33).
Листинг 10.33. ManageProduct.asp (продолжение)
<!-- Start the table
to display the
department list. -->
<table cellpadding="3" cellspacing="3" border="1">
<tr>
<th>Department</th>
<th>Delete</th>
<tr>
<%
' Loop through the departments.
do until rsDeptProd.eof
%>
<tr>
<!-- Display the department name. -->
<td><%=rsDeptProd("chrDeptName")%></td>
<!-- Build a link to the RemoveProdDept.asp page
to remove the department from the list. -->
<td><a href="RemoveProdDept.asp?idProduct=<%=request("idProduct")%>
&idDepartmentProduct=<%=rsDeptProd("idDepartmentProduct")%>">
Delete</a>
</td>
</tr>
</tr>
<%
' Move to the next department product
rsDeptProd.movenext
Loop
%>
</table>
После перечисления разделов, связанных с товаром в настоящий момент, создается список всех существующих разделов, чтобы мы могли отнести товар к новому разделу. Список создается на новой форме, передающей данные странице ProdAddDept.asp.
ПРИМЕЧАНИЕ
В данном примере в список включаются все существующие разделы, хотя логичнее было бы включить в него только те разделы, к которым товар не относится в настоящий момент. Кроме того, при отнесении товара к новому разделу следовало бы организовать проверку ошибок.
Хранимая процедура sp_RetrieveDepts получает из базы записи всех существующих разделов. Затем список заполняется в цикле Do Loop. Обратите внимание: идентификатор товара сохраняется на форме в скрытом поле. Это позволяет легко определить, с каким товаром связывается добавляемый раздел (см. листинг 10.34).
Листинг 10.34. ManageProduct.asp (продолжение)
<br>
<!-- Start a new form
to add an addition
department for the product. -->
<form method="post" action="ProdAddDept.asp">
<b>Add a department:</b>
<%
' Create an ADO database
connection
set dbDepts = server.createobject("adodb.connection")
' Create the record set
set rsDepts = server.CreateObject("adodb.recordset")
' Open the connection using
our ODBC file DSN
dbDepts.open("filedsn=WildWillieCDs")
' Retrieve all of the departments
in the database
sql = "execute sp_RetrieveDepts"
' Execute the statement
set rsDepts = dbDepts.Execute(sql)
%>
<!-- Start the select
box for the list of
departments. -->
<select name="idDepartment">
<%
' Loop through the departments
do until rsDepts.eof
%>
<!-- Build the option
list for each
department. -->
<option value="<%=rsDepts("idDepartment")%>">
<%=rsDepts("chrDeptName")%>
<%
' Move to the next row
rsDepts.movenext
loop
%>
</select>
<!-- Build a hidden variable
in this form so
we know what product to assign this
department to. -->
<input type="hidden" name="idProduct"
value="<%=request("idProduct")%>">
<!-- Submit button form
teh form. -->
<input type="submit" value="Submit" name="Submit">
</form>
<hr>
От разделов мы переходим к атрибутам товаров (см. листинг 10.35). В данном примере используется фиксированный набор категорий атрибутов - цвет и размер.
Сначала обрабатываются атрибуты категории "цвет". Чтение всех атрибутов, назначенных товару, осуществляется хранимой процедурой sp_Attributes.
Листинг 10.35. ManageProduct.asp (продолжение)
<%
' Create an ADO database
connection
set dbAttributes = server.createobject("adodb.connection")
' Create a record set
set rsAttributes = server.CreateObject("adodb.recordset")
' Open the connection using
our ODBC file DSN
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)
%>
Перечень атрибутов выводится в таблице (см. листинг 10.36). Как и при выводе разделов, рядом с каждым атрибутом выводится ссылка для удаления атрибута из информации текущего товара.
Листинг 10.36. ManageProduct.asp (продолжение)
<!-- Start the table
to build list of color and
size attributes. -->
<table>
<tr>
<td>
<b>COLOR:</b>
<!-- Build a list of current color assignments. -->
<table cellpadding="3" cellspacing="3" border="1">
<%
' Loop through the attributes.
do until rsAttributes.EOF
' Check to see if we have moved beyond the color
' attribute in the list..
if rsAttributes("chrCategoryName") <> "Color" then
' Exit the do loop
exit do
end if
%>
<tr>
<td>
<!-- Display the attribute name. -->
<%=rsAttributes("chrAttributeName")%>
</td>
<td>
<!-- Build a link to the DeleteAttribute.asp page
to remove the attribute for the product. -->
<a href="DeleteAttribute.asp
?idProduct=<%=request("idProduct")%>
&idProductAttribute=<%=rsAttributes("idProductAttribute")%>">
Delete</a>
</td>
</tr>
<%
' Move to the next row
rsAttributes.MoveNext
' Loop back
loop
%>
</table>
</td>
</tr>
<!-- Build a buffer between the listings. -->
<tr><td> </td></tr>
Далее выводятся текущие значения атрибутов размера (см. листинг 10.37). Они, как и атрибуты цвета, выводятся в таблице. Рядом с каждым атрибутом размера отображается ссылка удаления, в URL которой включается идентификатор атрибута.
Листинг 10.37. ManageProduct.asp (продолжение)
<tr>
<!-- Start the size listings -->
<td>
<b>SIZE: </b>
<!-- Start the size
listing table. -->
<table cellpadding="3" cellspacing="3" border="1">
<%
' Loop through the size attributes
do until rsAttributes.EOF
%>
<tr>
<td>
<!-- Display the name of the size attribute -->
<%=rsAttributes("chrAttributeName")%>
</td>
<td>
<!-- Build a link to the DeleteAttribute.asp
page to remove the size setting for the
product. -->
<a href="DeleteAttribute.asp
?idProduct=<%=request("idProduct")%>
&idProductAttribute=<%=rsAttributes("idProductAttribute")%>">
Delete</a>
</td>
</tr>
<%
' Move to the next row
rsAttributes.MoveNext
' Loop back
loop
%>
</table>
</td>
</tr>
</table>
Затем создаются списки атрибутов, в которых пользователь выбирает варианты расцветки и размера текущего товара. Чтобы упростить структуру страницы, для построения этого фрагмента используется процедура ShowAttributeList (см. листинг 10.38). За вызовом процедуры следуют стандартные завершающие теги.
Листинг 10.38. ManageProduct.asp (продолжение)
<!-- Вызвать процедуру
ShowAttributeList -->
<% ShowAttributeList %>
<!-- Завершить страницу
-->
</body>
</html>
В работе процедуры ShowAttributeList используется та же логика, что и при выводе текущих атрибутов размера и цвета. На этот раз вызывается хранимая процедура sp_RetrieveAttributes, возвращающая все атрибуты из базы данных (см. листинг 10.39).
Листинг 10.39. ManageProduct.asp (продолжение)
<!-- Start the ShowAttributeList subroutine. -->
<%
' Start the subroutine.
Sub ShowAttributeList()
' Create an ADO database
connection
set dbAttributes = server.createobject("adodb.connection")
' Create a record set
set rsAttributes = server.CreateObject("adodb.recordset")
' Open the connection using
our ODBC file DSN
dbAttributes.open("filedsn=WildWillieCDs")
' Execute the stored procedure
to retrieve the attributes
' in the database.
sql = "execute sp_RetrieveAttributes"
' Execute the SQL statement
set rsAttributes = dbProduct.Execute(sql)
%>
<br>
<!-- Start the option
to build the list of
attributes in the database. -->
<b>Select an Attribute to Add:</b>
Списки атрибутов заносятся в таблицу (см. листинг 10.40). Каждое значение атрибута заносится в список отдельной строкой. Обратите внимание - для каждого списка создается специальная форма, передающая данные странице AddAttribute.asp. В скрытом поле на форме сохраняется идентификатор товара, которому назначается новый атрибут.
Листинг 10.40. ManageProduct.asp (продолжение)
<table>
<tr>
<!-- Color column -->
<td>
<!-- Build a form to post the adding of the
attribute. -->
<form method="post" action="AddAttribute.asp">
Color:
<!-- Select box for display of the color options -->
<select name="idAttribute">
<%
' Loop through the attributes.
do until rsAttributes.EOF
' Check to see if we have moved beyond the color
' attribute in the list..
if rsAttributes("chrCategoryName") <> "Color" then
' Exit the do loop
exit do
end if
%>
<!-- Build the option value for the color. The value will be
the ID of the color -->
<option value="<%=rsAttributes("idAttribute")%>">
<%=rsAttributes("chrAttributeName")%>
<%
' Move to the next row
rsAttributes.MoveNext
' Loop back
loop
%>
</select>
<!-- Build a hidden variable to store the id
of the product so we know what product to
add the attribute to. -->
<input type="hidden" value="<%=rsProduct("idProduct")%>"
name="idProduct">
<!-- Submit button to add the attribute to the list. -->
<input type="Submit" value="Add" name="Submit">
</form>
</td>
</tr>
После списка цветов аналогичным образом создается список размеров (см. листинг 10.41).
ПРИМЕЧАНИЕ
Вероятно, в списки не следует включать размеры и цвета, уже назначенные текущему товару.
Листинг 10.41. ManageProduct.asp (продолжение)
<tr>
<!-- Build the size attributes select box. -->
<td>
<form method="post" action="AddAttribute.asp">
Size:
<!-- Start the size
select box -->
<select name="idAttribute">
<%
' Loop through the size attributes
do until rsAttributes.EOF
%>
<!-- Display the options -->
<option value="<%=rsAttributes("idAttribute")%>">
<%=rsAttributes("chrAttributeName")%>
<%
' Move to the next row
rsAttributes.MoveNext
' Loop back
loop
%>
</select>
<!-- Build the hidden HTML element to store the
product id. -->
<input type="hidden" value="<%=rsProduct("idProduct")%>"
name="idProduct">
<!-- Build the submit button for the form. -->
<input type="Submit" value="Add" name="Submit">
</form>
</td>
</tr>
</table>
<%
End Sub
%>
На этом редактирование товара подходит к концу. Страница делится на три части, относящиеся к разным аспектам редактирования. Первая часть связана с основными данными товара. Вторая часть выполняет операции с разделами, а третья - операции с атрибутами.
На странице используется несколько хранимых процедур. Первая, sp_RetrieveProduct, возвращает основные данные товара по идентификатору товара, переданному в качестве параметра (см. листинг 10.42).
Листинг 10.42. Хранимая процедура sp_RetrieveProduct
/* Загрузка данных товара
*/
CREATE PROCEDURE sp_RetrieveProduct
/* При вызове процедуре
передается идентификатор товара */
@idProduct int
AS
/* Выборка сведений о товаре
*/
select * from products
where idProduct = @idProduct
Хранимая процедура sp_RetrieveDeptByProd (см. листинг 10.43) возвращает информацию о разделах, к которым относится заданный товар. Идентификатор товара передается процедуре в качестве параметра.
Листинг 10.43. Хранимая процедура sp_RetrieveDeptByProd
CREATE PROCEDURE sp_RetrieveDeptByProd
@idProduct int
AS
select * from department,
departmentproducts
where departmentproducts.idProduct = @idProduct and
department.iddepartment = departmentproducts.iddepartment
Кроме того, нам понадобится получить из базы данных список всех текущих разделов. Задача решается хранимой процедурой sp_RetrieveDepts (см. листинг 10.44).
Листинг 10.44. Хранимая процедура sp_RetrieveDepts
/* Загрузка информации обо
всех разделах магазина из базы данных */
CREATE PROCEDURE sp_RetrieveDept
/* Выборка всех записей
разделов */
@idDepartment int
AS
/* Select all of the data
on the
department */
select * from department
where idDepartment = @idDepartment
Следующая хранимая процедура sp_Attributes загружает из базы все атрибуты, назначенные текущему товару (см. листинг 10.45). Идентификатор товара передается процедуре в качестве параметра. Для получения необходимых данных следует объединить таблицы Products, ProductAttribute, Attribute и AttributeCategory; объединение осуществляется средствами SQL.
Листинг 10.45. Хранимая процедура sp_Attributes
/* Загрузка атрибутов заданного
товара из базы данных */
CREATE PROCEDURE sp_Attributes
/* При вызове процедуре
передается идентификатор товара */
@idProduct int
AS
/* select statement to return
attributes for the product. */
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
Наконец, в листинге 10.46 приведена хранимая процедура sp_RetrieveAttributes, возвращающая все атрибуты текущей базы данных.
Листинг 10.46. Хранимая процедура sp_RetrieveAttributes
CREATE PROCEDURE sp_RetrieveAttributes AS
select * from attribute,
attributecategory
where attribute.idattributecategory = attributecategory.idAttributeCategory
order by attributecategory.chrCategoryName
Экран редактирования товара. Все текущие данные, прочитанные из базы, заносятся в поля формы. Обратите внимание: на странице отсутствует информация о разделах или атрибутах, назначенных товару.
Давайте свяжем товар с новым разделом. Выберите в списке Add a Department строку Cool Backstreet Jazz и щелкните на кнопке Submit. На рис. 10.10 показан вид страницы после назначения нового раздела.
Повторите операцию и свяжите товар со вторым разделом. На этот раз в списке следует выбрать строку Punked Out.
Теперь можно воспользоваться ссылкой Delete для удаления только что назначенного раздела.
Перейдем к операциям с атрибутами. Выберите из списков атрибуты red и small и включите их в информацию товара. При желании попробуйте поэкспериментировать с удалением атрибутов.
После того как информация о товаре будет полностью введена, можно посмотреть, как этот товар будет выглядеть в магазине - для этого следует щелкнуть на ссылке preview.
Рассмотрим вспомогательные страницы, обеспечивающие работу ManageProduct.asp. В листинге 10.47 приведена страница DeleteProduct.asp, удаляющая товар из базы данных. Она состоит исключительно из программного кода и возвращает пользователя к списку товаров.
Листинг 10.47. DeleteProduct.asp
<%@ Language=VBScript
%>
<%
' ****************************************************
' DeleteProduct.asp - Удаление товаров из магазина.
' ****************************************************
' Create an ADO database
connection
set dbProduct = server.createobject("adodb.connection")
' Create the record set
set rsProduct = server.CreateObject("adodb.recordset")
' Open the connection using
our ODBC file DSN
dbProduct.open("filedsn=WildWillieCDs")
При вызове хранимой процедуры sp_DeleteProduct передается идентификатор товара (см. листинг 10.48). Товар удаляется из базы данных, и пользователь возвращается к списку товаров.
Листинг 10.48. DeleteProduct.asp (продолжение)
' Call the sp_DeleteProduct
stored procedure to
' remove the product from the database.
sql = "execute sp_DeleteProduct " & request("idProduct")
' Execute the statement
set rsProduct = dbProduct.Execute(sql)
' Send the user back to
the list of producs.
Response.Redirect "ListProducts.asp"
%>
Программный код хранимой процедуры приведен в листинге 10.49. Процедура содержит команду SQL DELETE, удаляющую данные товара из базы.
СОВЕТ
При удалении товара в базе остаются сведения о присвоенных ему разделах и атрибутах. Для обеспечения целостности данных в базе эту информацию следовало бы удалить в хранимой процедуре или на уровне базы данных с использованием триггеров.
Листинг 10.49. Хранимая процедура sp_DeleteProduct
CREATE PROCEDURE sp_DeleteProduct
@idProduct int
AS
delete from products where
idProduct = @idProduct
Следующая вспомогательная страница, UpdateProduct.asp(см. листинг 10.50), обновляет базу данных информацией, введенной пользователем. Ее работа начинается со сбора данных, введенных пользователем. Перед сохранением строковых данных в базе все одиночные апострофы необходимо удвоить.
Листинг 10.50. UpdateProduct.asp
<%@ Language=VBScript
%>
<%
' ****************************************************
' UpdateProduct.asp - Handles updating the product
' data.
' ****************************************************
' Retrieve the product id
idProduct = request("idProduct")
' Retrieve the product name
and ensure any single
' quotes are doubled.
chrProductName = replace(request("chrProductName"), "'",
"''")
' Retrieve the product description
and ensure any single
' quotes are doubled.
txtDescription = replace(request("txtDescription"), "'",
"''")
' Retrieve the product image
chrProductImage = request("chrProductImage")
' Retrieve the product price
and multiply by 100 to
' ensure it is stored as an integer.
intPrice = request("intPrice") * 100
' Retrieve the active status.
intActive = request("intActive")
Флаг активности проверяется дополнительно (см. листинг 10.51). Если флаг не установлен, в базе данных сохраняется значение 0.
Листинг 10.51. UpdateProduct.asp (продолжение)
' Проверить, установлен
ли флажок активности товара.
if intActive = "" then
' Если флажок сброшен,
товар неактивен
intActive = 0
else
' Если флажок установлен,
товар активен
intActive = 1
end if
После получения данных с формы мы создаем подключение к базе данных и передаем изменения в базу (см. листинг 10.52). Соответствующая команда SQL вызывается в хранимой процедуре sp_UpdateProduct. Наконец, пользователь возвращается на страницу ManageProduct.asp и продолжает редактирование товара.
Листинг 10.52. UpdateProduct.asp (продолжение)
' Create an ADO database
connection
set dbProduct = server.createobject("adodb.connection")
' Create the record set
set rsProduct = server.CreateObject("adodb.recordset")
' Open the connection using
our ODBC file DSN
dbProduct.open("filedsn=WildWillieCDs")
' Execute the SQL stored
procedure to update the
' product data
sql = "execute sp_UpdateProduct " & _
request("idProduct") & ", '" & _
chrProductName & "', '" & _
txtDescription & "', '" & _
chrProductImage & "', " & _
intPrice & ", " & _
intActive
' Execute the statement
set rsProduct = dbProduct.Execute(sql)
' Send the user back to
the product manager page and
' pass back the product i.
Response.Redirect "ManageProduct.asp?idProduct=" & _
request("idProduct")
%>
Хранимая процедура sp_UpdateProduct обновляет сведения о товаре по значениям, переданным в качестве параметров (см. листинг 10.53).
Листинг 10.53. Хранимая процедура sp_UpdateProduct
CREATE PROCEDURE sp_UpdateProduct
@idProduct int,
@chrProductName varchar(255),
@txtDescription text,
@chrProductImage varchar(100),
@intPrice int,
@intActive int,
@intFeatured int,
@dtFeatureStart datetime,
@dtFeatureEnd datetime,
@intSalePrice int,
@dtSaleStart datetime,
@dtSaleEnd datetime
AS
update products set
chrProductName = @chrProductName, txtDescription = @txtDescription,
chrProductImage = @chrProductImage,
intPrice = @intPrice,
intActive = @intActive,
intFeatured = @intFeatured,
dtFeatureStart = @dtFeatureStart,
dtFeatureEnd = @dtFeatureEnd,
intSalePrice = @intSalePrice,
dtSaleStart = @dtSaleStart,
dtSaleEnd = @dtSaleEnd
where
idProduct = @idProduct
Страница RemoveProdDept.asp (см. листинг 10.54) исключает товар из раздела. Странице передаются идентификаторы товара и связи "товар-раздел". Полученное значение передается хранимой процедуре sp_DeleteProdDept, после чего пользователь возвращается на страницу редактирования товара.
Листинг 10.54. RemoveProdDept.asp
<%@ Language=VBScript
%>
<%
' ****************************************************
' RemoveProdDept.asp - Removes the product from a
' specific department.
' ****************************************************
' Create an ADO database
connection
set dbProdDept = server.createobject("adodb.connection")
' Create the record set
set rsProdDept = server.CreateObject("adodb.recordset")
' Open the connection using
our ODBC file DSN
dbProdDept.open("filedsn=WildWillieCDs")
' Execute the sp_DeleteProdDept
stored procedure to remove
' the department assignment for the product.
sql = "execute sp_DeleteProdDept " & request("idDepartmentProduct")
' Execute the statement
set rsProdDept = dbProdDept.Execute(sql)
' Send the user back to
the product management page
Response.Redirect "ManageProduct.asp?idProduct=" & _
request("idProduct")
%>
Хранимая процедура sp_DeleteProdDept удаляет из таблицы DepartmentProducts информацию о связи между товаром и разделом (см. листинг 10.55). Идентификатор связи передается процедуре в качестве параметра.
Листинг 10.55. Хранимая процедура sp_DeleteProdDept
CREATE PROCEDURE sp_DeleteProdDept
@idDepartmentProduct int
AS
delete from departmentproducts
where idDepartmentProduct = @idDepartmentProduct
Следующая вспомогательная страница, ProdAddDept.asp (см. листинг 10.56), создает связь между товаром и разделом. Идентификаторы товара и раздела передаются в качестве параметров. После создания связи пользователь возвращается на страницу ManageProduct.asp и продолжает редактирование товара.
Листинг 10.56. ProdAddDept.asp
<%@ Language=VBScript
%>
<%
' ****************************************************
' ProdAddDept.asp - Adds a new product into a
' department.
' ****************************************************
' Create an ADO database
connection
set dbProdDept = server.createobject("adodb.connection")
' Create the record set
set rsProdDept = server.CreateObject("adodb.recordset")
' Open the connection using
our ODBC file DSN
dbProdDept.open("filedsn=WildWillieCDs")
' Execute the sp_AddProdDept
stored procedure to
' tie together the product and the department
sql = "execute sp_AddProdDept " & _
request("idProduct") & ", " & _
request("idDepartment")
' Execute the statement
set rsProdDept = dbProdDept.Execute(sql)
' Send the user back to
the manageproduct.asp page
Response.Redirect "ManageProduct.asp?idProduct=" & _
request("idProduct")
%>
Хранимая процедура sp_AddProdDept (см. листинг 10.57) создает в таблице DepartmentProducts новую запись. Для построения связи используются значения идентификаторов товара и раздела.
Листинг 10.57. Хранимая процедура sp_AddProdDept
CREATE PROCEDURE sp_AddProdDept
@idProduct int,
@idDepartment int
AS
insert into DepartmentProducts(idProduct,
idDepartment)
values(@idProduct, @idDepartment)
Переходим к операциям с атрибутами. Первая страница, Del eteAttri bute.asp (см. листинг 10.58), удаляет связь между товаром и атрибутом. Задача решается при помощи хранимой процедуры sp_Del eteProductAttr i bute. Идентификатор связи "товар-атрибут" передается процедуре в качестве параметра. После удаления атрибута пользователь возвращается на страницу ManageProduct.asp.
Листинг 10.58. DeleteAttribute.asp
<%@ Language=VBScript
%>
<%
' ****************************************************
' DeleteAttribute.asp - Deletes an attribute assigned
' to a product.
' ****************************************************
' Create an ADO database
connection
set dbProductAttribute = server.createobject("adodb.connection")
' Create the record set
set rsProductAttribute = server.CreateObject("adodb.recordset")
' Open the connection using
our ODBC file DSN
dbProductAttribute.open("filedsn=WildWillieCDs")
' The SQL statement removes
the product
' attribute setting for the specified product.
sql = "sp_DeleteProductAttribute " & request("idProductAttribute")
' Execute the statement
set rsProductAttribute = dbProductAttribute.Execute(sql)
' Send the user to the ManageProduct.asp
page
' to allow the user to continue editing the
' product.
Response.Redirect "ManageProduct.asp?idProduct=" & _
request("idProduct")
%>
Идентификатор связи "товар-атрибут" в таблице ProductAttribute передается при вызове хранимой процедуры sp_DeleteProductAttribute (см. листинг 10.59), удаляющей запись из таблицы.
Листинг 10.59. Хранимая процедура sp_DeleteProductAttribute
CREATE PROCEDURE sp_DeleteProductAttribute
@idProductAttribute int
AS
delete from productattribute
where idProductAttribute = @idProductAttribute
Последняя из вспомогательных страниц назначает атрибуты товару (см. листинг 10.60). Связь между товаром и атрибутом создается хранимой процедурой sp_AddProductAttri bute. При вызове этой процедуре передаются идентификаторы товара и атрибута.
Листинг 10.60. AddAttribute.asp
<%@ Language=VBScript
%>
<%
' ****************************************************
' AddAttribute.asp - Adds the attribute setting for
' the specified product.
' ****************************************************
' Create an ADO database
connection
set dbProdAttr = server.createobject("adodb.connection")
' Create the record set
set rsProdAttr = server.CreateObject("adodb.recordset")
' Open the connection using
our ODBC file DSN
dbProdAttr.open("filedsn=WildWillieCDs")
' Execute the sp_AddProductAttribute
stored procedure to
' indicate the attribute is to be assigned to the product.
sql = "execute sp_AddProductAttribute " & _
request("idAttribute") & ", " & _
request("idProduct")
' Execute the statement
set rsProdAttr = dbProdAttr.Execute(sql)
' Send the user back to
the product management page.
Response.Redirect "ManageProduct.asp?idProduct=" & _
request("idProduct")
%>
Хранимая процедура sp_AddProductAttribute создает новую запись в таблице ProductAttribute (см. листинг 10.61).
Листинг 10.61. Хранимая процедура sp_AddProductAttribute
CREATE PROCEDURE sp_AddProductAttribute
@idAttribute int,
@idProduct int
AS
insert into ProductAttribute(idAttribute,
idProduct)
values(@idAttribute, @idProduct)
На этом рассмотрение операции с товарами подходит к концу. Мы рассмотрели вывод списка товаров, поиск, операции создания и редактирования товаров. Остается лишь упомянуть о нескольких дополнительных аспектах.
Во многих магазинах для отслеживания товаров используются специальные коды, которые представляют собой комбинацию внутреннего системного идентификатора товара и его атрибутов.
Например, товар с идентификатором 1234 и атрибутами "желтый" (Yellow) и "большой" (Large) обозначается кодом 1234YL. В нашей системе коды товаров автоматически генерируются базой данных и не могут редактироваться пользователем. Возможно, в своем магазине вы захотите реализовать дополнительные средства для работы с кодами.
Также следует обратить внимание еще на одну проблему. Например, некоторые продавцы назначают дополнительную наценку за размеры XXL, специальные варианты товаров и т.д. В нашем магазине предполагается, что все разновидности одного товара стоят одинаково. Если бы цены менялись в зависимости от атрибутов товара, нам пришлось бы изменить структуру базы данных и сохранить в ней информацию о зависимости цены от атрибута.
Еще одна проблема - графическое изображение товара. Предполагается, что пользователь обладает прямым сетевым доступом к файлам изображений. Если пользователь работает с информацией о товарах через Интернет, нам придется реализовать дополнительные средства для работы с графикой. Например, можно предоставить FTP-доступ к каталогу изображений или реализовать пересылку файлов средствами HTML.
Наша следующая тема - операции с разделами.
Операции с разделами
Создание, обновление и удаление разделов должно происходить по аналогии с соответствующими операциями для товаров. Разумеется, информацию о разделах обрабатывать существенно проще, чем информацию о товарах.
Страница ListDepts.asp, выводящая список разделов, начинается в листинге 10.62. Список разделов создается почти так же, как и список товаров, однако предполагается, что все разделы легко помещаются на одной странице и ссылки для перехода к предыдущей/следующей странице не понадобятся. С учетом небольшого количества разделов механизм поиска нам также не понадобится.
Страница начинается со стандартного включения заголовочных файлов проверки пользователя и построения панели ссылок. Затем к базе данных открывается подключение ADO, через которое страница получает текущий список разделов.
Листинг 10.62. ListDepts.asp
<%@ Language=VBScript
%>
<!-- #Include file="include/validatecheck.asp" -->
<HTML>
<!--
ListDepts.asp - Lists the departments in the
store.
-->
<HEAD>
<META NAME="GENERATOR" Content="Microsoft Visual Studio 6.0">
</HEAD>
<BODY>
<!-- #include file="include/navinclude.asp" -->
<%
' Create an ADO database
connection
set dbDepts = server.createobject("adodb.connection")
' Create the record set
set rsDepts = server.CreateObject("adodb.recordset")
' Open the connection using
our ODBC file DSN
dbDepts.open("filedsn=WildWillieCDs")
' Call the sp_RetrieveDepts
stored procedure to
' return the departments in the database.
sql = "execute sp_RetrieveDepts"
' Execute the statement
set rsDepts = dbDepts.Execute(sql)
%>
Первым визуальным элементом страницы является ссылка для создания нового раздела (см. листинг 10.63). За ней начинается таблица, в которой выводится список разделов. В каждой строке таблицы отображаются идентификатор и название раздела.
Листинг 10.63. ListDepts.asp (продолжение)
<!-- Build a link the
new department page
if the user wants to add a department. -->
<BR><b>Click <a href="NewDept.asp">here</a>
to add
a new department.</b>
<!-- Start out the structure
to show the list
of departments.
-->
<BR><BR>
<b>To edit a department, select from the list below:</b>
<BR><BR>
<table cellpadding="3"
cellspacing="3">
<tr>
<th>Department ID</th>
<th>Name</th>
</tr>
Построение строк таблицы происходит в цикле (см. листинг 10.64). Для каждого раздела создается ссылка на страницу ManageDept.asp, содержащая идентификатор раздела и его название.
Листинг 10.64. ListDepts.asp (продолжение)
<%
' Loop through the list
of departments.
do until rsDepts.EOF
%>
<!-- Create a link to
the ManageDept.asp to work
with the department. -->
<tr>
<td>
<!-- The link to the department needs to include the
id of the department. Following that the id
of the deparment is displayed. -->
<a href="ManageDept.asp?idDepartment=<%=rsDepts("idDepartment")%>">
<%=rsDepts("idDepartment")%></a></td>
<!-- The link to the department needs to include the
id of the department. Following that the name
of the deparment is displayed. -->
<td>
<a href="ManageDept.asp?idDepartment=<%=rsDepts("idDepartment")%>">
<%=rsDepts("chrDeptName")%></a></td>
</tr>
<%
' Move to the next row
rsDepts.MoveNext
' Loop back
Loop
%>
</table>
</BODY>
</HTML>
В работе страницы используется хранимая процедура sp_RetrieveDepts (см. листинг 10.65). Эта процедура просто загружает данные всех разделов при помощи команды SQL SELECT.
Листинг 10.65. Хранимая процедура sp_RetrieveDepts
/* Stored procedure to retrieve
all of
the departments in the database */
CREATE PROCEDURE sp_RetrieveDepts AS
/* Выборка всех записей
разделов */
select * from department
После создания списка можно переходить к операциям создания или редактирования разделов. Начнем с создания новых разделов. Начало страницы NewDept.asp приведено в листинге 10.66.
Эта страница просто строит таблицу с полями для ввода ключевых данных раздела. Она начинается с включения стандартных заголовочных файлов.
Листинг 10.66. NewDept.asp
<%@ Language=VBScript
%>
<!-- #Include file="include/validatecheck.asp" -->
<HTML>
<!--
NewDept.asp - Handles adding in a new department
into the store.
-->
<HEAD>
<META NAME="GENERATOR" Content="Microsoft Visual Studio 6.0">
</HEAD>
<BODY>
<!-- #Include file="include/navinclude.asp" -->
На странице создается форма, которая передает данные нового раздела странице AddNewDepartment.asp. В форме присутствуют поля для ввода названия раздела, описания и имени файла с графическим изображением (см. листинг 10.67).
Листинг 10.67. NewDept.asp (продолжение)
<!-- Форма для ввода
сведений о новом разделе -->
<form method="post" action="AddNewDepartment.asp">
<!-- Таблица для вывода
полей формы -->
<table cellpadding="3" cellspacing="3">
<!-- Название раздела
-->
<tr>
<td>Department Name</td>
<td><input type="text" value="" name="chrDeptName"</td>
</tr>
<!-- Описание раздела
-->
<tr>
<td>Department Description</td>
<td><textarea name="txtDeptDesc" cols="40" rows="5"></textarea></td>
</tr>
<!-- Имя файла с графическим
изображением -->
<tr>
<td>Department Image</td>
<td><input type="text" value="" name="chrDeptImage"></td>
</tr>
Страница завершается кнопкой Submit, которая отправляет данные формы, и закрывающими тегами формы, таблицы и страницы (см. листинг 10.68).
Листинг 10.68. NewDept.asp (продолжение)
<!-- Кнопка отправки
данных формы. -->
<tr>
<td colspan="2">
<input type="Submit" value="Add Department"
name="Submit"></td>
</tr>
<!-- Close out the page
-->
</table>
</form>
</BODY>
</HTML>
В листинге 10.69 приведен код страницы AddNewDepartment.asp, которой передает данные страница NewDept.asp. Страница получает значения, введенные на странице создания нового раздела, и сохраняет их в базе данных.
Страница начинается с операций получения введенных данных - имени раздела, описания и файла графического изображения.
Листинг 10.69. AddNewDepartment.asp
<%@ Language=VBScript
%>
<%
' ****************************************************
' AddNewDepartment.asp - Handles adding a new
' department to the store.
' ****************************************************
' Retrieve the department
name and ensure any single
' quotes are doubled.
chrDeptName = replace(request("chrDeptName"), "'", "''")
' Retrieve the department
description and ensure any
' single quotes are doubled.
txtDeptDesc = replace(request("txtDeptDesc"), "'", "''")
' Retrieve the department
image file name.
chrDeptImage = request("chrDeptImage")
Затем страница создает подключение к базе данных (см. листинг 10.70). Хранимая процедура sp_InsertDept создает запись нового раздела в базе. После создания раздела пользователь возвращается на страницу ManageDept.asp, а новый раздел появляется в общем списке.
Листинг 10