如何写mvc基础框架

来源:互联网 发布:ackerman函数算法设计 编辑:程序博客网 时间:2024/04/28 10:04

 转老逆原创帖!

VC模式下,客户直接发送请求到控制器,控制器根据用户请求的资源分发到相对应的模型来处理,模型完成了业务逻辑后,把所要的数据发送到视图,视图显示返回给客户。这就是web 或是说B/S架构的MVC工作流程。

控制器:

用户的所有请求会发送到控制器,由控制器来根据需要调用模型和视图。比如用户请求index.php 控制器文件,index.php里面不会设计到任何的数据库操作、逻辑操作。它只会寻找执行用户请求的业务模型,把所有的业务逻辑操作交给模型也就是MVC中的M。把控制器独立出来,形成单入口访问模式,方便做全局管理,比如:日志记录等。

模型:

模型是业务逻辑数据的集合,比如数据库操作,复杂的逻辑运算等。按照功能或项目模块来分成一个个模型,模型间的耦合性很小有利于项目以后的扩展和修改。

视图:

Web技术中的MVC的C层。其主要是由 HTML 、XML语言组成的界面。以前的web界面是视图和模型混杂在一起使用,形成了杂乱的代码,这样使日后程序的维护十分艰难。PHP中知名的模板引擎smarty 就是为了实现模型和视图分离的一种技术。现在smarty 在PHP行业中被开发者广泛使用。

MVC思想不是为了某种语言而设计的,它适用于所有的面向对象的语言。比如知名的实现MVC思想的JAVA语言的 Struts 框架。当然PHP 框架也是百花齐放如 :Zend Framework 、 Fleaphp 、Thinkphp 、Cakephp 等,都能很好的实现MVC思想而且他们大量应用了GOF 设计模式,开发人员如果基于以上几种MVC框架来进行项目开发的话,开发的效率和代码质量都会大幅度提升,特别是多人协作开发的项目。那PHP怎么实现MVC的呢?下面给大家开发一个简单的MVC基础框架来说明这一点,完整代码附光盘09/20。

类驱动

在php5中可以使用__autoload 函数来实现类自动加载。但单纯这样的方式不够灵活的。比如类文件存放在不同的目录里面,而此时又需要自动加载的情况下,我们就需要在__autoload函数里进行复杂的逻辑判断来实现自动加载。
比如需要实例化两个类:Myblog 、Mybook。Mylog类在根目录下的Lib/test.php 文件里,Mybook类在根目录下的App/command.php 文件里。

__autoload

函数里实现加载:


[php]<?php
function __autoload($class){

if($class =='Myblog') include 'Lib/test.php';

if($class =='Mybook') include 'App/command.php';

 

if(!include_once($classpath)){//加栽类

throw newException("加载类库失败");

}
}
[size=10.5pt]?>

这只是实例化两个不同目录下的两个类而已。如果项目中使用面向对象开发的话,类不会那么少,大家可以想像一下。如果要加载数个不同目录下的类,在__autoload函数里实现会是多么的麻烦和不灵活。

在这里给出个比较简单的解决方案,而且这个解决方案在很多MVC框架中都得以很好的应用

我们只需要在类的命名方式上做些改变,以类的目录路径为类名:

/Yhmphp/ App.php 里的Mysession类,命名为:Yhmphp_App_Mysession。用‘ _ ’下划线来替换

路径分割符,以相对路径下的目录路径做类名。

又比如根目录下的Lib目录下test.php 文件里面(/Lib/test.php) 有个Myblog 类,可以这样给Myblog类命名:

<?php
class Lib_Myblog{}
?>
实例化Lib_Myblog类:
<?php

$myblog = newLib_Myblog;
[size=10.5pt]?>

__autoload函数里用str_replace函数把路径分割符替换类名中的‘ _ ’下划线,这样就可以准确的找到Lib_Myblog类的所在文件的路径然后准确的加载了:

<?php
function __autoload($class){

$classpath =str_replace('_','/',$class).'.php';

if(!include_once($classpath)){//加栽类

throw newException("加载类库失败");

}
}
[size=10.5pt]?>

模型的路由:

当我们给单入口文件(控制器)index.php
加上了模型选参m(index.php?m=myblog),控制器就会去寻找 myblog模型类,并实例化,然后执行myblog模型类中的 model 方法,最后执行show方法
来显示视图。

<?php
class App_Run
{

publicfunction routing(){

$model =MOBILE_MODEL.'_'.MODEL_SWITCHING.'_'.$_REQUEST['m'];

if(class_exists($model)){

$cake= new $model;

method_exists($cake,'model')&& $cake->model(); //执行模型里面的 model方法

method_exists($cake,’show’)&& $cake->show(); //视图层

}else{

thrownew Exception("数据模型不存在");

}
}
}
[size=10.5pt]?>

常量MBILE_MODEL定义了模型存放的目录名,在这里做了定义方便日后模型目录的更改。这是个良好的习惯。能统一定义的信息就该统一。能模块化的业务逻辑就应该模块化。为日后的项目维护和项目扩展做好铺垫。

MBILE_MODEL

在Config/__Active.php文件里面这样定义:

[php]<?php
/**
系统配置
*/
define('MOBILE_ROOT', ''); //站根目录
define('MOBILE_MODEL','Modules'); //模型目录名
[size=10.5pt]?>

class_exists() 函数来判断客户请求的($_REQUEST['m']) 模型类是否存在。Class_exists() 方法依据__autoload() 函数来加载判断。所以__autoload()函数必须在class_exists() 方法之前先加载。

模型类如果存在,就使用method_exists() 方法来判断执行模型类里面的model 方法里面的业务逻辑,然后再执行show() 方法显示视图。这样就完成了一个 MVC 流程。

存放模型的目录是 Modules 目录。这个在上面的MOBILE_MODEL常量中已经定义。难道所有的业务模型都存在一个Modules目录里面吗?这样的设计的确有点问题。文件夹里面的文件过多,损耗程序寻找模型加载的时间,而且模型过多存在一个目录之中,会让这个项目变得很杂乱。比如前台和后台的模型目录和视图目录就应该分开存放。

<?php
/**
多模型目录
*/
if(empty($_GET['c']) && ) $_GET['c'] ='Default';
if(empty($_GET['m'])) $_GET['m'] = 'Index';

define ('MODEL_SWITCHING',$_GET['c']) ; //前台后台目录切换
[size=10.5pt]?>

常量MODEL_SWITCHING 就是为解决这个问题设计的:
我们先看最后一句代码:

[size=10.5pt]define('MODEL_SWITCHING',$_GET['c']) ; //前台后台目录切换


它定义了常量MODEL_SWITCHING 以$_GET ['c']变量为值。App_Run 类中,常量MOBILE_MODEL 、MODEL_SWITCHING 和$_GET['m'] 组成了模型的加载路径:
$model = MOBILE_MODEL.'_'.MODEL_SWITCHING.'_'.$_GET['m'];

这样设计以后我们想添加多个模型目录都是很容易的事情了。比如:项目需要添加两个模型目录。前台模型目录:Default 和 后台模型目录:Admin 。只需要在默认的模型目录 Modules 下创建 Default 和Admin 两个目录,然后客户访问的URL 中 添加一个 参数c :
访问Default 目录下的Myblog业务模型: index.php?c=default&m=myblog
访问Admin 目录下的Member业务模型: index.php?c=admin&m=member
我们可以再设计灵活及人性化点,就是当$_GET[‘m’]和$_GET[‘c’] 客户没有设置的时候来给模型目录和业务模型类设置一个默认值。
if(empty($_GET['c'])
) $_GET['c'] = 'Default';
if(empty($_GET['m'])) $_GET['m'] = 'Index';

控制器
PHP MVC典型的设计就是使用单入口php文件来实现MVC中的C:控制器。所有的客户请求全部经过index.php 控制器集中控制。然后按需进行分发:

<?php
/**
入口文件
*/
try{

error_reporting(E_ALL);//关闭错误输出

require'Config/__Homeswitching.php';

require'Config/__Active.php';

require'App/Auto.php';


$set = newApp_Run;

$set->routing();


}catch (Exception $e){


echo($e->getMessage());


}
[size=10.5pt]?>

多模型设置文件:__Homeswitching.php,定义基础模型目录文件:__Active.php,类驱动文件:Auto.php
加载完后,再实例化模型路由App_Run类,执行其 routing() 方法 寻找完成客户请求。

业务模型

业务模型类是整个项目中使用最多的。它就是MVC 中的M (模型)。类里面封装了大量的业务逻辑。比如:客户向index.php 控制器 请求 (index.php?m=newbook)newbook业务模型,来查询最新出的图书。 那么newbook业务模型类所需要完成的任务就是查询数据库,从数据库中提取最新的图书资料,然后发送到视图层(View)显示给客户。显然,MVC 模式可以把 模型与模型、功能与功能之间的耦合度变得很小、扩展性很强。

<?php
/**

* 模型类

* */
class Modules_Default_Index
extends App_Manage
{

 

privatenewbook=’’;

publicfunction __c****truct(){

parent::__c****truct();

}

 

publicfunction show(){

$this->tpl->assign(‘newbook’,$this->newbook);
 
$this->tpl->display('Index');

}

 

publicfunction model(){

/**实现业务逻辑*/

$this->newbook = ‘PHP MVC’;

}
}
[size=10.5pt]?>

业务模型类里面有两个主要方法: model() 、show() 方法。Model() 方法主要实现业务逻辑,比如:读数据库、写数据库等。Show() 方法主要和model() 方法联系获取业务逻辑的数据,然后输出给视图(php模板)。

读者请思考下以下几个问题:
模型类 Modules_Default_Index 为什么要起这个名字?前面__autoload讲解中已讲解。Show() 方法是否能去掉只实现model() 模型?模型的路由小节中讲解。

全局类
在项目开发里,有很多我们自己封装好的类:数据库操作类、模板类、email发送类、分页类、文本缓存类、内存缓存类memcache等等。这些类中有些是每个模型都必须加载使用的,比如 数据库操作类、分页类。难道我们每个模型里都要显式的实例化一次?那真是太麻烦了。所以设计一个全局管理类App_Manage :

<?php
class App_Manage{
protected$tpl;
protected$mem;
protected$email;
publicfunction __c****truct(){

$this->tpl = new Lib_Tpl;
/*
$this->mem = new Lib_Memcached;


$this->email= new Lib_Mailer();*/
}
[font=宋体]}[/font]
[font=宋体][size=10.5pt]?>

在App_Manage中。我们简单的在其构造方法中实例化了几个常用的类。每个模型都继承App_Manage类,这样模型类里面就可以使用父类的方法和属性,以此实现全局类:

public function __c****truct(){

parent::__c****truct();
[size=10.5pt]}[/size]

<SPAN
style="COLOR: #007700">

本篇文章来源于PHP论坛原文链接:http://bbs.php.cn/thread-20644-1-1.html

原创粉丝点击