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

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

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


Операция

Описание

Административный вход

Проверка данных пользователя при входе в управляющую программу

Создание нового товара

Добавить новый товар в базу данных магазина

Список товаров

Вывести список товаров в базе данных

Поиск товаров

Найти товары в базе данных

Удаление товаров

Удалить товар из базы данных

Обновление товаров

Обновляет данные товара

Операции с атрибутами товара

Создание, обновление и удаление атрибутов товара

Список разделов

Вывести список разделов в базе данных

Обновление разделов

Обновить данные раздела

Создание нового раздела

Создать новый раздел в базе данных

Обновление раздела

Обновляет данные раздела в базе данных


Одним из ключевых элементов Web-сайта является простая система перехода между различными управляющими операциями.
Для построения одинаковой панели ссылок на всех страницах сайта будет использован тот же способ, как и в интерфейсе покупателя. Содержимое заголовочного файла 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. Такое решение позволяет легко обновить навигационный интерфейс в случае расширения функциональных возможностей программы.

Безопасность
В программе управления электронным магазином необходимо реализовать систему безопасности. Нельзя допустить, чтобы доступ к управлению магазином был открыт для посторонних. Первое, что для этого понадобится, - страница для регистрации пользователей.
Страница регистрации 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>
Вторая часть страницы представляет собой обычное меню для вызова различных функций управляющей программы. В дальнейшем ссылки этого меню выводятся на панели, расположенной в верхней части страницы.
Последний заголовочный файл 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
На этом завершается построение основных средств работы со списком товаров. Эта страница является отправной точкой для операций с отдельными товарами.
Щелкните на ссылке 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
Заполните форму и нажмите кнопку 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"> &nbsp;&nbsp;&nbsp;&nbsp;
<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>&nbsp;</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. Повторите операцию и свяжите товар со вторым разделом. На этот раз в списке следует выбрать строку 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.70. AddNewDepartment.asp
' Create an ADO database connection
set dbDept = server.createobject("adodb.connection")
' Create the record set
set rsDept = server.CreateObject("adodb.recordset")
' Open the connection using our ODBC file DSN
dbDept.open("filedsn=WildWillieCDs")
' Execute the sp_InsertDept stored procedure to add
' the new department into the database.
sql = "execute sp_InsertDept '" & _
chrDeptName & "', '" & _
txtDeptDesc & "', '" & _
chrDeptImage & "'"
' Execute the statement
set rsDept = dbDept.Execute(sql)
' Redirect the user to the ManageDept.asp page to do some
' any editing on the product. Note that the ID of the
' new department is returned from the stored procedure.
Response.Redirect "ManageDept.asp?idDepartment=" & _
rsDept("idDepartment")
%>
В работе страницы используется хранимая процедура sp_InsertDept (см. листинг 10.71), которой в качестве параметров передаются три значения. Процедура создает команду SQL INSERT для сохранения данных в таблице и возвращает идентификатор нового раздела.
Листинг 10.71. Хранимая процедура sp_InsertDept
CREATE PROCEDURE sp_InsertDept
@chrDeptName varchar(255),
@txtDeptDesc text,
@chrDeptImage varchar(100)
AS
insert into department(chrDeptName, txtDeptDesc, chrDeptImage)
values(@chrDeptName, @txtDeptDesc, @chrDeptImage)
select idDepartment = @@identity
Теперь мы переходим к выполнению операций с разделами. Сначала построим страницу, предназначенную для редактирования и удаления существующих данных разделов.
Страница ManageDept.asp (см. листинг 10.72) начинается с включения стандартных заголовочных файлов, обеспечивающих проверку пользователя и создание панели ссылок.
Листинг 10.72. ManageDept.asp
<%@ Language=VBScript %>
<!-- #Include file="include/validatecheck.asp" -->
<HTML>
<!--
ManageDept.asp - Handles the management of the the
department and allows the shopper to update the
data.
-->
<HEAD>
<META NAME="GENERATOR" Content="Microsoft Visual Studio 6.0">
</HEAD>
<BODY>
<!-- #Include file="include/navinclude.asp" -->
Страница открывает подключение ADO и загружает данные раздела при помощи хранимой процедуры sp_RetrieveDept (см. листинг 10.73), которой в качестве параметра передается идентификатор раздела.
Листинг 10.73. ManageDept.asp (продолжение)
<%
' Create an ADO database connection
set dbDept = server.createobject("adodb.connection")
' Create the record set
set rsDept = server.CreateObject("adodb.recordset")
' Open the connection using our ODBC file DSN
dbDept.open("filedsn=WildWillieCDs")
' The retrieve sp_RetrieveDept stored procedure to
' retrieve the data on the specified departments.
sql = "execute sp_RetrieveDept " & request("idDepartment")

' Execute the statement
set rsDept = dbDept.Execute(sql)
%>
В листинге 10.74 создается форма, передающая новые данные раздела странице UpdateDept.asp для их обновления в базе данных.
Как и при выполнении операций с товарами, мы хотим иметь возможность предварительного просмотра редактируемого раздела. С этой целью создается ссылка на страницу Products.asp, содержащую список разделов.
После ссылки предварительного просмотра создается ссылка для удаления раздела из базы данных. Идентификатор удаляемого раздела передается в URL.
Листинг 10.74. ManageDept.asp (продолжение)
<!-- The form is created to post the changes -->
<form method="post" action="UpdateDept.asp">
<!-- Start the table to display the product data -->
<table cellpadding="3" cellspacing="3">
<tr>
<td>Preview Department:</td>
<td>
<!-- Build a link to the products.asp page
in the live store. The id of the department
is passed into the page. This will provide a
quick preview of the department. -->
<a href="/ecstore/wildwilliecds/products.asp?idDept=<%=request("idDepartment")%>">Preview</a>
</td>
</tr>
<tr>
<td>Delete Department:</td>
<!-- A link is built to the DeleteDept.asp page. The id
of the department is sent into the page. -->
<td><a href="DeleteDept.asp?idDepartment=<%=request("idDepartment")%>">
Delete</a></td>
</tr>
<!-- Build a buffer between this setion and the next -->
<tr>
<td colspan="2"><hr></td>
</tr>
После этого страница готова к выводу текущей информации раздела. Сначала выводится идентификатор раздела. Вместе с текстовыми полями мы создаем скрытое поле, содержимое которого передается вместе с формой. При помощи идентификатора раздела, сохраняемого в этом поле, страница UpdateDept.asp легко определяет, какой раздел следует обновить.
Листинг 10.75. ManageDept.asp (продолжение)
<tr>
<td>Department ID</td>
<!-- Идентификатор раздела только выводится, редактировать его нельзя. -->
<td><%=rsDept("idDepartment")%>
<input type="hidden"
value="<%=rsDept("idDepartment")%>" name="idDepartment">
</td>
</tr>
<!-- Текстовое поле для редактирования названия раздела. -->
<tr>
<td>Department Name</td>
<td><input type="text"
value="<%=rsDept("chrDeptName")%>" name="chrDeptName"</td>
</tr>
<!-- Текстовое поле для редактирования описания раздела. -->
<tr>
<td>Department Description</td>
<td><textarea name="txtDeptDesc" cols="40" rows="5"><%=rsDept("txtDeptDesc")%></textarea></td>
</tr>
Наряду с графическим изображением раздела выводится имя графического файла, в котором оно хранится (см. листинг 10.76) Как и при работе с изображениями товаров, предполагается, что файл каким-либо способом загружается в систему.
Листинг 10.76. ManageDept.asp (продолжение)
<!-- Текстовое поле для редактирования имени файла, содержащего изображение раздела. Изображение выводится рядом с именем файла. -->
<tr>
<td>Department Image</td>
<td><input type="text" value="<%=rsDept("chrDeptImage")%>"
name="chrDeptImage">&nbsp;&nbsp;&nbsp;&nbsp;
<img src="../wildwilliecds/images/<%=rsDept("chrDeptImage")%>"
align="center"></td>
</tr>
<!-- Кнопка отправки данных. -->
<tr>
<td colspan="2"><input type="Submit"
value="Update Department" name="Submit"></td>
</tr>
</table>
</form>
</BODY>
</HTML>
В работе страницы используется хранимая процедура sp_RetrieveDept. Она получает идентификатор раздела и загружает информацию о нем при помощи команды SELECT (см. листинг 10.77).
Листинг 10.77. Хранимая процедура sp_RetrieveDept
/* Загрузка информации об одном разделе */
CREATE PROCEDURE sp_RetrieveDept
/* При вызове процедуре передается идентификатор раздела */
@idDepartment int
AS
/* Выборка данных раздела с заданным идентификатором */
select * from department
where idDepartment = @idDepartment
Перейдем к рассмотрению функций обновления и удаления, присутствующих на странице ManageDept.asp. Начнем со страницы UpdateDept.asp, обновляющей раздел.
Сначала страница читает с полученной формы данные - идентификатор раздела, название, описание и имя графического файла (см. листинг 10.78).
Листинг 10.78. UpdateDept.asp
<%@ Language=VBScript %>
<%
' ****************************************************
' UpdateDept.asp - Handles updating the department
' department data.
' ****************************************************
' Retrieve the id of the department
idDepartment = request("idDepartment")
' Retrieve the department name and ensure that any
' single quotes are doubled.
chrDeptName = replace(request("chrDeptName"), "'", "''")
' Retrieve the department description and ensrue that
' any single quotes are doubled.
txtDeptDesc = replace(request("txtDeptDesc"), "'", "''")
' Retrieve the department image
chrDeptImage = request("chrDeptImage")
После чтения необходимых значений страница переходит к обновлению информации (см. листинг 10.79). Она открывает подключение ADO к базе данных и при помощи хранимой процедуры sp_UpdateDepartment выполняет запрос на обновление. Значения, прочитанные на форме, передаются в качестве параметров запроса. После обработки запроса пользователь возвращается на страницу редактирования раздела.
Листинг 10.78. UpdateDept.asp
' Create an ADO database connection
set dbDept = server.createobject("adodb.connection")
' Create the record set
set rsDept = server.CreateObject("adodb.recordset")
' Open the connection using our ODBC file DSN
dbDept.open("filedsn=WildWillieCDs")
' Execute the sp_UpdateDepartment stored procedure to
' update the department data.
sql = "execute sp_UpdateDepartment " & _
request("idDepartment") & ", '" & _
chrDeptName & "', '" & _
txtDeptDesc & "', '" & _
chrDeptImage & "'"

' Execute the statement
set rsDept = dbDept.Execute(sql)
' Send the user back to department manager and pass back
' the department id.
Response.Redirect "ManageDept.asp?idDepartment=" & _
request("idDepartment")
%>
Код хранимой процедуры sp_UpdateDepartment приведен в листинге 10.80. Процедура получает новые данные раздела, переданные в качестве параметров, и обновляет информацию в базе данных командой SQL UPDATE.
Листинг 10.80. Хранимая процедура sp_UpdateDepartment
CREATE PROCEDURE sp_UpdateDepartment
@idDepartment int,
@chrDeptName varchar(255),
@txtDeptDesc text,
@chrDeptImage varchar(100)
AS
update department set
chrDeptName = @chrDeptName,
txtDeptDesc = @txtDeptDesc,
chrDeptImage = @chrDeptImage
where idDepartment = @idDepartment
Переходим к следующей операции - к удалению разделов. Страница DeleteDept.asp приведена в листинге 10.81.
Идентификатор раздела передается в URL. Страница создает подключение ADO к базе данных и выполняет хранимую процедуру sp_DeleteDept. После удаления пользователь возвращается к списку разделов.
Листинг 10.81. DeleteDept.asp
<%@ Language=VBScript %>
<%
' ****************************************************
' DeleteDept.asp - Deletes the department from the
' store.
' ****************************************************
' Create an ADO database connection
set dbDept = server.createobject("adodb.connection")
' Create the record set
set rsDept = server.CreateObject("adodb.recordset")
' Open the connection using our ODBC file DSN
dbDept.open("filedsn=WildWillieCDs")
' The sp_DeleteDept stored procedure removes the
' department from the database.
sql = "execute sp_DeleteDept " & request("idDepartment")

' Execute the statement
set rsDept = dbDept.Execute(sql)
' Send the user back to the list of departments.
Response.Redirect "ListDepts.asp"
%>
В листинге 10.82 приведен код хранимой процедуры sp_DeleteDept. Процедура получает идентификатор раздела, переданный в качестве параметра, и удаляет данные из базы командой SQL DELETE.
Листинг 10.82. Хранимая процедура sp_DeleteDept
CREATE PROCEDURE sp_DeleteDept
@idDepartment int
AS
delete from department where idDepartment = @idDepartment
Следующий пример демонстрирует использование новых управляющих функций.  Обратите внимание на ссылку, создающую новый раздел.

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

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

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

 

 
На главную | Содержание | < Назад....Вперёд >
С вопросами и предложениями можно обращаться по nicivas@bk.ru. 2013 г. Яндекс.Метрика