收藏 分销(赏)

ECMall 2.X 架构分析与技术指南.doc

上传人:xrp****65 文档编号:7421883 上传时间:2025-01-03 格式:DOC 页数:25 大小:1.33MB 下载积分:10 金币
下载 相关 举报
ECMall 2.X 架构分析与技术指南.doc_第1页
第1页 / 共25页
ECMall 2.X 架构分析与技术指南.doc_第2页
第2页 / 共25页


点击查看更多>>
资源描述
ECMall 2.X 架构分析与技术指南 ECMall 2.X 架构分析与技术指南 一、系统架构分析: 根据文件目录结构分析得到ECMall为MVC系统架构。 控制器(Controllers)分析: 在ECMall中分2种控制器,FrontendApp(前端)和BackendApp(后端),我们可以依据基础控制器创建自己的控制器。 控制器文件的命名规则:类名(首字母大写) + .app + .php; 文件位置:一般放置在相应的app目录中; 类名规则:类名+App 访问规则:/index.php?app=控制器类&act=方法&param=… 注意:第一个控制器,必须拥有自己的语言文件,如MallBaseApp的子类语言文件位于\languages\sc-gbk\下,而模块Module类的语言文件则位于模块的文件夹中,即使语言文件中仅有return array()的空数组。若没有相应的语言文件,程序将会报错。 现在我们试着写一个自己的控制器: <?php /** * 控制器演示类,此处采用单入口模式,方法与模型对应; * 与ECMall本身的多入吕,控制器模型对应有所区别; * @author CTO */ class Demo1App extends MallbaseApp{ /** * 默认控制器方法 * @return void */ Function index(){ Echo __METHOD__; } /** * Goods方法对应Goods模型 * @return void */ function goods(){ //实例化goods $goods = m(‘goods’); $id = empty($_GET[‘id’]) ? 0 : intval($_GET[‘id’]); if(!$id){ echo “Warning! Hacking!”; return; } //获取goods信息 $goods_info = $goods->get_info($id); //输出goods信息 $result = print_r($goods_info); } //此处的test自定义模型将在下面讲述,暂时略过; function test(){ $test = m(‘test’); $data = array(‘name’=>’建航’,”name2”=>”科技”);//name,name2对应表字段 $test->addData($data); } } 测试访问:/index.php?app=demo1&goods&id=3 模型(Model)分析: ECMall模型分为业务模型与普通模型,二者皆继承自核心类的BaseModel模型; 模型、插件的继承关系 模型:可以理解为数据实体类,对应数据库表字段,一个实体表示一张表,每个实例为一行记录。ECMall的大部分模型与表有对应关系,仅业务模型没有表联系(操作业务如CRUD)。 创建自己的模型: 模型文件的命名规则:类名(首字母大写)+.model+.php; 文件位置:一般放置在相应的/includes/models目录中; 类名规则:类名+Model 调用规则:可能选用控制器对应模型的多入口方式 ,也可以选用控制器对应模型的单入口方式。 首先建立一张表Test(id,name,name2); 编写test.model.php,代码如下: <?php class TestModel extends BaseModel{ /** * $table为表映射,$prikey为映射表的主键,$alias为表查询时的别名,主要体现在SQL语句里,$_name为模型的名称,这四个值都与ECMall模型数据库操作(很独到的一种操作方式,非通用的Adodb+SQL的方式)有关,如果不想用SQL语句查询的话,而是使用模型自身提供的数据操作功能,那么至少要把$table,$prikey体现出来;至于$alias与$_name的存在与否并不重要。只有类似于我们需要得到模型名称时,才会将$_name进行实现,例如:function getName(){return $this->_name;}。 */ /** * 增加数据演示 * @author kichijyo; * @return void */ function addData($data){ $this->add($data);//该处的add()继承自BaseModel类 } } 测试访问:/index.php?app=demo1&act=test 下面我们试着建一对主、从表的模型关系: 首先我们建2张表,一个是ecm_test从来(id,stu_id相对于ecm_test2表的外键,name),另一个为ecm_test2主表(stu_id相对于ecm_test2的主键,score)。根据约束,先删除从表,方可继续删除主表。对照这两张表开始建立模型,分别为:TestModel,Test2Model两个类: 首先我们分析下\eccore\model\model.base.php文件中的_getJoinString()方法中的几个case条件: switch($relation_info[‘type’]){ case HAS_ONE: //一对一关系 break; case BELONGS_TO: //一对多的从属关系 break; case HAS_AND_BELONGS_TO_MANY: //多对多的从属关系 break; } 我们注意到,仅有3个可用,分别为HAS_ONE,BELONGS_TO,HAS_AND_BELONGS_TO_MANY,意味着HAS_MANY不可以用来做多表查询。 下面着重讲述一下\eccore\model\model.base.php: define(‘HAS_ONE’,1); //一对一关联 define(‘BELONGS_TO’,2); //属于关联 define(‘HAS_MANY’,3); //一对多关联 define(‘HAS_AND_BELONGS_TO_MANY’,4); //多对多关联 多表查询中存在的左右关系整理如下(√表示实体模型是否可用来操作多表查询): 包含 从属 HAS_ONE √ BELONGS_TO √ HAS_MANY BELONGS_TO √ HAS_AND_BELONGS_TO_MANY √ HAS_AND_BELONGS_TO_MANY √ 虽然从HAS_ONE与BELONGS_TO两种方式执行操作多表查询的left join 不同,但是之后一经加入conditions即where条件过滤后,两个的结果完全相同了。 例如: 1、 一个人有一个身份证号,我们称一个人HAS_ONE(既有)身份证号,反过来一个身份证号属于一个人所有,我们称一个身份证号BELONGS_TO(从属反向)一个人。(一对一关系); 2、 一个班级有多个学生,我们称一个班级HAS_MANY(既有)学生,一个学生只属于一个班级,我们称一个学生BELONGS_TO(从属反向)一个班级。(一对多关系); 3、 一个学生可以选择多门课程,反过来一个课程可以被多个学生选择,我们对这两个实体模型皆用HAS_AND_BELONGS_TO_MANY。(多对多关系,既有又从属反向); 关于这三种关系,模型中涉及几个重要的字段: “model”=>”指明对应的模型名称”, “type”=>”指明当前模型与对应模型的关系是三种关系中的哪一种?”, “ext_limit”=>”又称联合限制ext(扩展的意思),本质为left join … on … and XXX”扩展的即是这个语句的XXX内容”, “foreign_key”=>”对应模型中的外键”, “refer_key”=>”当前模型中的主键,若与对应模型外键同名,可省略不写”, “reverse”=>”当前模型相对于对应模型关系为BELONGS_TO的从属关系时需要填写这个反向指代关系,值为对应关系的数组key值”, “middle_table”=>”当实体模型关系出现多对多时才会出现的中间关系表” Okay,我们开始模型设计: 1、 主表模型: class Test2Model extends BaseModel{ var $table = ”test2”; var $prikey = “stu_id”; //这里同样对应着写,Test2对应test之后写test表中相对于Test2的外键与reference var $_relation = array( “has_test” => array( “model”=>”test”, “type”=>HAS_MANY, “foreign_key”=>”stu_id”, “refer_key”=>”stu_id” ) ); } 2、 从表模型: class TestModel extends BaseModel{ var $table = “test”; var $prikey = “id”; var $_relation = array( //关于关系,这里按对应来写,像test对应test2,而且test为从表,故belongs_to test2表 //reverse 反向关系到test上 “belongs_to_test2” => array( “model”=>”test2”, “type”=>BELONGS_TO, “revers”=”has_test” ) ); } 3、 应用: class Jiang3App extends MallbaseApp{ function index(){ //因为仅有3个可用,分别为HAS_ONE,BELONGS_TO,HAS_AND_BELONGS_TO_MANY //故关系上选取拥有type为以上三个值的模型。 $test = m(“test”); $sql = array( ‘join’=>’belongs_to_test2’, ‘conditions’=>’test.stu_id’ ); $arr = $test->find($sql); print_r($arr); } } 4、 多对多应用: Course.model.php class CourseModel extends BaseModel{ var $table = “course”; var $prikey = “id”; var $alias = “cour”; var $_name = “course”; var $_relation = array( “model”=>”student”, “type”=>HAS_AND_BELONGS_TO_MANY, “middle_table”=>”stu_course”, “foreign_key”=>”course_id”, “refer_key”=>”id”, “reverse”=>”learn_course” ); } Student.model.php class StudentModel extends BaseModel{ var $table = “stu”; var $prikey = “id”; var $alias = “stu”; var $_name = “student”; var $_relation = array( “learn_course”=> array( “model”=>”course”, “type”=>HAS_AND_BELONGS_TO_MANY, “middle_table”=>”stu_course”, “foreign_key”=>”stu_id”, “refer_key”=>”id”, “reverse”=>”join_student” ) ); } 应用: class Jiang3App extends MallbaseApp{ function index(){ $course = m(“course”); $sql = array( ‘join’=>’join_student’, ‘conditions’=>’cour.id=2’, ‘fields’=>’stu_course.stu_id’ ); $arr = $course->find($sql); print_r($arr); } } 5、 模型验证: 每个模型映射表结构,那么必然有相关的约束验证,模型中的$_autov(自动验证auto validate),此处的自动验证包括类如:required(是否必需),reg(正则验证)等内容。 下面我们来看一个典型的验证模型: 一个典型的表对应模型: class TestModel extends BaseModel{ var $table = “test”; var $prikey = “id”; var $_autov = array( “title”=>array( “required”=>true, “filter”=>”trim” ), “description”=>array( “required”=>true, “filter”=>”trim” ) ); var $_relation = array( “has_test2”=>array( “model”=>”test2”, “type”=>HAS_MANY, “foreign_key”=>”pid”, “refer_key”=>”id” ) ); } 从上面我们看到有$table,$prikey,$_autov,$_relation这4个部分,扩展可以加上$_name,$_alias等; 【延伸阅读】 刚刚在上面讲述了join语句,这里有个地方需要大家注意: 数据库在通过连接两张表或多张表来返回记录时,都会生成一张中间表,然后再将这张临时表返回给用户。 在使用left join 时,on和where条件的区别: 例如: 两条SQL语句: 1、 select * from tab1 left join tab2 on (tab1.size = tab2.size) where tab2.name=’AAA’ 2、 select * from tab1 left join tab2 on (tab1.size = tab2.size and tab2.name=’AAA’) 假设有两张表: Id Size 1 10 2 20 3 30 表tab1 size name 10 AAA 20 BBB 30 CCC 表tab2 第一条SQL的过程: 1、 先生成中间表 on 条件:tab1.size=tab2.size Tab1.id Tab1.size Tab2.size Tab2.name 1 10 10 AAA 2 20 20 BBB 3 30 30 CCC 4 40 40 DDD 临时中间表 2、 再对中间表过滤where条件:tab2.name=’AAA’ Tab1.id Tab1.size Tab2.size Tab2.name 1 10 10 AAA 最终数据表 结论:where条件是在临时表生成好后,再对临时表进行过滤的条件,这时已经没有left join的含义(必须返回左边表的记录)了,条件不为真的全部过滤掉了。 第二条SQL的过程: 1、 中间表on条件:tab1.size = tab2.size and tab2.name=’AAA’ Tab1.id Tab1.size Tab2.size Tab2.name 1 10 10 AAA 2 20 Null Null 3 30 Null Null 最终数据表 结论:on条件是在生成临时表时使用的条件,它不管on中的条件是否为真,都会返回左边表中的记录。 其实以上结果的关键原因就是left join,right join,full join的特殊性,不管on上的条件是否为真都会返回left或right表中的记录,full则具有left和right的特性的并集。而inner join(也就是join的全称)没这个特殊性,则条件放在on中和where中,返回的结果集都是相同的。 三表(更多表联合查询)join语句: select * from ecm_stu_s join ecm_stu_course sc join ecm_course c on s.id=sc.stu_id and sc.course_id=c.id where s.id=3; 标注:ECMall目前暂不支持类似这样的三表链接查询语句。 视图模板(Views)分析: 首先让我们看一段商城首页的部分代码: {include file=header.html} <div class=”keyword”> <div class=”keyword1”></div> <div class=”keyword2”></div> {$lang.hot_search}: <!--{foreach from=$hot_keywords item=keyword}--> <a href=”{url app=search&keyword=$keyword}”>{$keyword}</a> <!--{/foreach}--> </div> <div class=”content”> <!--{widgets page=index area=top_left}--> </div> 整体代码类似于此,这里有2个部分需要我们分析: 1、{include}或{foreach}的Smarty语法,可以参考在、相关的Smarty资料; 2、{widgets}挂件开发; 让我们先来看下挂件的管理方法: 1、 进入2.2后台,在这里寻找挂件管理界面: 挂件管理界面 2、 将设计好的挂件放入模板中; 模板管理界面 拖“挂”动作,这就是所谓的“挂”件 在图中我们看到这块灰色的区域是我在\themes\mall\default\index.html文件中设置的,一般的我们会先设计出挂件将要拖入的区域,而后拖放挂件。 这块区域的代码如下: <div style=”height:200px; background-color:gray;” area=”mina” widget_type=”area”> </div> 注意两点:area与widget_type属性,其中area属性用来拖放挂件时给挂件配置文件指明从属方向,这里我们看下挂件拖放后形成的配置文件代码: 文件定位:\data\page_config里面有两个配置文件,default.gcategory.config.php(商品分类页)与default.index.config.php(商城首页),我们打开商城首页代码: 部分截取: return array( ‘widgets’=>array( ‘_widget_235’=>array( ‘name’=>’kichijyo’, ‘options’=>NULL) ), ‘_widget_877’=>array( ‘name’=>’notice’, ‘options’=>array( ‘ad_image_url’=>’data/files/mall/template/200908070207084061.gif’, ‘ad_link_url’=>’’) ), ) ); 上面是对每个挂件定义的ID值,继续向下滚动代码近于文档底部: ‘config’=> array( ‘mina’ => array(0=>’_widget_235’), ‘top_left’ => array(0=>’_widget_877’,1=>’_widget_578’,2=>’_widget_323’), ‘cycle_image’=> array(0=>’_widget_698’) ); 在这里可以看到,刚才在模板管理后台拖放挂件的时候,div标签中的area为上面这段配置代码指明了方向,就是说:{widgets page=index area=mina}里mina区域将包括ID为_widget_235这个的挂件,展示页面位于index上。{widgets page=index area=cycle_image}里cycle_image将包括ID为_widget_368的挂件,用于展示位于index的模板中。 既然系统都已经做好了配置文件,下面我们可以完善填充代码了,加入代码后开始: <div style=”height:200px;background-color:gray;” area=”mina” widget_type=’area’> {widgets page=index area=mina} </div> 3、 挂件开发: 3.1、数据调用形式概述: 3.1.1、通用调用(ADODB类) //通用Adodb数据调用描述;包含有直接返回数组的getone,getcol,getrow,getall,除query(返回资源,非数组) //这里我使用了通用的Query方法 $db = db(‘test’);//这里我们查询test表,注意没有表前缀 $sql = “select * from ecm_test”; //注意这里有表前缀 $quer = $db->query($sql); //这里和我们的PHP很类似,返回一个资源 $arr[] = array(); //创建一个空数组,用来存放数据 while($row=$db->fetchrow($query)){ array_push($arr,$row); } return $arr;//返回数组;下面准备进入widget模板做Smarty 上面的SQL可以替换为:insert,delete,update等相关常规语句; 3.1.2、内部模型调用(ECMall独有,需单独研究) 使用内部模型有两步骤:一、建实体类(或业务模型);二、数据调用,比通用麻烦点,但是后面省心哦。下面继续: 让我们来创建一个模型: class TestBModel extends BaseModel{ var $table = ‘test’; //映射表名,必须字段,注意无前缀; var $prikey = ‘id’;//主键定义,必须字段 function getData(){}//其它方法定义 } 实例化时注意:$test=bm(‘test’);注意用bm实例化; 这里我用的BModel(操作业务模型),我们从前面的模型继承图知道,BModel继承自BaseModel,当然这里如果不使用操作业务模型,仅仅使用与表有映射关系的普通模型也是可以的,比如: class TestModel extends BaseModel{ var $table = ‘test’; var $prikey = ‘id’; function getData(){}//其它方法定义 } 实例化时使用:$test=m(‘test’); //这里使用m来实例化; 模型建立完,我们开始调用模型,可以在诸如挂件、控制器等地方使用,代码如下: 以实体类为例: function index(){ $test = m(‘test’);//实例化test模型 //这里使用了模型中比较通用的find()方法,当然你也可以使用直接返回数组的get_info($id),get($array)等模型中的函数;下面的$query直接返回是数组,不是资源了,这点与ADODB的DB不同。 $query = $test->find($array( ‘conditions’=>’id>2’, ‘limit’=>1, ‘order’=>’id desc’ )); $this->assign(‘arr’,$query); $this->display(‘test.html’); } 讲述到这里,数据CRUD部分算是分析完毕了;下面我们将讲述挂件设计 3.2、挂件代码设计: 挂件的组织形式按照文件夹组织,位于\external\widgets目录下,每个文件夹对应一个挂件,每个文件夹中包含4个文件:config.html(配置界面模板)、main.widget.php(挂件主程序文件)、widget.html(挂件显示模板)、widget.info.php(挂件信息文件)。我们不必自己创建,只要直接复制一个挂件文件夹即可。然后着手修改里面的代码就Okay了。 文件分析: 3.2.1、widget.info.php(信息配置文件): return array( ‘name’=>’kichijyo’, //给挂件起名,注意应与包含文件夹的名字相同; ‘display_name’=>’客齐集’, //模板管理界面显示的挂件名字; ‘author’=>’Kichijyo Team’, ‘website’=>’’, ‘version’=>’1.0’, ‘desc’=>’显示测试数据内容’, //挂件描述Description; ‘configurable’=>true //配置本挂件是否可以配置,false时挂件不能被傻瓜配置; ); 3.2.2 main.widget.php(挂件主程序): a) 类的命名规则:挂件文件夹名(首字母大写)+widget;类中的$_name字段,赋值为挂件文件夹名; b) 需要关注的几个方法: c) _get_data():该方法用来返回挂件需要展示的数据; d) get_config_datasrc():该方法用来获取配置界面所需的展示数据; e) function parse_config($input):该方法用来处理配置页面传入的数据; f) 需要关注的变量:$widget_data(相当于Smarty.assign()之后的数据),$options配置的数组数据; g) parse_config($input):该方法是配置数据的保存处理方法,默认它将数据保存到\data\page_config\default.index.config.php或另外一个商品文件中,具体的保存位置依据具体挂件模板来差别,index对应index,商品分类的对应商品分类,也就是说,它的数据保存不面向数据库,它以文本文件的形式保存在了\data\page_config\下的php文件中。如果需要保存到数据库,请自行加入数据库操作的实现处理。 3.2.3、挂件操作流程图: 挂件处理流程图 3.2.4、主程序简易代码实现: class KichijyoWidget extends BaseWidget{ var $_name = ‘kichijyo’; //必须与文件夹同名 function _get_data(){ return $this->options; //返回$widget_data数组给widget.html模板 } //从\data\page_config文件夹中的相应PHP文件中获取相关的options数组,并以$options来填充配置页面模板 function get_config_datasrc(){ $this->options=stripslashes_deep($this->options); $this->assign(‘options’,$this->options); } //处理配置页面传入的数据,并将该数据返回给$options数组,即保存到\data\page_config文件夹下的PHP文件中; function parse_config($input){ return $input; } } 3.2.5、其它文件简述: config.html: 请输入您的姓名:<input type=”text” name=”name” value={$options.name} /><br /> 请输入您的描述:<input type=”text” name=”description” value={$options.description} /> 需要注意的是,这里不需要任何method=post等,仅仅写少许必要代码即可,ECMall连提交按钮都给做好了。 widget.html: {$widget_data.name} and {$widget_data.description} 这里直接上Smarty模板语法,一切皆Okay。 至此,挂件部分描述完毕;下一节模块开发 4、 模块开发 模块的组织形式:\external\modules目录下创建模块文件夹,模块内的组织文件包括: a) index.module.php(前端控制器) b) admin.module.php(后端控制器) c) module.info.php(模块配置信息文件) d) 模板templates文件夹 e) 语言文件夹languages 对于安装与卸载文件不是必须文件,我们略过。。。 简要浏览下模块控制器\admin\app\module.app.php 这里有Install,Uninstall,Manage,Configer等等方法。 感兴趣的朋友,可以研究一下哦。 下面开始我们的开发之旅,首先和挂件一样,我们根本不需要自己逐一创建文件,只要复制一个已经有的模块文件夹并重命名即可。这里我复制DataCall文件夹并重命名为Kichijyo。 步骤: 1. 修改模块配置文件Module.info.php; <?php return array(‘id’ => ‘kichijyo’,//这里应保证与模块文件夹同名。 //这里是调用语言配置文件 //external\modules\kichijoy\languages\sc-utf-8\common.php //或\languages\sc-utf-8\admin\module.lang.php ‘name’ => Lang::get(‘data_call’), ‘desc’ =>Lang::gete(‘datacall_desc’), ‘version’=>’2.0’, ‘author’ =>’Kichijyo Team’, ‘Website’ =>’’, ‘menu’=>array( array( ‘text’ => Lang::get(‘datacall_manage’), ‘act’=>’index’, ), ), ); ?> 通过上面的设置,我们在模块管理界的安装连接上已经改变了: /admin/index.php?app=module&act=install&id=kichijyo 通过id=kichijyo使得安装模块指向了我们新创建的Kichijyo。 下面是部分的语言文件: \external\modules\kichijyo\languages\sc-utf-8\common.lang.php <?php return array( ‘detacall_desc’=>”这是客齐集演示用模块”, ‘data_call’=>’客齐集模块’, ‘manage_data’=>’管理’, ‘add_goods’=>’新增商品调用’, ‘belong_type’=>’所属类型’, ‘handler’=>’操作’, ‘data_goods’=>’商品数据’); ?> 2. 修改前端控制器Index.module.php 所有的模块Module都是ECBaseApp的子类,即模块也是控制器; 类的命名规则:模块名(首字母)+Module 扩展自 IndexBaseModule。 <?php //前端访问形式往往是体现为JSON的数据调用形式,如: //<script type=”text/javascript” src=”/index.php?module=kichijyo”></script> //可以用来给外部系统提供js的数据调出。 class KichijyoModule extends IndexbaseModule { //index()为默认方法,延续了所有的控制器的特征; function index() { $db=db(“test”); $sql=”select * from cem_test”; $query=$db->query($sql); $result=array(); while($row=$db->fetchrow($query)){ $result[]=$row; } $this->assign(“result”,ecm_json_encode($result));//注意这里的ECM_JSON $this->display(“admin/datacall.index.html”); } } ?> 与上部匹配的前端调用模板,我们可以这样写: \external\modules\kichijyo\templates\admin\datacall.index.html var result={$result}; for(var i=0;i<result.length;i++){ document.write(result[i][“description”]+”<br/>”); } 此种方式为JS嵌入式的写法,上面皆是JS语法规则;当我们把<script type=”text/javascript” src=”/index.php?module=kichijyo”></script>代码加入到需要跨系统调用数据的页面时,系统将执行上面的这段JS代码,将数据调出给外部系统。 3. 修改后端的控制器的Admin.module.php类的命名规则:模块名(首字母)+Module 扩展自AdminBaseModule。 <?php //后端访问的URL形式,具体体现在模块管理的链接地址:
展开阅读全文

开通  VIP会员、SVIP会员  优惠大
下载10份以上建议开通VIP会员
下载20份以上建议开通SVIP会员


开通VIP      成为共赢上传

当前位置:首页 > 应用文书 > 其他

移动网页_全站_页脚广告1

关于我们      便捷服务       自信AI       AI导航        抽奖活动

©2010-2026 宁波自信网络信息技术有限公司  版权所有

客服电话:0574-28810668  投诉电话:18658249818

gongan.png浙公网安备33021202000488号   

icp.png浙ICP备2021020529号-1  |  浙B2-20240490  

关注我们 :微信公众号    抖音    微博    LOFTER 

客服