轻松实现你自己的在线商店

分享于 

39分钟阅读

Web开发

  繁體

介绍

本文介绍了如何使用PHP中的开发框架创建在线商店系统。 所有代码都托管在GitHub存储库中,因此你可以轻松地在这里浏览它们。 在这个基础文章中,你应该阅读 ,首先要获得Web开发框架的基础。

观众

具有纯 PHP。JavaScript和SQL知识的有经验的软件开发人员。

任务

我们需要一个商店,不复杂"

我们经常从客户那里听到,而且我们非常确信你也知道。 幸好我们可以告诉你如何在没有时间的情况下使用 Scavix WebFramework建立一个基本的商店系统。 这个示例向你展示了基础,以便你可以为你需要的商店添加所有额外的。 这使你可以自由从一个非常基本的小型商店代码,然后用所有addional逻辑为你的目的。

在线商店的需求几乎是相同的:

  • 产品清单
  • 产品详细信息页面
  • 每个产品都有标题。标语。说明。价格和图片。
  • 购物篮
  • 简单 的 行政 工作
  • 不同的付款提供商

所有这些都可以快速轻松地完成。 当然还有很多,但这是几乎所有商店系统的核心。 如果实现了这些基础,所有的适应性都很容易。

伟大的商店系统( 那里有很多非常好的商店系统) 可以做到所有这些,但需要为每个客户调整。 这些调整可能会耗费很多时间,而实现小型轻型专业商店将在同一或者 LESS 时间实现。 这里外,full-fledged-online商店系统还有一些不需要的功能,尤它的是当你有一个只有几个产品的商店。

我们不会讨论它的他商店系统的优缺点,但是只是从。"。"。

数据库方案

这很常见所以我们会让一个 告诉我们:

可以看到,我们将开始没有用户注册,但只允许客户输入他们的地址细节。 我们还将跳过管理表(。对于管理员用户和物料),只依赖于硬编码的管理部分。

当然,对于现场商店来说,这是不可以能的,但如前所述,我们想要关注商店的基础知识。

位于 GitHub的样例代码包含一个函数,它确保数据库结构存在,并在 SQLite数据库中填充一些示例数据。 要了解的是,上面 映像只是显示了数据库的基本思想,而不是真实的结构,所以请不要责怪我们在代码中找到的字段,而不是在图像中的字段。

基本设置

使用 Scavix Web开发框架插件时一样,你需要三个文件: ,,以及一个( 实际上是可选的) 文件。

  • index.php 包含数据库设置代码,但是你还没有知道。
  • config.php 建立了 Scavix Web开发框架和
  • 。htaccess 需要很好的url。

这里对所有这些细节进行了详细的描述: 超快速的PHP应用程序开发。

结构

我们需要三个控制器: 产品,篮子和管理。

当然,如果你分割成其他逻辑单位,你也可以这样做 !

无论如何,会有一个产品清单,产品细节页面,购物篮页面和管理。 从篮子页面顾客可以开始付款过程,需要更多的页面来收集客户数据。

布局

我们不会花很多时间为我们的样例商店创建一个漂亮的布局。 只有控制器的基类和几行CSS代码现在就足够了:

//controller/shopbase.class.php <?phpuse ScavixWDFBaseHtmlPage;class ShopBase extends HtmlPage { /* no code needed here */ }?>
// controller/shopbase.tpl.php<divid="page"><divid="navigation"><ahref="<?=buildQuery('Products')?>">Products</a><ahref="<?=buildQuery('Basket')?>">Basket</a><ahref="<?=buildQuery('Admin')?>">Administration (normally hidden)</a></div><divid="content"><? foreach( $contentas$c ) echo$c; ?> 
 </div></div>
// res/shopbase.css#page { font: 14px normal Verdana,Arial,serif; }
#page>div { width: 960px;margin: auto; }
#navigation { padding-bottom: 10px;border-bottom: 1px solid gray; }
#navigationa{
 font-size: 18px; 
 font-weight: bold; 
 margin-right: 15px;}
.product_overview{
 clear: both;margin-top: 25px;border-bottom: 1px solid gray;height: 75px;}
.product_overviewimg{
 width: 50px;float: left;margin-right: 10px;}
.product_overviewdiv{
 white-space: nowrap;overflow: hidden;}
.product_overview. title { font-weight: bold; }
.product_overviewa { float: right; }
.product_basket{
 clear: both;}
.product_basketimg{
 width: 30px;float: left;margin-right: 10px;}
.basket_total{
 clear: both;text-align: right;font-size: 14px; 
 font-weight: bold;}

因这里现在 ShopBase 只是一个中心基类,所以所有派生类都将对同一个布局进行 inherit。 当然,使用继承共享通用CSS和程序逻辑总是一个好主意,因这里这是不可以能的。

查看脏硬编码导航链接? 这将是以后的另一项任务。

可以只显示已经验证的用户的链接,或者可以能在空时隐藏了篮链接。 但是就像前面提到过的:我们不希望失去焦点。

产品

我们将在这里实现两个页面: 产品清单和产品详细信息页面。

// controller/products.class.php<?phpuse ScavixWDFBaseTemplate;
use ScavixWDFJQueryUIuiMessage;class Products extends ShopBase
{
 /**
 * Lists all products.
 * @attribute[RequestParam('error','string',false)]
 */ function Index($error)
 {
 // display error message if given if( $error )
 $this->content(uiMessage::Error($error));
 // loop thru the products...$ds = model_datasource('system');
 foreach( $ds->Query('products')->orderBy('title') as$prod )
 {
 //... and use a template to represent each $this->content( Template::Make('product_overview') )
 ->set('title',$prod->title)
 ->set('tagline',$prod->tagline)
 // see config.php where we set up products images folder as resource folder ->set('image',resFile($prod->image))
 ->set('link',buildQuery('Products','Details',array('id'=>$prod->id)))
 ;
 }
 }
 /**
 * Shows product details
 * @attribute[RequestParam('id','int')]
 */ function Details($id)
 {
 // check if product really exists$ds = model_datasource('system');
 $prod = $ds->Query('products')->eq('id',$id)->current();
 if(!$prod )
 redirect('Products','Index',array('error'=>'Product not found'));
 // create a template with product details $this->content( Template::Make('product_details') )
 ->set('title',$prod->title)
 ->set('description',$prod->body)
 // see config.php where we set up products images folder as resource folder ->set('image',resFile($prod->image))
 ->set('link',buildQuery('Basket','Add',array('id'=>$prod->id)))
 ;
 }
} 

这里没有魔法很简单的代码: '索引'方法循环遍历数据库中的所有产品,并使用模板'product_overview'显示每个产品。 如果你对这一点不熟悉,请再次查看基本插件

// templates/product_overview.tpl.php<divclass="product_overview"><imgsrc="<?=$image?>"alt=""/><divclass="title"><?=$title?></div><divclass="tagline"><?=$tagline?></div><ahref="<?=$link?>">Details...</a></div>

'详情'方法使用类似的方法,但不循环,而是加载单个产品。 它还使用另一个模板文件。

// templates/product_details.tpl.php<divclass="product_details"><imgsrc="<?=$image?>"alt=""/><divclass="title"><?=$title?></div><divclass="description"><?=$description?></div><ahref="<?=$link?>">Add to basket</a><ahref="javascript:history.back()">back to listing</a></div>

好吧。: 就是这样我们已经完成了完整的产品部分。

产品清单页面:

产品页面的详细信息:

篮子

就像几乎所有的商店我们想要一个购物篮。 简单原则:顾客向它添加产品,然后可以改变篮子里每个产品的数量。 减少到零将从篮子中移除产品。 当然可以,但是。"。"。你已经知道了'保持焦点'句子:。

所以代码再直接一次:

// controller/basket.class.php<?phpuse ScavixWDFBaseTemplate;
use ScavixWDFJQueryUIuiButton;
use ScavixWDFJQueryUIuiMessage;class Basket extends ShopBase
{
 /**
 * Lists all items in the basket.
 * @attribute[RequestParam('error','string',false)]
 */ function Index($error)
 {
 // display any given error message if( $error )
 $this->content(uiMessage::Error($error));
 // prepare basket variable if(!isset($_SESSION['basket']) )
 $_SESSION['basket'] = array();
 if( count($_SESSION['basket']) == 0 )
 $this->content(uiMessage::Hint('Basket is empty'));
 else {
 // list all items in the basket.. .$ds = model_datasource('system');
 $price_total = 0;
 foreach( $_SESSION['basket'] as $id=>$amount )
 {
 $prod = $ds->Query('products')->eq('id',$id)->current();
 //... each using a template $this->content( Template::Make('product_basket') )
 ->set('title',$prod->title)
 ->set('amount',$amount)
 ->set('price',$prod->price)
 // see config.php where we set up// products images folder as resource folder ->set('image',resFile($prod->image))
 ->set('add',buildQuery('Basket','Add',array('id'=>$prod->id)))
 ->set('remove',buildQuery('Basket','Remove',array('id'=>$prod->id)))
 ;
 $price_total += $amount * $prod->price;
 }
 // display total price and the button to go on $this->content("<div 
 class='basket_total'>Total price: $price_total</div>");
 $this->content( uiButton::Make("Buy now") )->onclick = 
 "location.href = '".buildQuery('Basket','BuyNow')."'";
 }
 }
 /**
 * Adds a product to the basket.
 * @attribute[RequestParam('id','int')]
 */ function Add($id)
 {
 // check if the product exists$ds = model_datasource('system');
 $prod = $ds->Query('products')->eq('id',$id)->current();
 if(!$prod )
 redirect('Basket','Index',array('error'=>'Product not found'));
 // increase the counter for this product if(!isset($_SESSION['basket'][$id]) )
 $_SESSION['basket'][$id] = 0;
 $_SESSION['basket'][$id]++;
 redirect('Basket','Index');
 }
 /**
 * Removes an item from the basket.
 * @attribute[RequestParam('id','int')]
 */ function Remove($id)
 {
 // check if the product exists$ds = model_datasource('system');
 $prod = $ds->Query('products')->eq('id',$id)->current();
 if(!$prod )
 redirect('Basket','Index',array('error'=>'Product not found'));
 // decrease the counter for this product if( isset($_SESSION['basket'][$id]) )
 $_SESSION['basket'][$id]--;
 // and unset if no more items left if( $_SESSION['basket'][$id] == 0 )
 unset($_SESSION['basket'][$id]);
 redirect('Basket','Index');
 }
 /* full code: https://github.com/ScavixSoftware/WebFramework/blob/master/web/sample_shop/controller/basket.class.php */} 

同样'索引'方法提供一个清单,这次源是包含产品id的key-value 对及其数量的会话变量。

'添加'和'移除'方法在篮子中增加和减少了这些产品的数量。

其他 上面 代码检查产品是否存在,以及变量是否存在。

最后提到:我们对订单项列表使用了一个特殊模板:

// templates/product_basket.tpl.php<divclass="product_basket"><imgsrc="<?=$image?>"alt=""/><spanclass="title"><?=$title?></span><spanclass="amount">Amount: <?=$amount?></span><spanclass="amount">Price: <?=$price?></span><spanclass="amount">Total: <?=$amount * $price?></span><ahref="<?=$add?>">add one more</a><ahref="<?=$remove?>">remove one</a></div>

这就是篮子的清单/编辑部分。 结果如下所示:

结帐

这实际上是实现商店系统时最有趣的部分: 如何获得客户的钱。 示例为你提供了到 PayPalGate2Shop的接口,并且可以很容易地扩展到支持其他提供商。 对于开发阶段,也有一个测试支付提供程序。

订单支付的基本工作流是:

  • 获取客户数据的地址
  • 将它与篮子数据一起存储到数据库中
  • 为所选付款提供程序启动签出过程
  • 对提供程序的付款消息作出React

复杂不:?

// controller/basket.class.php<?php
use ScavixWDFBaseTemplate;
use ScavixWDFJQueryUIuiButton;
use ScavixWDFJQueryUIuiMessage;class Basket extends ShopBase
{
 /* full code: https://github.com/ScavixSoftware/WebFramework/blob/master/web/sample_shop/controller/basket.class.php*//**
 * Entrypoint for the checkout process.
 * 
 * Requests customers address details and asks for payment processor.
 */ function BuyNow()
 {
 // displays the chechout form, which has all inputs for address on it $this->content( Template::Make('checkout_form') );
 }
 /**
 * Persists current basket to the database and starts checkout process.
 * @attribute[RequestParam('fname','string')]
 * @attribute[RequestParam('lname','string')]
 * @attribute[RequestParam('street','string')]
 * @attribute[RequestParam('zip','string')]
 * @attribute[RequestParam('city','string')]
 * @attribute[RequestParam('email','string')]
 * @attribute[RequestParam('provider','string')]
 */ function StartCheckout($fname,$lname,$street,$zip,$city,$email,$provider)
 {
 if(!$fname ||!$lname ||!$street ||!$zip ||!$city ||!$email )
 redirect('Basket','Index',array('error'=>'Missing some data'));
 // create a new customer. note that we do not check for existance or stuff.// this should be part of a real shop system! $cust = new SampleCustomer();
 $cust->fname = $fname;
 $cust->lname = $lname;
 $cust->street = $street;
 $cust->zip = $zip;
 $cust->city = $city;
 $cust->email = $email;
 $cust->price_total = 0;
 $cust->Save();
 // create a new order and assign the customer (from 上面) $order = new SampleShopOrder();
 $order->customer_id = $cust->id;
 $order->created = 'now()';
 $order->Save();
 // now loop thru the basket-items and add them to the order... $ds = model_datasource('system');
 foreach( $_SESSION['basket'] as $id=>$amount )
 {
 //... by creating a dataset for each item $prod = $ds->Query('products')->eq('id',$id)->current();
 $item = new SampleShopOrderItem();
 $item->order_id = $order->id;
 $item->price = $prod->price;
 $item->amount = $amount;
 $item->title = $prod->title;
 $item->tagline = $prod->tagline;
 $item->body = $prod->body;
 $item->Save();
 $order->price_total += $amount * $prod->price;
 }
 // save the order again to persist the total amount $order->Save();
 $_SESSION['basket'] = array();
 // finally start the checkout process using the given payment provider log_debug("Handing control over to payment provider '$provider'");
 $p = new $provider();
 $p->StartCheckout($order,buildQuery('Basket','PostPayment'));
 }
 /**
 * This is the return URL for the payment provider.
 * Will be called when payment raches a final state, so control is handed over to our 
 * app again from the payment processor.
 */ function PostPayment()
 {
 // we just display the $_REQUEST data for now.// in fact this is the point where some processing// should take place: send email to the team,// that prepares the items for shipping, send email(s) to customer,... log_debug("PostPayment",$_REQUEST);
 $this->content("<h1>Payment processed</h1>");
 $this->content("Provider returned this data:<br/>" + 
 "<pre>".render_var($_REQUEST)."</pre>");
 }
 /**
 * This is a special handler method for PayPal.
 * It will be called asynchronously from PayPal
 * backend so user will never see results of it.
 * Just here to update the database when payments
 * are ready or refunded or whatever.
 * See https://www.paypal.com/ipn for details
 * but in fact WebFramework will handle this for you.
 * Just needs this entry point for the callback.
 * @attribute[RequestParam('provider','string')]
 */ function Notification($provider)
 {
 log_debug("Notification",$_REQUEST);
 $provider = new $provider();
 if( $provider->HandleIPN($_REQUEST) )
 die("OK");
 die("ERR");
 }
} 

代码创建表单,询问客户地址数据和他希望使用的付款提供程序:

到目前为止,如果单击'立即购买'( 与测试提供者不同,就不会出现)。

为了使它以这种简单的方式工作,SampleShopOrder必须实现 IShopOrder 接口。 它提供一些简单的方法,允许一般处理支付过程:

// model/sampleshoporder.class.php<?php
use ScavixWDFModelModel;
use ScavixWDFPaymentIShopOrder;
use ScavixWDFPaymentShopOrderAddress;/**
 * Represents an order in the database.
 * 
 * In fact nothing more than implementations for the inherited Model 
 * and the implemented IShopOrder interface.
 * See https://github.com/ScavixSoftware/WebFramework/wiki/classes_modules_payment#wiki-1c67f96d00c3c22f1ab9002cd0e3acbb
 * More logic would go into the Set* methods to handle different order states.
 * For our sample we just set the states in the DB.
 */class SampleShopOrder extends Model implements IShopOrder
{
 const UNKNOWN = 0;
 const PENDING = 10;
 const PAID = 20;
 const FAILED = 30;
 const REFUNDED = 40;
 /**
 * Returns the table name.
 * See https://github.com/ScavixSoftware/WebFramework/wiki/classes_essentials_model_model.class#gettablename
 */public function GetTableName() { return'orders'; }
 /**
 * Gets the orders address.
 * @return ShopOrderAddress The order address
 */public function GetAddress()
 {
 $res = new ShopOrderAddress();
 $res->Firstname = $this->fname;
 $res->Lastname = $this->lname;
 $res->Address1 = $this->street;
 $res->Zip = $this->zip;
 $res->City = $this->city;
 $res->Email = $this->email;
 return $res;
 }
 /**
 * Gets the currency code.
 * @return string A valid currency code
 */public function GetCurrency() { return'EUR'; }
 /**
 * Gets the invoice ID.
 * @return mixed Invoice identifier
 */public function GetInvoiceId() { return"I".$this->id; }
 /**
 * Gets the order culture code.
 * 
 * See <CultureInfo>
 * @return string Valid culture code
 */public function GetLocale() { return'en-US'; }
 /**
 * Return the total price incl. VAT (if VAT applies for the given country). 
 * @param float $price The price without VAT.
 * @return float Price including VAT (if VAT applies for the country).
 */public function GetTotalPrice($price = false)
 {
 if( $price!== false )
 return $price * ( (1+$this->GetVatPercent())/100 );
 return $this->price_total * ( (1+$this->GetVatPercent())/100 );
 }
 /**
 * Return the total VAT (if VAT applies for the given country). 
 * @return float VAT in order currency
 */public function GetTotalVat() { return $this->price_total * ($this->GetVatPercent()/100); }
 /**
 * Return the total VAT percent (if VAT applies for the given country). 
 * @return float VAT percent
 */public function GetVatPercent() { return19; }
 /**
 * Returns all items.
 * 
 * @return array A list of <IShopOrderItem> objects
 */public function ListItems() { return SampleShopOrderItem::Make()->eq('order_id',$this->id)->orderBy('id'); }
 /**
 * Sets the currency
 * @param string $currency_code A valid currency code
 * @return void
 */public function SetCurrency($currency_code) { /* we stay with EUR */ }
 /**
 * Creates an instance from an order id.
 * @return IShopOrder The new/loaded order <Model>
 */publicstatic function FromOrderId($order_id)
 {
 return SampleShopOrder::Make()->eq('id',$order_id)->current();
 }
 /**
 * Called when the order has failed.
 * 
 * This is a callback from the payment processor. Will be called when there was an error in the payment process.
 * This can be synchronous (when cutsomer aborts in then initial payment ui) or asynchronous when something goes wrong
 * later in the payment processors processes.
 * @param int $payment_provider_type Provider type identifier (<PaymentProvider>::PROCESSOR_PAYPAL, <PaymentProvider>::PROCESSOR_GATE2SHOP,.. .)
 * @param mixed $transaction_id Transaction identifier (from the payment provider)
 * @param string $statusmsg An optional status message
 * @return void
 */public function SetFailed($payment_provider_type, $transaction_id, $statusmsg = false)
 {
 $this->status = self::FAILED;
 $this->updated = $this->deleted = 'now()';
 $this->Save();
 }
 /**
 * Called when the order has been paid.
 * 
 * This is a callback from the payment processor. Will be called when the customer has paid the order.
 * @param int $payment_provider_type Provider type identifier (<PaymentProvider>::PROCESSOR_PAYPAL, <PaymentProvider>::PROCESSOR_GATE2SHOP,.. .)
 * @param mixed $transaction_id Transaction identifier (from the payment provider)
 * @param string $statusmsg An optional status message
 * @return void
 */public function SetPaid($payment_provider_type, $transaction_id, $statusmsg = false)
 {
 $this->status = self::PAID;
 $this->updated = $this->completed = 'now()';
 $this->Save();
 }
 /**
 * Called when the order has reached pending state.
 * 
 * This is a callback from the payment processor. Will be called when the customer has paid the order but the
 * payment has not yet been finished/approved by the provider.
 * @param int $payment_provider_type Provider type identifier (<PaymentProvider>::PROCESSOR_PAYPAL, <PaymentProvider>::PROCESSOR_GATE2SHOP,.. .)
 * @param mixed $transaction_id Transaction identifier (from the payment provider)
 * @param string $statusmsg An optional status message
 * @return void
 */public function SetPending($payment_provider_type, $transaction_id, $statusmsg = false)
 {
 $this->status = self::PENDING;
 $this->updated = 'now()';
 $this->Save();
 }
 /**
 * Called when the order has been refunded.
 * 
 * This is a callback from the payment processor. Will be called when the payment was refunded for any reason.
 * This can be reasons from the provider and/or from the customer (when he cancels the payment later).
 * @param int $payment_provider_type Provider type identifier (<PaymentProvider>::PROCESSOR_PAYPAL, <PaymentProvider>::PROCESSOR_GATE2SHOP,.. .)
 * @param mixed $transaction_id Transaction identifier (from the payment provider)
 * @param string $statusmsg An optional status message
 * @return void
 */public function SetRefunded($payment_provider_type, $transaction_id, $statusmsg = false)
 {
 $this->status = self::REFUNDED;
 $this->updated = $this->deleted = 'now()';
 $this->Save();
 }
 /**
 * Checks if VAT needs to be paid.
 * @return boolean true or false
 */public function DoAddVat() { returntrue; /* Let's assume normal VAT customers for now */ }
} 

你还需要为订单项目创建一个类,我们称为 SampleShopOrderItem,并实现了IShopOrderItem的接口。

// model/sampleshoporderitem.class.php<?phpuse ScavixWDFModelModel;
use ScavixWDFPaymentIShopOrderItem;/**
 * Represents an order item in the database.
 * 
 * In fact nothing more than implementations for the inherited Model 
 * and the implemented IShopOrderItem interface.
 * See https://github.com/ScavixSoftware/WebFramework/wiki/classes_modules_payment#wiki-97745ff2e14aebb2225c7647a8a059bc
 */class SampleShopOrderItem extends Model implements IShopOrderItem
{
 /**
 * Returns the table name.
 * See https://github.com/ScavixSoftware/WebFramework/wiki/classes_essentials_model_model.class#gettablename
 */ public function GetTableName() { return'items'; }
 /**
 * Gets the price per item converted into the requested currency.
 * @param string $currency Currency code
 * @return float The price per item converted into $currency
 */ public function GetAmount($currency) { return $this->price; }
 /**
 * Gets the discount.
 * @return float The discount
 */ public function GetDiscount() { return0; }
 /**
 * Gets the handling cost.
 * @return float Cost for handling
 */ public function GetHandling() { return0; }
 /**
 * Gets the items name.
 * @return string The item name
 */ public function GetName() { return $this->title; }
 /**
 * Gets the quantity.
 * @return float The quantity
 */ public function GetQuantity() { return $this->amount; }
 /**
 * Gets the shipping cost.
 * @return float Cost for shipping
 */ public function GetShipping() { return0; }
}

最后,必须配置付款 MODULE:

// config.php <?php// full code: https://github.com/ScavixSoftware/WebFramework/blob/master/web/sample_shop/config.php// configure payment module with your IShopOrder class$CONFIG["payment"]["order_model"] = 'SampleShopOrder';// set up Gate2Shop if you want to use it$CONFIG["payment"]["gate2shop"]["merchant_id"] = '<your_merchant_id>';
$CONFIG["payment"]["gate2shop"]["merchant_site_id"] = '<your_merchant_site_id>';
$CONFIG["payment"]["gate2shop"]["secret_key"] = '<your_secret_key>';// set up PayPal if you want to use it$CONFIG["payment"]["paypal"]["paypal_id"] = '<your_paypal_id>';
$CONFIG["payment"]["paypal"]["notify_handler"] = array('Basket','Notification');

管理

对于一个商店系统,你需要能够创建产品,并且有一些对客户的数据和订单的访问。

进入管理页面后,它将询问你凭据和( 如上所述如上所述) ,它们是硬编码的: 使用'管理员'作为用户名,'管理员'作为密码。

// controller/admin.class.php<?phpuse ScavixWDFBaseAjaxAction;
use ScavixWDFBaseAjaxResponse;
use ScavixWDFBaseTemplate;
use ScavixWDFControlsFormForm;
use ScavixWDFJQueryUIDialoguiDialog;
use ScavixWDFJQueryUIuiButton;
use ScavixWDFJQueryUIuiDatabaseTable;
use ScavixWDFJQueryUIuiMessage;class Admin extends ShopBase
{
 /**
 * Checks if aa admin has logged in and redirects to login if not.
 */ private function _login()
 {
 // check only the fact that somebody logged in if( $_SESSION['logged_in'] ) 
 return true;
 // redirect to login. this terminates the script execution. redirect('Admin','Login');
 }
 /**
 * @attribute[RequestParam('username','string',false)]
 * @attribute[RequestParam('password','string',false)]
 */ function Login($username,$password)
 {
 // if credentials are given, try to log in if( $username && $password )
 {
 // see config.php for credentials if( $username==cfg_get('admin','username') && $password==cfg_get('admin','password') )
 {
 $_SESSION['logged_in'] = true; // check only the fact that somebody logged in redirect('Admin');
 }
 $this->content(uiMessage::Error("Unknown username/passsword"));
 }
 // putting it together as control here. other ways would be to create a new class // derived from Control or a Template (anonymous or with an own class)$form = $this->content(new Form());
 $form->content("Username:");
 $form->AddText('username', '');
 $form->content("<br/>Password:");
 $form->AddPassword('password', '');
 $form->AddSubmit("Login");
 }
 /* full code: https://github.com/ScavixSoftware/WebFramework/blob/master/web/sample_shop/controller/admin.class.php*/} 

Admin类中的每个方法都调用'_login'方法,如果没有管理员用户登录到'管理员/登录',则该方法将重定向到。 方法构建一个没有模板的登录窗体,只使用纯控件语法。


在这种原始开发状态中非常丑陋,但是。

现在我们先从前面提到的上面 开始: 产品管理。

// controller/admin.class.php<?phpuse ScavixWDFBaseAjaxAction;
use ScavixWDFBaseAjaxResponse;
use ScavixWDFBaseTemplate;
use ScavixWDFControlsFormForm;
use ScavixWDFJQueryUIDialoguiDialog;
use ScavixWDFJQueryUIuiButton;
use ScavixWDFJQueryUIuiDatabaseTable;
use ScavixWDFJQueryUIuiMessage;class Admin extends ShopBase
{
 /* full code: https://github.com/ScavixSoftware/WebFramework/blob/master/web/sample_shop/controller/admin.class.php */ function Index()
 {
 $this->_login(); // require admin to be logged in// add products table and a button to create a new product $this->content("<h1>Products</h1>");
 $this->content(new uiDatabaseTable(model_datasource('system'),false,'products'))
 ->AddPager(10)
 ->AddRowAction('trash', 'Delete', $this, 'DelProduct');
 $this->content(uiButton::Make('Add product'))->onclick = AjaxAction::Post('Admin', 'AddProduct');
 // add orders table $this->content("<h1>Orders</h1>");
 $this->content(new uiDatabaseTable(model_datasource('system'),false,'orders'))
 ->AddPager(10)
 ->OrderBy = 'id DESC';
 // add customers table $this->content("<h1>Customers</h1>");
 $this->content(new uiDatabaseTable(model_datasource('system'),false,'customers'))
 ->AddPager(10)
 ->OrderBy = 'id DESC';
 }
 /**
 * @attribute[RequestParam('title','string',false)]
 * @attribute[RequestParam('tagline','string',false)]
 * @attribute[RequestParam('body','text',false)]
 * @attribute[RequestParam('price','double',false)]
 */ function AddProduct($title,$tagline,$body,$price)
 {
 $this->_login(); // require admin to be logged in// This is a quite simple condition: You MUST provide each of the variables if( $title && $tagline && $body && $price )
 {
 // store the uploaded image if present if( isset($_FILES['image']) && $_FILES['image']['name'] )
 {
 $i = 1; $image = __DIR__.'/../images/'.$_FILES['image']['name'];
 while( file_exists($image) )
 $image = __DIR__.'/../images/'.($i++).'_'.$_FILES['image']['name'];
 move_uploaded_file($_FILES['image']['tmp_name'], $image);
 $image = basename($image);
 }
 else 
 $image = '';
 // store the new product into the database$ds = model_datasource('system');
 $ds->ExecuteSql("INSERT INTO products(title,tagline,body,image,price)VALUES(?,?,?,?,?)",
 array($title,$tagline,$body,$image,$price));
 redirect('Admin');
 }
 // create a dialog and put a template on it.$dlg = new uiDialog('Add product',array('width'=>600,'height'=>450));
 $dlg->content( Template::Make('admin_product_add') );
 $dlg->AddButton('Add product', "$('#frm_add_product').submit()"); // frm_add_product is defined in the template $dlg->AddCloseButton("Cancel");
 return$dlg;
 }
 /**
 * @attribute[RequestParam('table','string',false)]
 * @attribute[RequestParam('action','string',false)]
 * @attribute[RequestParam('model','array',false)]
 * @attribute[RequestParam('row','string',false)]
 */ function DelProduct($table,$action,$model,$row)
 {
 $this->_login(); // require admin to be logged in// we use the ajax confirm features of the framework which require some translated string, so we set them up here// normally we would start the sysadmin and create some, but for this sample we ignore that. default_string('TITLE_DELPRODUCT','Delete Product');
 default_string('TXT_DELPRODUCT','Do you really want to remove this product? This cannot be undone!');
 if(!AjaxAction::IsConfirmed('DELPRODUCT') )
 return AjaxAction::Confirm('DELPRODUCT', 'Admin', 'DelProduct', array('model'=>$model));
 // load and delete the product dataset$ds = model_datasource('system');
 $prod = $ds->Query('products')->eq('id',$model['id'])->current();
 $prod->Delete();
 // delete the image too if present if( $prod->image )
 {
 $image = __DIR__.'/../images/'.$prod->image;
 if( file_exists($image) )
 unlink($image);
 }
 return AjaxResponse::Redirect('Admin');
 }
}

'Index'方法创建一个数据库 table 和一个按钮来添加另一个产品。 Simple控件可以添加行操作,以便在单击时调用'delproduct'。

单击'添加产品'按钮将显示一个对话框,其中包含一个表单,用于输入所有产品数据。 然后( 当对话框被接受时) 将新产品添加到数据库中,并重定向浏览器以刷新产品列表。

猜一下:这就是产品管理基础部分的内容。 当然,我们缺少'编辑'功能,但是它显示了我们需要进行完整管理界面的方式。

按照这个想法,我们在这里只显示两个表: 订单和客户。只是为了展示基本的想法:

接下来

你已经准备好了但是你怎么? 嗯,有一些'标准'的事情要做:

  • register 在PayPal和/或者 Gate2Shop,更新商店配置,使它们变得可用
  • 实现更多使支付工作流有用的代码( 发送电子邮件。)
  • 扩展管理界面,以管理产品和订单

更改日志

  • 2013/05/8: 初始发布
  • 2013/05/23: 固定中断链接,将代码段更新为新版本
  • 2014/10/31: 添加了命名空间代码

IMP  SHOP  
相关文章