首页 > 程序开发 > 综合编程 > 其他综合 >

Opencart使用学习

2016-07-06

Opencart主要是做外贸电商的,跨境电商之类的,使用的是PHP语言。 OpenCart基于PHP+MYSQL开发,支持无限分类、无限产品、多主题、多种币种、多语言、购买评论、产品点评、搜索引擎优化、在线下单支付、内置多种支付接口等完善的电子商务功能,是国外著名的B2C电子商务系统。

公司搞跨境电商,使用Opencart,好好整理了一下

Opencart

Opencart主要是做外贸电商的,跨境电商之类的,使用的是PHP语言。
OpenCart基于PHP+MYSQL开发,支持无限分类、无限产品、多主题、多种币种、多语言、购买评论、产品点评、搜索引擎优化、在线下单支付、内置多种支付接口等完善的电子商务功能,是国外著名的B2C电子商务系统

界面简洁、友好、符合欧美人使用习惯、是最适合国人建外贸网站的程序之一。(同类型的B2C开源程序国内有Ecshop、ZenCart,国外还有Magento)

安装配置

环境要求:
PHP 5.3+(开启cURL、GD、mCrypt扩展)
MySQL 5.1+
Nginx / Apache(开启mod_rewrite重写模块)

安装流程:
OpenCart最新版本为 2.0.3,可从官网下载,下载地址http://www.opencart.com/index.php?route=download/download,下载最新版本后解压缩到网站根目录,通过浏览器访问首页,会自动进入安装过程。按照提示输入数据库配置信息设置后台密码直接安装完成。安装成功后,安全起见需移除install目录,访问http://your-site/admin即可进入管理后台。

注意事项:
安装前除需开启所需的PHP扩展、Apache服务器扩展,还需将以下目录或文件设置为可写。
system/cache 系统缓存目录
system/logs 系统日志目录 debug::log()将会用到
system/download 下载文件存放位置
system/upload 系统默认上传目录
system/modification 系统程序缓存目录
image/cache 缩略图缓存目录
admin/config.php 管理员配置文件
config.php 前台配置文件
以下为comprame需设为可写的目录或文件
hbsitemaps 谷歌地图生成存放目录
journal-cache Journal2 静态资源缓存目录
vqmod/logs vqmod日志目录
vqmod/vqcache vqmod缓存目录
vqmod/checked.cache vqmod用于保存已缓存的文件名
vqmod/mods.cache vqmod用于保存缓存文件的序列化源码
merchenta-product-feeds.txt 产品feeds list生成文件
product-feeds.txt 产品feeds list 生成文件

设为可写的方法:
*inux系统可终端执行chmod–R 777 path/to命令完成此操作。

结构

表结构

概述对比:
OC官方安装后数据表会有123张,现comprame已有数据表190张。OC默认的数据是以MyISAM为存储引擎,不支持事物和回滚,时间字段的数据类型为datetime类型。从查询读写效率上对比,MyISAM比Innodb快很多倍,但MyISAM并不支持事物和回滚,所以今后创建新数据表都默认使用Innodb引擎。

主要表的前缀:
category_* 产品分类相关表
coupon_* 优惠券相关的表
customer_* 客户相关的表
emailtempate_* 邮件模板相关表
journal2_* journal2插件相关表
order_* 订单数据相关表
product_* 产品数据相关表

文件目录结构

/admin 后台MVC程序目录
/catalog 前台MVC程序目录
/hbsitemaps Google地图生存存放目录
/image 图片资源目录
/image/cache 图片缩略图缓存目录
/media 静态资源目录
/sql/sql.txt 每次发版本所需执行的SQL存放
/system 系统公用类库程序脚本
/system/journal2 journal2插件程序
/system/modification modification缓存目录
/index.php 前台入口文件
/.htaccess Apache 重写配置文件
/d_social_login.php 外部登录文件
/robots.txt 搜索引擎机器人文件

URL重写及路由

URL规则:
RewriteCond%{REQUEST_FILENAME} !-f
RewriteCond%{REQUEST_FILENAME} !-d
RewriteCond%{REQUEST_URI} !.*.(ico|gif|jpg|jpeg|png|js|css)
RewriteRule^([^?]*) index.php?route=$1 [L,QSA]
OC的产品详情页、分类页面都是重写伪静态过后的网址,产品的详情页的网址在后台添加修改产品表单的 General -> SEO Keyword中设置。

直接指定:
除重写外,OC可通过route参数来指定需要访问的MVC路由。例如访问注册动作,MVC动作为 account/register,用户可通过local.comprame.com/index.php?route=account/register直接访问。参数传递示例:local.comprame.com/index.php?route=account/register¶m1=val1&..

重写:
运行opencart的环境必须支持重写,opencart默认推荐Apache服务器,将Apache服务器的mod_rewrite模块开启后,将网站配置directory指令中的AllowOverride设置为All,即可。如果使用nginx服务器,可在location指令中添加
if(!-e request_filename){
rewrite^/(.*) /index.php?_route_=$1 last;
break;
}
重启nginx即可。

程序运行流程

请求URL重写到入口文件

通过重写,将请求地址转发到入口文件,一般为index.php,入口文件可从请求参数 route 获得当前请求网址。如果是直接访问入口文件,通过route参数可指定对应的请求路由。

入口文件加载所类库并实例化

执行入口文件,会先引入配置文件,再通过是否定义 !defined(‘DIR_APPLICATION’)判断是否已安装,如果未定义此常量,则转到安装。
之后,入口文件内会引入所需的类、扩展、并实例化所需的类,包括链接数据库、实例化registry、loader、config、request、response、session、customer、controller……

绑定Action动作

执行动作会预先绑定new Action(‘common/seo_url’)动作,用于解析请求的route参数。

开始SEO插件检查并且指派动作Action

实例化控制器后,会开始执行请求的动作controller?>dispatch(action, new Action(‘error/not_found’));计算出真正的请求路由,并引入该控制器实例化执行动作,执行完毕后会将内注册加到$response对象。

执行结果返回输出

通过$response->output();格式化内容进行输出

控制器 Controller

存放位置:
OC的控制器程序文件统一存放在 DIR_APPLICATION . ‘controller/’ 目录下。默认的即前台控制器都是放在 /catalog/controller目录下,后台都放在 /admin/controller。

类名规则:
控制器类名以Controller开头,继承自Controller父类。类名的与route路由对应原则:
this?>class=′Controller′.pregreplace(′/[a?zA?Z0?9]/′,′′,path);
所以
示例1:
请求URL:index.php?route= account/register_promotion
对应文件:/catalog/controller/account/register_promotion.php
执行类名:ControllerAccountRegisterPromotion
示例2:
请求URL:index.php?route=test/account/register_promotion
对应文件:/catalog/controller/test/account/register_promotion.php
执行类名:ControllerTestAccountRegisterPromotion

注意信息:
PHP中函数名、类名、方法名是不区分大小写,但为了阅读方便和规范,类名书写应采用驼峰命名方式。

动作Action

动作Action即控制器的成员方法,命名英文字母或下划线开头英文字母数字下划线组成。请求时动作名附带在route参数中,如果未指定,默认动作为 index,注意请求访问以双下划线开头的方法不会被调用。

动作的参数接受:
对于直接请求的动作,可通过网址传递参数,接受可在动作内通过如下方式,先判定是否设置再接收。
if(isset(this->request->get[‘product_id’])){product_id= (int)this->request->get[‘product_id’];
}else{product_id= 0;
}

控制器的动作可直接调用前台其他控制器的动作,参数的传递可通过如下方式进行
传递:
$this->load->controller(‘catalog/feeds/build’,1);

接收:
public function build($notSetSession= false){
}

模型Model

模型文件存放在DIR_APPLICATION . ‘model/’目录下,模型的寻找如下:
file=DIRAPPLICATION.′model/′.model .’.php’;
class=′Model′.pregreplace(′/[a?zA?Z0?9]/′,′′,model);

在Action中引入和调用示例:
this?>load?>model(‘catalog/product′);this->model_catalog_product->getProducts();

视图View

视图文件可任意扩展名结尾,统一存放在DIR_TEMPLATE目录下,默认为.tpl。
在Action的末尾,可通过如下代码实现视图的渲染,并注册到response对象以用于入口文件输出。
this?>response?>setOutput(this->load->view(‘catalog/product_list.tpl’, $data));

插件

VQMOD

vQmod全称是 Virtual File Modification System 又称快速虚拟MOD)是一个虚拟覆盖系统的设计,以避免原有系统核心文件被修改。这个概念是很简单,它通过创建XML搜索/替换脚本文件,而不是直接更改核心文件。这些脚本文件是在页面加载解析为每个“源”核心文件“包括”或“规定”的php函数加载和资源,然后打补丁的脚本文件的变化,并保存到一个临时文件,然后在执行临时文件,在过程中取代了原来的文件。原来的源文件是永远不会改变。这将实现一个“虚拟”的变化中,在没有任何实际修改的核心文件中执行想要的过程和结果。

执行原理:PHP搜索替换。(通过XML读取vqmod/xml下的xml文件,逐个配置内指定的文件进行替换对应的代码,缓存到vqmod/vqcache下生成对应的缓存文件)

相关文件:

缺点:如果改动了要替换的文件的源码,可导致搜索不到对应文字而不替换。
刷新:可通过清空vqmod/checked.cache及vqmod/mods.cache内容强制vqmod重新生成缓存。

SEO插件

用途作用:后台管理操作管理oc_url_alias的数据。
后台位置:Extension -> Modules ->Complete SEO Package

邮件模板Emailtemplate

用途作用:可后台配置的邮件模板,用于格式化发送的邮件内容。
用法示例:
template=newEmailTemplate(this->request, this?>registry);template->addData(this?>request?>post);template->data['password'] = password;template->data['account_login'] = this->url->link(‘account/login’,‘&email=’.this->request->post[‘email’], ‘SSL’);
template?>data[′accountlogintracking′]=template->getTracking($template->data[‘account_login’]);

mail=newMail();mail->useExternal= this?>config?>get(‘configmailexternal′);mail->setRegistry(this?>registry);mail->protocol = this?>config?>get(‘configmailprotocol′);mail->parameter = this?>config?>get(‘configmailparameter′);mail->smtp_hostname= this?>config?>get(‘configmailsmtphostname′);mail->smtp_username= this?>config?>get(‘configmailsmtpusername′);mail->smtp_password= html_entity_decode(this?>config?>get(‘configmailsmtppassword′),ENTQUOTES,‘UTF?8′);mail->smtp_port= this?>config?>get(‘configmailsmtpport′);mail->smtp_timeout= $this->config->get(‘config_mail_smtp_timeout’);

mail?>setTo(this->request->post[‘email’]);
mail?>setFrom(this->config->get(‘config_email’));
mail?>setSender(htmlentitydecode(this->config->get(‘config_name’), ENT_QUOTES, ‘UTF-8’));
mail?>setSubject(subject);
mail?>setText(message);
template?>load(‘customer.forgotten′);mail = template?>hook(mail);
mail?>send();template->sent();

Journal2 插件

OC的一款主题模板主题,可最大化的在后台配置前台的模板显示。Journal2 可通过PHP判断设备尺寸,再渲染不同的HTML以自动适配。Journal2插件会在journal-cache下生成JS CSS的静态资源,因此此目录需要可写。

交易及支付流程(TBD)

交易流程 PayULatam支付 PayPal 快速付款 PayPal 标准支付 线下付款及流程

专题

参数接受

在OC中,POST、_GET、FILES、_REQUEST、SERVER、_COOKIE都已过滤后封装在 $this->request对象中,如需要再控制器动作中接受,可通先判断是否isset再传值。

过滤方式:
data=htmlspecialchars(data, ENT_COMPAT, ‘UTF-8’);

参考代码:
if(isset(this->request->get[‘order’])){url.='&order=' . $this->request->get[‘order’];
}

多语言支持(西语、英语、汉语)

语言的设置:
comprame前台默认的是西班牙语,后台使用的是英语。如需更改,可在增加语言文件后再在后台进行设置。后台位置 System -> Settings -> Store -> Local,Language、Administrator Language,在控制器动作中可通过config?>get(‘configlanguage′)获得前台语言文件ID,config->get(‘config_admin_language’)
获得后台语言文件ID。

翻译的使用:
先载入对应的语言文件,再通过this?>language?>get对象即可,示例如下:this->load->language(‘catalog/product’);
data[′success′]=this->language->get(‘text_success’);

注意的事项:
1) 由于comprame已经使用了journal2 ,开启多语言支持会导致journal2主题不会引入对应的设置。所以不能开启多语言支持。
原因源码:

#D:\website\latin\src\catalog\view\theme\journal2\template\common\language.tpl
#common/header
$data['language'] = $this->load->controller('common/language');

动作Action调用

在Action动作中,可相互调用同端的动作Action(前端的控制器中只能调用前端的,后端调用后端的),实现方式如下:
data[′currency′]=this->load->controller(‘common/currency’);

调用时参数的传递:
this?>load?>controller(′payment/ppexpress′,varArray);

调用时参数的接受:
public function index() {
$varArray= func_get_args(); //获取传递的参数
}

Event事件触发

示例代码:
this?>event?>trigger(′pre.admin.attribute.add′,data);
$data为传递进入的参数,默认为空。涉及表oc_event

#event执行原理
public function trigger($key, &$arg= array()) {
if (isset($this->data[$key])) {
foreach($this->data[$key] as $event) {
$action = new Action($event, $arg);
$action->execute($this->registry);
      }
   }
}

SQL过滤

SQL的过滤,主要是转换接受参数为int类型或string类型,使用显性类型强制转换。
示例int转换:
this?>db?>query("DELETEFROM".DBPREFIX."attributeWHEREattributeid=′".(int)attribute_id.”’”);

示例string过滤:
sql="ANDnameLIKE′this->db->escape($data[‘filter_name’]).”%’”;

SQL宽字符注入:
PHP中因为字符集问题可引起SQL宽字符chr(0x5c) chr(0x27) chr(0xbf)
注入,通过字符集强制转换为UTF-8,可防止。

在OC中,this?>request对象clean方法中已经过滤并有字符集转换。data= htmlspecialchars($data, ENT_COMPAT, ‘UTF-8’);

所以针对传入需进行SQL拼接参数,必须从$this->request中获取!

货币转换

comprame开启支持3种货币,分别是美元USD、比索COP、墨西哥元MXN,前台默认的货币显示为COP(哥伦比亚币),后台员添加和修改产品时,所用的单位都是USD(美元)。
所以在前台显示商品价格,涉及货币转换,转换代码可参考如下:

this?>currency?>convert(value, $this->config->get(‘config_currency’), ‘USD’);

config_currency为后台设置的默认货币。

货币格式化输出:
this?>currency?>format(money, ‘COP’);

注意事项:
COP哥伦比亚比索,其小数点使用的是逗号,而千分是使用的点号。

汇率的更新:
汇率是后台人员手动设置,位置System ->Localisation -> Currencies,涉及表oc_currency。

文件上传

后台通过common/filemanager/upload 进行上传,上传后保存在catalog目录下,默认为image。
// Make sure we have the correct directory
if (isset(this->request->get[‘directory’])){directory = rtrim(DIR_IMAGE . 'catalog/' . str_replace(array('../', '..\\', '..'), '', this->request->get[‘directory’]),‘/’);
}else{directory = DIR_IMAGE . ‘catalog’;
}

对于上传的文件,限制在如下4中mime类型的文件
// Allowed file mime types
$allowed = array(
‘image/jpeg’,
‘image/pjpeg’,
‘image/png’,
‘image/x-png’,
‘image/gif’
);

SESSION操作

在入口文件index.php,SESSION对象已经初始化后注册到了registry对象中。session= new Session();
registry?>set(′session′,session);

在动作中,可通过如下方式对SESSION进行操作:
//检测
isset($this->session->data[‘success’])

//赋值
this?>session?>data[′success′]=this->language->get(‘text_success’);

//销毁
unset($this->session->data[‘success’]);

COOKIE操作

//读取
if(isset(this->request->cookie[‘tracking’])){order_data['tracking'] = $this->request->cookie[‘tracking’];
}

//COOKIE的设置还是通过默认的setcookie进行操作
setcookie(‘language’, code,time()+86400?30,′/′,request->server[‘HTTP_HOST’]);

跳转及重定向

在Action动作中通过response对象进行重定向
this?>response?>redirect(this->url->link(‘catalog/attribute’, ‘token=’ . this?>session?>data[′token′].url, ‘SSL’));

后台通信前台方法

原理:通过CURL请求前台api/login接口,使用api表的用户密码数据录到前台,再CURL请求前台要执行的接口。

例子:common/dashboardapiRequest
public function apiRequest($url, $data = array())
{
$json= array();

$this->load->language('sale/order');
$this->load->model('sale/order');
$this->load->model('user/api');

unset($this->session->data['cookie']);

$api_info= $this->model_user_api->getApi($this->config->get('config_api_id'));

if ($api_info) {
$curl = curl_init();
if (substr(HTTPS_CATALOG, 0, 5) == 'https') {
curl_setopt($curl, CURLOPT_PORT, 443);
      }

curl_setopt($curl, CURLOPT_HEADER, false);
curl_setopt($curl, CURLINFO_HEADER_OUT, true);
curl_setopt($curl, CURLOPT_USERAGENT, $this->request->server['HTTP_USER_AGENT']);
curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($curl, CURLOPT_FORBID_REUSE, false);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl, CURLOPT_URL, HTTPS_CATALOG.'index.php?route=api/login');
curl_setopt($curl, CURLOPT_POST, true);
curl_setopt($curl, CURLOPT_POSTFIELDS, http_build_query($api_info));

$result = curl_exec($curl);

if (!$result) {
$json['error'] = sprintf($this->language->get('error_curl'), curl_error($curl), curl_errno($curl));
      } else {
$response = json_decode($result, true);

if (isset($response['cookie'])) {
$this->session->data['cookie'] = $response['cookie'];
         }

curl_close($curl);
      }
   } else {
$json['error'] = $this->language->get('error_action');
   }

if (isset($this->session->data['cookie'])) {
$curl = curl_init();

if (substr($url, 0, 5) == 'https') {
curl_setopt($curl, CURLOPT_PORT, 443);
      }

curl_setopt($curl, CURLOPT_HEADER, false);
curl_setopt($curl, CURLINFO_HEADER_OUT, true);
curl_setopt($curl, CURLOPT_USERAGENT, $this->request->server['HTTP_USER_AGENT']);
curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($curl, CURLOPT_FORBID_REUSE, false);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl, CURLOPT_URL, HTTPS_CATALOG.$url);

if ($data) {
curl_setopt($curl, CURLOPT_POST, true);
curl_setopt($curl, CURLOPT_POSTFIELDS, http_build_query($data));
      }

curl_setopt($curl, CURLOPT_COOKIE, session_name().'='.$this->session->data['cookie'].';');

$result = curl_exec($curl);

if (!$result) {
$json['error'] = sprintf($this->language->get('error_curl'), curl_error($curl), curl_errno($curl));
      } else {
$json= (array)json_decode($result);
      }

curl_close($curl);       
   } else {
$json['error'] = $this->language->get('error_action');
   }

return $json;
}  

数据分页Pagination

$pagination = new Pagination();
$pagination->total = $affiliate_total;
$pagination->page = $page;
$pagination->limit = $this->config->get('config_limit_admin');
$pagination->url= $this->url->link('marketing/affiliate', 'token=' . $this->session->data['token'] . $url.'&page={page}', 'SSL');

$data['pagination'] = $pagination->render();

图片缩略图生成

原理,检测对应尺寸的缩略图文件是否存在,如果不存在或者文件创建时间小于原图创建时间,重新生成对应尺寸的图片,并保存到DIR_IMAGE目录中。

调用源码:

$this->load->model('tool/image');
if($product['image']){
$image = $this->model_tool_image->resize($product['image'],
    $this->config->get('config_image_cart_width'),
    $this->config->get('config_image_cart_height'));
}else{
$image = $this->model_tool_image->resize('no_image.png',
    $this->config->get('config_image_cart_width'),
    $this->config->get('config_image_cart_height'));
}

备注如果没有缩略图,可使用no_image.png生成。
data[′placeholder′]=this->model_tool_image->resize(‘no_image.png’, 100, 100);

后台对应设置图片尺寸:System -> Settings ->Store -> Image
config_image_cart_width 购物车产品缩略图大小
config_image_thumb_width 产品缩略图大小,包括手机端
config_image_popup_width 产品弹出图片大小
config_image_product_width 产品列表图片大小

OC的模板

控制器动作中调用模板的代码:
this?>response?>setOutput(this->load->view(‘catalog/category_list.tpl’, $data));

OC模板为原生PHP,无额外模板解析,模板文件夹扩展名默认为*.tpl,存放在DIR_TEMPLATE
目录中。模板解析后的内容通过PHP 的ob_get_content读取缓冲内容返回到response对象中。

public function view($template, $data = array()) {
$file = DIR_TEMPLATE . $template;
if (file_exists($file)) {
extract($data);
ob_start();
require($file);
$output = ob_get_contents();
ob_end_clean();
return $output;
   } else {
trigger_error('Error: Could not load template ' . $file .'!');
exit();
   }
}
相关文章
最新文章
热点推荐