zen cart关于折扣的插件不少,但大部分是基于产品的,比如买多少个产品时,价格打多少折,或者是买多少个产品送多少。今天要探讨的是基于订单总金额或整个购物车产品的数量来打折。如,
当订单金额超过$500时,优惠$26
当订单金额超过$1000时,给%8的折扣
当订单中产品总数量超过5个时,给予整个订单总额%5的折扣
当订单中产品数量超过20个时,整个订单打9折
等等… …
经过对zen cart相关代码的分析,我发现实施上面的这睦规则其实很容易。只是zen cart本身并没有非常好的模块和数据分离机制,所以我就先把简单实施的方法写出,待以后有空再整理形成模块发布。
几乎所有的zen cart模板通常只会有两到三个地方会显示订单的汇总信息,一个是在购物车页面shopping_cart,再一个是订单信息确认页面checkout_confirmation,最后还有可能显示的则是相关的sidebox.当然某些zen cart模板可能会有更多的页面有显示这个内容。不过总结下来,不外乎这两个页面的修改。所以,实现这个打折功能的主要工作就是修改这两个页面和生成订单的类。事实上经过代码分析我发现shopping_cart页面的订单统计采用的方法和checkout_confirmation页面的不一样,前者是调用shopping_cart类,而后者是调用order类。
先看下shopping_cart页面的模板includes/templates/[模板名称]/templates/tpl_shopping_cart_default.php:
原先购物车只显示订单总金额,现在要显示三项,分别是打折前总金额,优惠的金额,折扣后的总金额,模板要修改成
那么就需要在shopping_cart对应的header_php.php文件中定义初始化这三个变量includes/modules/pages/shopping_cart/header_php.php:
$flagHasCartContents = ($_SESSION['cart']->count_contents() > 0);
$cartShowTotal = $currencies->format($_SESSION['cart']->show_total());
//added by george
$cartOtotal = $currencies->format($_SESSION['cart']->show_ototal());
$discountTotal = $cartOtotal - $cartShowTotal;
$cartOtotal是订单原价,$cartShowTotal是打折后的订单总额.$discountTotal优惠的金额。下一步则是对shopping_cart类的修改了:修改calculate成员方法实现打折规则;添加一个成员属性来保存原价;添加一个方法也读取原价;代码如下:
-------includes/classes/shopping_cart.php
...
...
var $contents;
/**
* shopping cart total price
* @var decimal
*/
var $total;
/**
* shopping cart total weight
* @var decimal
*/
//added by george
//添加成员属性$ototal
var $ototal;
/**
* Method to calculate cart totals(price and weight)
*
* @return void
* @global object access to the db object
*/
function calculate() {
global $db;
$this->total = 0;
$this->weight = 0;
...
...
...
// + or blank adds
if ($attribute_weight->fields['products_attributes_weight_prefix'] == '-') {
$this->weight -= $qty * $new_attributes_weight;
} else {
$this->weight += $qty * $new_attributes_weight;
}
}
} // attributes weight
}
//added by george to apply discount
//实现打折规则
$this -> ototal = $this -> total; //save origianl order total
if ( $this -> count_contents() == 2) {
$this -> total = $this -> total * 0.95;
} elseif ( $this -> count_contents() >= 3) {
$this -> total = $this -> total * 0.9;
}
//end of discount code
}
/**
* Method to calculate price of attributes for a given item
...
...
/**
* Method to calculate total price of items in cart
*
* @return decimal Total Price
*/
function show_total() {
$this->notify('NOTIFIER_CART_SHOW_TOTAL_START');
$this->calculate();
$this->notify('NOTIFIER_CART_SHOW_TOTAL_END');
return $this->total;
}
//added by george
//获取原价的方法
function show_ototal() {
$this->notify('NOTIFIER_CART_SHOW_TOTAL_START');
$this->calculate();
$this->notify('NOTIFIER_CART_SHOW_TOTAL_END');
return $this->ototal;
}
到目前为止,对购物车页面的修改算是完成了。当然CSS之类的修饰是必要的。这里就不提了。不过订单确认页面checkout_confirmation还末修改。
if (MODULE_ORDER_TOTAL_INSTALLED) {
$order_totals = $order_total_modules->process();
?>
output(); ?>
}
?>
显然checkout_confirmation页面的订单汇总信息是通过$order_total_modules对象生成的而这个对象是在checkout_confirmation页面的header_php.php中定义的:
./includes/modules/pages/checkout_confirmation/header_php.php:66:$order_total_modules = new order_total;
那么再往上追踪一步应该是查看order_total类的output方法了:
function output($return_html=false) {
global $template, $current_page_base;
$output_string = '';
if (is_array($this->modules)) {
reset($this->modules);
while (list(, $value) = each($this->modules)) {
$class = substr($value, 0, strrpos($value, '.'));
$size = sizeof($GLOBALS[$class]->output);
// ideally, the first part of this IF statement should be dropped, and the ELSE portion is all that should be kept
if ($return_html == true) {
for ($i=0; $i<$size; $i++) {
$output_string .= '
' . "\n" .
'
' . $GLOBALS[$class]->output[$i]['title'] . '
' . "\n" .
'
' . $GLOBALS[$class]->output[$i]['text'] . '
' . "\n" .
'
';
}
} else {
// use a template file for output instead of hard-coded HTML
require($template->get_template_dir('tpl_modules_order_totals.php',DIR_WS_TEMPLATE, $current_page_base,'templates'). '/tpl_modules_order_totals.php');
}
}
}
return $output_string;
}
显然最终调用的是tpl_modules_order_totals.php这个模板:
for ($i=0; $i<$size; $i++) { ?>
output[$i]['text']; ?>
output[$i]['title']; ?>
通过分析zen cart的order_total类的构造函数order_total(代码如下),再综合order_total类的output方法,process方法,可以确定tpl_modules_order_totals.php这个模板是通过调用includes/modules/order_total/目录下的ot_subtotal,ot_shipping,ot_total这三个类。通过在tpl_modules_order_totals.php这个模板
function order_total() {
global $messageStack;
if (defined('MODULE_ORDER_TOTAL_INSTALLED') && zen_not_null(MODULE_ORDER_TOTAL_INSTALLED)) {
$module_list = explode(';', MODULE_ORDER_TOTAL_INSTALLED);
reset($module_list);
while (list(, $value) = each($module_list)) {
//include(DIR_WS_LANGUAGES . $_SESSION['language'] . '/modules/order_total/' . $value);
$lang_file = zen_get_file_directory(DIR_WS_LANGUAGES . $_SESSION['language'] . '/modules/order_total/', $value, 'false');
if (@file_exists($lang_file)) {
include_once($lang_file);
} else {
if (IS_ADMIN_FLAG === false) {
$messageStack->add(WARNING_COULD_NOT_LOCATE_LANG_FILE . $lang_file, 'caution');
} else {
$messageStack->add_session(WARNING_COULD_NOT_LOCATE_LANG_FILE . $lang_file, 'caution');
}
}
//由此可确定调用的是includes/modules/order_total/目录下的这些类
$module_file = DIR_WS_MODULES . 'order_total/' . $value;
if (@file_exists($module_file)) {
include_once($module_file);
$class = substr($value, 0, strrpos($value, '.'));
$GLOBALS[$class] = new $class;
$this->modules[] = $value;
}
}
}
}
Ok,终于快找到我们要修改的代码了。再分析一下,现在checkout_confirmation显示的三项是subtotal,shipping,order total,订单原价,运费,加运费后订单总额。
通过分析ot_subtotal,ot_shipping,ot_total这三个类对output数组的初始化,我最终发现数据最终来源于order类[includes/class/order.php],以ot_total为例看一下代码:
function process() {
global $order, $currencies;
$this->output[] = array('title' => $this->title . ':',
'text' => $currencies->format($order->info['total'], true, $order->info['currency'], $order->info['currency_value']),
'value' => $order->info['total']);
}
最终我通过修改了order类cart成员方法,直接应用了打折规,实现代码如下:
*********************************************/
$index++;
}
//下面的代码实现打折规则,不过这样写运费是没参与打折的
$this->info['osubtotal'] = $this->info['subtotal'];
if ( $_SESSION['cart'] ->count_contents() == 2) {
$this->info['subtotal'] *= 0.95;
} elseif( $_SESSION['cart'] ->count_contents() >= 3) {
$this->info['subtotal'] *= 0.9;
}
// Update the final total to include tax if not already tax-inc
if (DISPLAY_PRICE_WITH_TAX == 'true') {
$this->info['total'] = $this->info['subtotal'] + $this->info['shipping_cost'];
} else {
$this->info['total'] = $this->info['subtotal'] + $this->info['tax'] + $this->info['shipping_cost'];
}
需要注意的是,上面的这个代码打折是针对产品的总价,没有对运费打折。如果要对运费也打折,那就要修改成这样:
*********************************************/
$index++;
}
// Update the final total to include tax if not already tax-inc
if (DISPLAY_PRICE_WITH_TAX == 'true') {
$this->info['total'] = $this->info['subtotal'] + $this->info['shipping_cost'];
} else {
$this->info['total'] = $this->info['subtotal'] + $this->info['tax'] + $this->info['shipping_cost'];
}
//在这边应用打折规则,是对订单总金额,包括运费一起打折
if ( $_SESSION['cart'] ->count_contents() == 2) {
$this->info['total'] *= 0.95;
} elseif( $_SESSION['cart'] ->count_contents() >= 3) {
$this->info['total'] *= 0.9;
}
以上的只是我实施后的对实现思路的粗略回忆,待有空再整理成zen cart模块形式发布。并在zen cart后台设置一个界面进行打折规则的设置。而不是将打折规则写死在代码中。
(责任编辑:好模板) |