首页 » PHP和MySQL Web开发(原书第4版) » PHP和MySQL Web开发(原书第4版)全文在线阅读

《PHP和MySQL Web开发(原书第4版)》33.3 解决方案概述

关灯直达底部

对于这个项目,我们还将使用事件驱动的方法来编写代码,正如第29章和第30章所介绍的。在这个例子中,我们不会绘制系统流程图,因为系统中只出现非常少的几个界面,而且这些页面之间的链接是非常简单的。

如图33-1所示的是用户访问Tahuayo时看到的主页面。

图 33-1 Tahuayo的第一个页面显示了该站点的所有主要特性:目录浏览、搜索和购物车

可以看到,该站点的主要特性就是选中目录的显示以及显示这些目录中的物品。在默认情况下,第一页上显示当前销售情况最好的产品目录。如果一个用户点击其他目录,将看到该目录下相似的显示。

在我们进一步开始项目之前,需要掌握一些简单的术语:Amazon将目录当作是浏览节点。在贯穿整个代码和正式的文档中,我们都将发现这种表达方式。

文档提供了所有流行的浏览节点列表。此外,如果希望看到特定的节点,可以浏览Amazon站点并且从URL中读入它们,你可以在http://www.browsernodes.com/获得Browse Nodes资源。

奇怪的是,某些重要的目录,例如销售最佳的图书,无法通过浏览节点进行访问。

在这个页面的下方,还有许多图书和链接,但是无法从上图中看到(上图只是屏幕的截图)。我们在每一页上显示10本图书,同时显示链接到30多本相关图书。这种每页显示10本图书的设置是由Amazon确定的。而每页显示30本图书的设置是我们自己的选择。

从这个页面,用户可以点击并查看每一本图书的详细信息。这个页面如图33-2所示。

图 33-2 详细信息页面显示了关于特定图书的详细信息,同时还包括类似产品和概述

虽然无法在一个截图中显示整个页面,但是我们已经尽量显示了更多的内容,但是并不是全部的,因为在这个页面上显示的信息是一个heavy查询得来的。我们选择过滤了图书以外的产品,以及不适合图书目录的其他产品列表。

如果点击图书的封面,可以看到一个扩大后的图书封面。

我们可能已经注意到了位于上图右上方的搜索文本框。这个搜索操作可以对站点的关键字进行搜索,同样,它也可以通过Web服务接口搜索Amazon的目录。一个搜索操作的输出结果示例如图33-3所示。

图 33-3 搜索batman的结果输出

虽然我们只是列出了少量的目录,但是客户可以通过这个搜索工具找到任何想要的图书,并且浏览特定的图书。

每一本图书都有一个“添加到购物车(Add to Cart)”的链接。点击购物车汇总中的这个链接或“详细信息(Details)”链接,将显示购物车中的详细内容,如图33-4所示。

图 33-4 在购物车页面,可以删除物品,清空购物车或付账

最后,当用户点击其中任何一个“付账(Checkout)”链接,都会将购物车中的详细信息发送给Amazon并且转到Amazon站点。这样,客户就将看到如图33-5所示的页面。

图 33-5 在将客户选购物品保存在Amazon购物车之前,系统将确认该交易,并显示Tahuayo购物车中的所有物品

通过构建我们自己的前台应用程序和使用Amazon作为后台,可以理解本项目的含义。

由于这个项目仍然使用了事件驱动的方法,所以这个应用程序中的核心程序逻辑都是在一个文件中实现的——index.php。在该应用程序中,所用到的文件概述如表33-1所示。

我们还需要前面所介绍的nusoap.php文件,因为在以上文件中,这个文件是必需的。NuSOAP文件可以在附带的文件中找到,具体目录位于chapter33中,但是也可以从http://dietrich.ganx4.com/nusoap/index.php中找到该文件的最新版本(如果发布了新版本)。

下面,我们开始了解核心应用程序index.php文件。

33.3.1 核心应用程序

程序清单33-3所示的就是核心应用程序index.php文件。

程序清单33-3 index.php——核心应用程序文件

<?php

//we are only using one session variable/'cart/'to store the cart contents

session_start;

require_once(/'constants.php/');

require_once(/'Product.php/');

require_once(/'AmazonResultSet.php/');

require_once(/'utilityfunctions.php/');

require_once(/'bookdisplayfunctions.php/');

require_once(/'cartfunctions.php/');

require_once(/'categoryfunctions.php/');

//These are the variables we are expecting from outside.

//They will be validated and converted to globals

$external=array(/'action/',/'ASIN/',/'mode/',/'browseNode/',/'page/',/'search/');

//the variables may come via Get or Post

//convert all our expected external variables to short global names

foreach($external as$e){

if(@$_REQUEST[$e]){

$$e=$_REQUEST[$e];

}else{

$$e=/'/';

}

$$e=trim($$e);

}

//default values for global variables

if($mode==/'/'){

$mode=/'Books/';//No other modes have been tested

}

if($browseNode==/'/'){

$browseNode=53;//53 is bestselling non-fiction books

}

if($page==/'/'){

$page=1;//First Page-there are 10 items per page

}

//validate/strip input

if(!eregi(/'^[A-Z0-9]+$/',$ASIN)){

//ASINS must be alpha-numeric

$ASIN=/'/';

}

if(!eregi(/'^[a-z]+$/',$mode)){

//mode must be alphabetic

$mode=/'Books/';

}

$page=intval($page);//pages and browseNodes must be integers

$browseNode=intval($browseNode);

//it may cause some confusion,but we are stripping characters out from

//$search it seems only fair to modify it now so it will be displayed

//in the heading

$search=safeString($search);

if(!isset($_SESSION[/'cart/'])){

session_register(/'cart/');

$_SESSION[/'cart/']=array;

}

//tasks that need to be done before the top bar is shown

if($action==/'addtocart/'){

addToCart($_SESSION[/'cart/'],$ASIN,$mode);

}

if($action==/'deletefromcart/'){

deleteFromCart($_SESSION[/'cart/'],$ASIN);

}

if($action==/'emptycart/'){

$_SESSION[/'cart/']=array;

}

//show top bar

require_once(/'topbar.php/');

//main event loop.Reacts to user action on the calling page

switch($action){

case/'detail/':

showCategories($mode);

showDetail($ASIN,$mode);

break;

case/'addtocart/':

case/'deletefromcart/':

case/'emptycart/':

case/'showcart/':

echo/"<hr/><h1>Your Shopping Cart</h1>/";

showCart($_SESSION[/'cart/'],$mode);

break;

case/'image/':

showCategories($mode);

echo/"<h1>Large Product Image</h1>/";

showImage($ASIN,$mode);

break;

case/'search/':

showCategories($mode);

echo/"<h1>Search Results For/".$search./"</h1>/";

showSearch($search,$page,$mode);

break;

case/'browsenode/':

default:

showCategories($mode);

$category=getCategoryName($browseNode);

if(!$category||($category==/'Best Selling Books/')){

echo/"<h1>Current Best Sellers</h1>/";

}else{

echo/"<h1>Current Best Sellers in/".$category./"</h1>/";

}

showBrowseNode($browseNode,$page,$mode);

break;

}

require(/'bottom.php/');

下面,我们用自己的方法来了解这个文件。首先,我们创建了一个会话。就像前面所介绍的,将客户的购物车保存为一个会话变量。

接着,我们包括并引入了几个文件。这些文件都是下面将要介绍的函数,但是在介绍它们之前,我们必须先介绍第一个被包括进来的文件。constants.php文件定义了一些重要的常量,这些常量将在整个应用程序中使用。程序清单33-4给出了constants.php的所有代码。

程序清单33-4 constants.php——声明重要的全局常量和变量

<?php

//this application can connect via REST(XML over HTTP)or SOAP

//define one version of METHOD to choose.

//define(/'METHOD/',/'SOAP/');

define(/'METHOD/',/'REST/');

//make sure to create a cache directory an make it writable

define(/'CACHE/',/'cache/');//path to cached files

define(/'ASSOCIATEID/',/'XXXXXXXXXXXXXX/');//put your associate id here

define(/'DEVTAG/',/'XXXXXXXXXXXXXX/');//put your developer tag here

//give an error if software is run with the dummy devtag

if(DEVTAG==/'XXXXXXXXXXXXXX/'){

die(/"You need to sign up for an Amazon.com developer tag at

<a href=/"https://aws.amazon.com//">Amazon</a>

when you install this software.You should probably sign up

for an associate ID at the same time.Edit the file constants.php./");

}

//(partial)list of Amazon browseNodes.

$categoryList=array(5=>/'Computers&Internet/',3510=>/'Web Development/',

295223=>/'PHP/',17=>/'Literature and Fiction/',

3=>/'Business&Investing/',53=>/'Non Fiction/',

23=>/'Romance/',75=>/'Science/',21=>/'Reference/',

6=>/'Food&Wine/',27=>/'Travel/',

16272=>/'Science Fiction/'

);

这个应用程序可以使用REST或SOAP进行开发。修改METHOD常量值,可以选择所使用的开发方法。

CACHE常量定义了保存我们从Amazon站点上下载的数据的路径。可以修改该常量值,使其指向系统上任何希望的地方。

ASSOCIATEID常量定义了会员ID。如果是在事务中发送会员ID,将得到现金奖励。修改该常量,使其保存会员ID。

DEVTAG常量定义了Amazon在注册时为我们分配的开发人员令牌。必须将其修改为我们自己的开发人员令牌,否则该应用程序将无法正常运行。可以在如下URL注册一个开发人员令牌:http://aws.amazon.com。

现在,让我们回头看看index.php文件。它包含一些初始设置,以及主要的事件循环。首先,我们将从通过GET或POST方法传递进来的$_REQUEST超级全局变量中获取所需的变量。接着,将为这些标准的全局变量设置默认值,这些全局变量可以确定以后的页面显示,如下所示:

//default values for global variables

if($mode==/'/'){

$mode=/'Books/';//No other modes have been tested

}

if($browseNode==/'/'){

$browseNode=53;//53 is bestselling non-fiction books

}

if($page==/'/'){

$page=1;//First Page-there are 10 items per page

在以上代码中,我们将mode变量设置为/"books/"。Amazon支持许多模式(产品的类型),但是对于这个应用程序,我们只考虑图书。修改本章代码使其适用于其他产品目录不会太困难,只要重新设置$mode变量就可以了。此外,可能还需要查看Amazon文档,确认对于非图书类产品是否还具有其他属性,同时从用户界面上去除只与图书相关的文字。

browseNode变量用来指定要显示的图书种类。如果用户点击了/"Selected Categories/"链接,就可以自动设置图书种类。如果还没有设置(例如,当用户第一次来到该站点)我们仍然需要对其进行设置,设置为53。Amazon的浏览节点都是整数,用整数来标识一个种类。53表示非科幻图书类别,该类别也是一个非常不错的节点,就像那些出现在初始页面的类别,虽然有些最好的常见目录(例如,最佳销售)无法通过浏览节点进行访问。

page变量用来告诉Amazon我们希望在给定的种类中要显示的结果子集。page 1包含了1~10的结果,page 2包含了11~20的结果等。Amazon可以设置一个页面上的结果数,我们不用对其进行控制。当然,也可以在一页上显示Amazon站点上两页或更多页上的数据,但是对Amazon和我们的站点来说,10是都可以接受的,而且这样可以使我们的站点与Amazon保持一致。

接下来,我们将整理接收到的输入数据,可以通过搜索文本框或者是GET和POST参数:

//validate/strip input

if(!eregi(/'^[A-Z0-9]+$/',$ASIN)){

//ASINS must be alpha-numeric

$ASIN=/'/';

}

if(!eregi(/'^[a-z]+$/',$mode)){

//mode must be alphabetic

$mode=/'Books/';

}

$page=intval($page);//pages and browseNodes must be integers

$browseNode=intval($browseNode);

//it may cause some confusion,but we are stripping characters out from

//$search it seems only fair to modify it now so it will be displayed

//in the heading

以上代码没有什么新内容。safeString函数源自utilityfunctions.php函数库。它只是通过一个正则表达式的替换操作,从输入字符串中删除任何非字母字符。由于我们在前面已经介绍它,这里不再进行描述。

在应用程序中,我们对用户输入数据进行校验的主要原因在于,我们将使用客户的输入在缓存中创建文件名称。如果允许客户在输入中使用“..”或“/”,可能会遇到非常严重的问题。

接下来,如果客户还没有购物车的话,将为其设置一个购物车:

if(!isset($_SESSION[/'cart/'])){

session_register(/'cart/');

$_SESSION[/'cart/']=array;

在页面最上方的信息栏中显示信息之前(参阅图33-1),还需要完成一些操作。购物车将出现在每一个页面最上方的信息栏中。因此,在显示购物车内容之前,保持购物车变量为最新的是非常重要的:

//tasks that need to be done before the top bar is shown

if($action==/'addtocart/'){

addToCart($_SESSION[/'cart/'],$ASIN,$mode);

}

if($action==/'deletefromcart/'){

deleteFromCart($_SESSION[/'cart/'],$ASIN);

}

if($action==/'emptycart/'){

$_SESSION[/'cart/']=array;

在这里,我们将在显示购物车之前添加或删除一些物品。讨论购物车和付账时,我们还将介绍这些函数。如果想现在就了解这些函数,可以在cartfunctions.php文件中找到它们。现在,我们先将它们放到一边,因为首先必须理解Amazon的接口。

下一步,我们将包含并引入topbar.php文件。这个文件只包含了HTML和样式单,以及一个对ShowSmallCart函数的调用(源自cartfunctions.php)。这个函数将显示购物车的总结信息,我们将在每个页面右上方看到它们。介绍购物车函数时,我们还将介绍这些函数。

最后,我们介绍事件处理的主循环。表33-2给出了可能出现的事件总结。

可以看到,表33-2中的前4个事件都与获取和显示信息相关。而后4个事件都与管理购物车相关。

从Amazon获取数据的事件具有相似的工作方式。我们将以browsenode(种类)事件为例介绍如何获取关于图书的数据。