公司搞跨境电商,使用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规则:
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即可。
通过重写,将请求地址转发到入口文件,一般为index.php,入口文件可从请求参数 route 获得当前请求网址。如果是直接访问入口文件,通过route参数可指定对应的请求路由。
执行入口文件,会先引入配置文件,再通过是否定义 !defined(‘DIR_APPLICATION’)判断是否已安装,如果未定义此常量,则转到安装。
之后,入口文件内会引入所需的类、扩展、并实例化所需的类,包括链接数据库、实例化registry、loader、config、request、response、session、customer、controller……
执行动作会预先绑定new Action(‘common/seo_url’)动作,用于解析请求的route参数。
实例化控制器后,会开始执行请求的动作 controller−>dispatch( action, new Action(‘error/not_found’));计算出真正的请求路由,并引入该控制器实例化执行动作,执行完毕后会将内注册加到$response对象。
通过$response->output();格式化内容进行输出
存放位置:
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即控制器的成员方法,命名英文字母或下划线开头英文字母数字下划线组成。请求时动作名附带在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){
}
模型文件存放在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();
视图文件可任意扩展名结尾,统一存放在DIR_TEMPLATE目录下,默认为.tpl。
在Action的末尾,可通过如下代码实现视图的渲染,并注册到response对象以用于入口文件输出。
this−>response−>setOutput( this->load->view(‘catalog/product_list.tpl’, $data));
vQmod全称是 Virtual File Modification System 又称快速虚拟MOD)是一个虚拟覆盖系统的设计,以避免原有系统核心文件被修改。这个概念是很简单,它通过创建XML搜索/替换脚本文件,而不是直接更改核心文件。这些脚本文件是在页面加载解析为每个“源”核心文件“包括”或“规定”的php函数加载和资源,然后打补丁的脚本文件的变化,并保存到一个临时文件,然后在执行临时文件,在过程中取代了原来的文件。原来的源文件是永远不会改变。这将实现一个“虚拟”的变化中,在没有任何实际修改的核心文件中执行想要的过程和结果。
执行原理:PHP搜索替换。(通过XML读取vqmod/xml下的xml文件,逐个配置内指定的文件进行替换对应的代码,缓存到vqmod/vqcache下生成对应的缓存文件)
相关文件:
缺点:如果改动了要替换的文件的源码,可导致搜索不到对应文字而不替换。
刷新:可通过清空vqmod/checked.cache及vqmod/mods.cache内容强制vqmod重新生成缓存。
用途作用:后台管理操作管理oc_url_alias的数据。
后台位置:Extension -> Modules ->Complete SEO Package
用途作用:可后台配置的邮件模板,用于格式化发送的邮件内容。
用法示例:
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();
OC的一款主题模板主题,可最大化的在后台配置前台的模板显示。Journal2 可通过PHP判断设备尺寸,再渲染不同的HTML以自动适配。Journal2插件会在journal-cache下生成JS CSS的静态资源,因此此目录需要可写。
在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(前端的控制器中只能调用前端的,后端调用后端的),实现方式如下:
data[′currency′]= this->load->controller(‘common/currency’);
调用时参数的传递:
this−>load−>controller(′payment/ppexpress′, varArray);
调用时参数的接受:
public function index() {
$varArray= func_get_args(); //获取传递的参数
}
示例代码:
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的过滤,主要是转换接受参数为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’
);
在入口文件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’]);
//读取
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 = 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 产品列表图片大小
控制器动作中调用模板的代码:
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();
}
}