模块系统

初始化

下面将以文件“index.php”为例来说明 osCommerce V3 模块系统

文件:index.php[21]

$osC_Template = osC_Template::setup('index');

require('templates/' . $osC_Template->getCode() . '.php');

第 21 行代码,调用 osC_Template 类的静态方法 setup 初始化模块,参数“index”指定了需要显 示的主模板。

第 23 行代码通过得到的 osC_Template 对象的方法 getCode 获得模板的 Code 文件,即模板系统的 主显示文件,并执行它。

osC_Template 类文件:includes/classes/templates.php[178]

function &setup($module) {
  $group = basename($_SERVER['SCRIPT_FILENAME']);
  if (($pos = strrpos($group, '.')) !== false) {
    $group = substr($group, 0, $pos); // 当调用文件为“index.php”时,$group 的值将为 “index” 
  }

  if (empty($_GET) === false) {
    $first_array = array_slice($_GET, 0, 1); // GET 参数的第一位被设定为自动模块设置, 也就是说 GET 参数的第一位将被替代参数$module 的作用
    $_module = osc_sanitize_string(basename(key($first_array))); // 只取第一位参数的 KEY
    if (file_exists('includes/content/' . $group . '/' . $_module . '.php')) {
      $module = $_module;
    }
  }

  include('includes/content/' . $group . '/' . $module . '.php'); // 默认情况下, 将调用“includes/content/index/index.php”文件,此文件将输出首页的主体内容
  $_page_module_name = 'osC_' . ucfirst($group) . '_' . ucfirst($module); // 主 模块是类,类名的格式是:osC_GROUP_MODULE,GROUP:组名(主文件名去除后缀)、 MODULE:主模块名
  $object = new $_page_module_name(); // 初始化主模块

  // 主模块在初始化后就已经完成了页面主体内容的准备功能,初始化后,$object->_page_title 内 部参数保存了主模块的标题(通常也是页面的标题),$object->_page_contents 内部参数保存最终需要显 示的主体内容,因为主模块类都是osC_Template类的子类,所以可以通过osC_Template的方法来访问这些 参数并输出。

  if ( isset($_GET['action']) && !empty($_GET['action']) ) { // GET参数的action 指定全局动作,如加入购物车、移出购物车等
    include('includes/classes/actions.php');
    osC_Actions::parse($_GET['action']); // 动作类 osC_Action 只有一个方法 parse 用于 分析处理全局动作
  }
  return $object; // 返回主模块对象,用于最终的输出
}

osC_Template 的方法 setup 只有一个参数:主模块$module,该方法会返回对象,对象的类型取决 于参数$module 的值。

提示:

文件夹“includes/content/”存放的是每个页的主体内容(页面的中间部分),同时文件按约定目录 及名称保存,首先是在“includes/content”下以主文件名(如 index.php、account.php 等)为目录, 再在其下以主模块作为文件的文件名。

工作原理

osC_Index_Index 类文件:includes/content/index/index.php

class osC_Index_Index extends osC_Template { // 所有主模块类继承自 osC_Template 模板类 /* Private variables */
  var $_module = 'index',
      $_group = 'index',
      $_page_title,
      $_page_contents = 'index.php',
      $_page_image = 'table_background_default.gif';

  /* Class constructor */
  function osC_Index_Index() {
    global $osC_Database, $osC_Services, $osC_Language, $osC_Breadcrumb, $cPath,
           $cPath_array, $current_category_id, $osC_Category;

    $this->_page_title = sprintf($osC_Language->get('index_heading'), STORE_NAME);

    // 用内部变量_page_title 保存页面的标题
    if (isset($cPath) && (empty($cPath) === false)) {
      if ($osC_Services->isStarted('breadcrumb')) {
        $Qcategories = $osC_Database->query('select categories_id, categories_name from :table_categories_description where categories_id in (:categories_id) and language_id = :language_id');
        $Qcategories->bindTable(':table_categories_description', TABLE_CATEGORIES_DESCRIPTION);
        $Qcategories->bindTable(':categories_id', implode(',', $cPath_array));
        $Qcategories->bindInt(':language_id', $osC_Language->getID());
        $Qcategories->execute();


        $categories = array();
        while ($Qcategories->next()) {
          $categories[$Qcategories->value('categories_id')] = $Qcategories->valueProtected('categories_name'); // 查询当前产品分类及其以上所有分类的名 称与 ID,并保存到关联数组$categories
        }
        $Qcategories->freeResult();
        for ($i=0, $n=sizeof($cPath_array); $i<$n; $i++) {
          $osC_Breadcrumb->add($categories[$cPath_array[$i]],
          osc_href_link(FILENAME_DEFAULT, 'cPath=' . implode('_', array_slice($cPath_array, 0, ($i+1))))); // 创建产品分类的 Breadcrumb 导航
        }
      }

      $osC_Category = new osC_Category($current_category_id); // 类 osC_Category 用 于处理分类的查询
      $this->_page_title = $osC_Category->getTitle(); // 以当前分类的名称作为页面的标题 ,getTitle 方法返回当前分类的名称
      if ( $osC_Category->hasImage() ) {
        $this->_page_image = 'categories/' . $osC_Category->getImage();// 当前分类的图片取代默认的图片
      }

      $Qproducts = $osC_Database->query('select products_id from :table_products_to_categories where categories_id = :categories_id limit 1');
      $Qproducts->bindTable(':table_products_to_categories', TABLE_PRODUCTS_TO_CATEGORIES);
      $Qproducts->bindInt(':categories_id', $current_category_id);


      $Qproducts->execute();
      if ($Qproducts->numberOfRows() > 0) { // 当前分类下是否有直属产品
        $this->_page_contents = 'product_listing.php'; // 将调用“includes/modules/product_listing.php”文件显示出当前分类所有的产品
        $this->_process(); // 处理过滤以及排序 } else {
        $Qparent = $osC_Database->query('select categories_id from :table_categories where parent_id = :parent_id limit 1');
        $Qparent->bindTable(':table_categories', TABLE_CATEGORIES);
        $Qparent->bindInt(':parent_id', $current_category_id);
        $Qparent->execute();
        if ($Qparent->numberOfRows() > 0) { // 否则显示当前分类属下的所有产品分类
          $this->_page_contents = 'category_listing.php';
        } else { // 将显示没有任何内容的提示信息
          $this->_page_contents = 'product_listing.php';
          $this->_process();
        }
      }
    }
  }

  /* Private methods */
  function _process() {
    global $current_category_id, $osC_Products;
    include('includes/classes/products.php');
    $osC_Products = new osC_Products($current_category_id);

    if (isset($_GET['filter']) && is_numeric($_GET['filter']) && ($_GET['filter'] > 0)) {
      $osC_Products->setManufacturer($_GET['filter']);
    }

    if (isset($_GET['sort']) && !empty($_GET['sort'])) {
      if (strpos($_GET['sort'], '|d') !== false) {
        $osC_Products->setSortBy(substr($_GET['sort'], 0, -2), '-');
      } else {
        $osC_Products->setSortBy($_GET['sort']);
      }
    }
  }
}

上面是整个主模块文件“index.php”,osC_index_index 类只有两个方法,一个是初始化方法 osC_Index_Index,另一个是_process 方法,初始化方法用于输出显示的内容,_process 方法是一 个内部方法(private),是可选方法,通常用于处理更复杂的数据交互。

介绍完主模块文件,接着就要开始模板的显示任务了,我们回到 index.php 的第 23 行:

require('templates/' . $osC_Template->getCode() . '.php');

这行代码便是开始加载模板的关键。

osC_Templae 模板类文件:includes/classes/template.php[230]

function getCode($id = null) {
  if (isset($this->_template) === false) {
    $this->set(); // 设定初始模板 
  }

  if (is_numeric($id)) { // 如果指定了模板 ID,则需要变更初始模板
      foreach ($this->getTemplates() as $template) {
        if ($template['id'] == $id) {
          return $template['code']; // 得到需要的模板
        }
      }
  } else {
    return $this->_template; // 使用初始模板进行显示
  }
}

提示:

模块 code 决定了模块的文件名,即模块文件名=模板 code.php,并且模板必须保存在“templates/” 目录

设置初始模板 includes/classes/template.php [490]

function set($code = null) {
  if ( (isset($_SESSION['template']) === false) || !empty($code) || (isset($_GET['template']) && !empty($_GET['template'])) ) { // 使用会话 template 来保 存当前的模板
    if ( !empty( $code ) ) {
      $set_template = $code; // 如参数$code 指定模板
    } else {
      $set_template = (isset($_GET['template']) && !empty($_GET['template'])) ? $_GET['template'] : DEFAULT_TEMPLATE; // 或者使用$_GET 参数 template 当作当前模板(这样便 可允许临时切换模板,对于新模板的调试非常有用),否则使用默认的模板
    }

    $data = array();
    $data_default = array();
    foreach ($this->getTemplates() as $template) { // getTemplates 方法获得所有可用 的模板
      if ($template['code'] == DEFAULT_TEMPLATE) {
        $data_default = array('id' => $template['id'], 'code' => $template['code']);
      } elseif ($template['code'] == $set_template) {
        $data = array('id' => $template['id'], 'code' => $template['code']);
      }
    }

    if (empty($data)) {
      $data =& $data_default;
    }

    $_SESSION['template'] =& $data;
  }

  $this->_template_id =& $_SESSION['template']['id'];
  $this->_template =& $_SESSION['template']['code'];
  // 内部变量$_template_id(默 认模板 ID)和$_template(默认模板 code)与会话$_SESSION[‘template’][‘id’]、 $_SESSION[‘template’][‘code’]是引用的关系。所以无论是修改内部变量的值或者是修改$_SESSION 的 值,两边的值都保证是同步的
}

获得可用模板 includes/classes/template.php [376]

function &getTemplates() {
  global $osC_Database;

  $templates = array();
  // 直接通过数据库查询可用模板,并且开启的缓存功能
  $Qtemplates = $osC_Database->query('select id, code, title from :table_templates');
  $Qtemplates->bindTable(':table_templates', TABLE_TEMPLATES);
  $Qtemplates->setCache('templates');//开启缓存 $Qtemplates->execute();

  while ($Qtemplates->next()) {
    $templates[] = $Qtemplates->toArray(); // 得到所有可用模板
  }

  $Qtemplates->freeResult();
  return $templates;
}

模板文件 templates/default.php[15]

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" dir="<?php echo
$osC_Language->getTextDirection(); ?>" xml:lang="<?php echo
$osC_Language->getCode(); ?>" lang="<?php echo $osC_Language->getCode(); ?>">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=<?php echo
$osC_Language->getCharacterSet(); ?>" />
<title><?php echo STORE_NAME . ($osC_Template->hasPageTitle() ? ': ' .
$osC_Template->getPageTitle() : ''); ?></title>
<base href="<?php echo osc_href_link(null, null, 'AUTO', false); ?>" />
<script language="javascript" src="ext/jquery/jquery-1.3.2.min.js"></script>
<link rel="stylesheet" type="text/css" href="templates/<?php echo $osC_Template->getCode(); ?>/stylesheet.css" />
<?php
  if ($osC_Template->hasPageTags()) {
   echo $osC_Template->getPageTags();
  }
  if ($osC_Template->hasJavascript()) {
   $osC_Template->getJavascript();
} ?>

模板文件里使用的最多的两个类是 osC_Language 和 osC_Template,因为模板是负责显示的,所 以需要切换语言(通过 osC_Language 类)以及得到需要的内容(通过 osC_Template 类)。

osC_Template 类的 getPageTitle 方法是取得页面标题,代码如下:

osC_Template 类文件 includes/classes/template.php[275]

function getPageTitle() {
  return osc_output_string_protected($this->_page_title); // 还记得在主模块文件 index.php 里已经设置了此内部变量的值吗?
}

方法hasPageTags和getPageTags负责管理Meta Tags,一个是判断是否存在Tags,另一个是得到 Tags。同样 hasJavascript 方法和 getJavascript 方法负责管理需要附加的 Javascript 文件。

提示:

Meta Tags 由 osC_Template 类的内部变量$_page_tags 保存,它是一个关联数组,Key 指定了 Meta 的名称,Value 指定 Meta 的值 Javascript 文 件 是 通 过 osC_Template 类 的 内 部 变 量 $_javascript_filenames 保 存 , $_javascript_filenames 是一个数组,所以可以在一个文件里引入多个 Javascript 文件

显示主模块内容 templates/default.php [80]

if ($osC_Template->getCode() == DEFAULT_TEMPLATE) { //可能是出于执行性能的考虑,才有这样的判断,其实直接使用下面 else 的语言就可以实现加载主模块内容的任务
  include('templates/' . $osC_Template->getCode() . '/content/' . $osC_Template->getGroup() . '/' . $osC_Template->getPageContentsFilename());

  //getGroup 方法返回 osC_Template 类的内部变量 $_group,它的值是在主模块里已经定义的(如 osC_Index_Index 的$_group=’index’), getPageContentsFilename 方法便是返回内部变量$_page_contents,此值在主模块里设置,决定了最终 的结果,前文已经讲过。
} else {
  if (file_exists('templates/' . $osC_Template->getCode() . '/content/' . $osC_Template->getGroup() . '/' . $osC_Template->getPageContentsFilename())) {
    include('templates/' . $osC_Template->getCode() . '/content/' . $osC_Template->getGroup() . '/' . $osC_Template->getPageContentsFilename());
  } else {
    include('templates/' . DEFAULT_TEMPLATE . '/content/' . $osC_Template->getGroup() . '/' . $osC_Template->getPageContentsFilename());
  }
}

提示:

在前面讲到的主模块 index.php 可能会返回三种结果(通过设置内部变量$_page_contents),下面 是三种情况的介绍:

返回值 调用文件路径 说明
index.php templates/TEMPLATE_CODE/content/index/index.php 首页的显示
category_listing.php templates/TEMPLATE_CODE/content/index/category_listing.php 无直属产品的分类 将显示所有下属分类
product_listing.php templates/TEMPLATE_CODE/content/index/product_listing.php 有产品的分类将显 示所有产品

results matching ""

    No results matching ""