一、什么是MVC

MVC模式(Model-View-Controller)是软件工程中的一种软件架构模式,把软件系统分为三个基本部分:模型(Model)、视图(View)和控制器(Controller)。

MVC模式的目的是实现一种动态的程序设计,使后续对程序的修改和扩展简化,并且使程序某一部分的重复利用成为可能。除此之外,此模式通过对复杂度的简化,使程序结构更加直观。软件系统通过对自身基本部份分离的同时也赋予了各个基本部分应有的功能。专业人员可以通过自身的专长分组:

  • (控制器Controller)- 负责转发请求,对请求进行处理。
  • (视图View) – 界面设计人员进行图形界面设计。
  • (模型Model) – 程序员编写程序应有的功能(实现算法等等)、数据库专家进行数据管理和数据库设计(可以实现具体的功能)。


模型(Model) “数据模型”(Model)用于封装与应用程序的业务逻辑相关的数据以及对数据的处理方法。“模
型”有对数据直接访问的权力,例如对数据库的访问。“模型”不依赖“视图”和“控制器”,也就是说,模型不关心它会被如何显示或是如何被操作。但是模型中
数据的变化一般会通过一种刷新机制被公布。为了实现这种机制,那些用于监视此模型的视图必须事先在此模型上注册,从而,视图可以了解在数据模型上发生的改
变。

视图(View) 视图层能够实现数据有目的的显示(理论上,这不是必需的)。在视图中一般没有程序上的逻辑。为了实现视图上的刷新功能,视图需要访问它监视的数据模型(Model),因此应该事先在被它监视的数据那里注册。

控制器(Controller) 控制器起到不同层面间的组织作用,用于控制应用程序的流程。它处理事件并作出响应。“事件”包括用户的行为和数据模型上的改变。

二、为什么要自己开发MVC框架

网络上有大量优秀的MVC框架可供使用,本教程并不是为了开发一个全面的、终极的MVC框架解决方案,而是将它看作是一个很好的从内部学习PHP的机会,在此过程中,你将学习面向对象编程和设计模式,并学习到开放中的一些注意事项。

更重要的是,你可以完全控制你的框架,并将你的想法融入到你开发的框架中。虽然不一定是做好的,但是你可以按照你的方式去开发功能和模块。

三、开始开发自己的MVC框架

在开始开发前,让我们先来把项目建立好,假设我们建立的项目为todo,那么接下来的第一步就是把目录结构先设置好。

虽然在这个教程中不会使用到上面的所有的目录,但是为了以后程序的可拓展性,在一开始就把程序目录设置好使非常必要的。下面就具体说说每个目录的作用:

  • application – 存放程序代码
  • config – 存放程序配置或数据库配置
  • db – 用来存放数据库备份内容
  • library – 存放框架代码
  • public – 存放静态文件
  • scripts – 存放命令行工具
  • tmp – 存放临时数据

在目录设置好以后,我们接下来就要来顶一下一些代码的规范:

  1. MySQL的表名需小写并采用复数形式,如items,cars
  2. 模块名(Models)需首字母大写,并采用单数模式,如Item,Car
  3. 控制器(Controllers)需首字母大写,采用复数形式并在名称中添加“Controller”,如ItemsController, CarsController
  4. 视图(Views)采用复数形式,并在后面添加行为作为文件,如:items/view.php, cars/buy.php

上述的一些规则是为了能在程序钟更好的进行互相的调用。接下来就开始真正的编码了。

第一步将所有的的请求都重定向到public目录下,解决方案是在todo文件下添加一个.htaccesss文件,文件内容为:

<IfModule mod_rewrite.c>
RewriteEngine on
RewriteRule ^$ public/ [L]
RewriteRule (.*) public/$1 [L]
</IfModule>

在我们把所有的请求都重定向到public目录下以后,我们就需要将所有的数据请求都再重定向到public下的index.php文件,于是就需要在public文件夹下也新建一个.htaccess文件,文件内容为:

<IfModule mod_rewrite.c>
RewriteEngine On
#如果文件存在就直接访问目录不进行RewriteRule
RewriteCond %{REQUEST_FILENAME} !-f
#如果目录存在就直接访问目录不进行RewriteRule
RewriteCond %{REQUEST_FILENAME} !-d
#将所有其他URL重写到 index.php/URL
RewriteRule ^(.*)$ index.php?url=$1 [PT,L]
</IfModule>

这么做的主要原因有:

  1. 可以使程序有一个单一的入口,将所有除静态程序以外的程序都重定向到index.php上;
  2. 可以用来生成利于SEO的URL,想要更好的配置URL,后期可能会需要URL路由,这里先不做介绍了。

做完上面的操作,就应该知道我们需要做什么了,没错!在public目录下添加index.php文件,文件内容为:

<?php

    define('DS',DIRECTORY_SEPARATOR);
define('ROOT',dirname(dirname(__FILE__)));
$url = $_GET['url'];
require_once(ROOT.DS.'library'.DS.'bootstrap.php');

注意上面的PHP代码中,并没有添加PHP结束符号”?>”,这么做的主要原因是:对于只包含PHP代码的文件,结束标志(“?>”)最好不存在,PHP自身并不需要结束符号,不添加结束符号可以很大程度上防止末尾被添加额外的注入内容,让程序更加安全。

在index.php中,我们对library文件夹下的bootstrap.php发起了请求,那么bootstrap.php这个启动文件中到底会包含哪些内容呢?

<?php
require_once(ROOT.DS.'config'.DS .'config.php');
require_once(ROOT.DS.'library'.DS .'shared.php');

以上文件都可以直接在index.php文件中引用,我们这么做的原因是为了在后期管理和拓展中更加的方便,所以把需要在一开始的时候就加载运行的程序统一放到一个单独的文件中引用。

先来看看config文件下的config .php文件,该文件的主要作用是设置一些程序的配置项及数据库连接等,主要内容为:

<?php
# 设置是否为开发状态
define('DEVELOPMENT_ENVIRONMENT',true);
# 设置数据库连接所需数据
define('DB_HOST','localhost');
define('DB_NAME','todo');
define('DB_USER','root');
define('DB_PASSWORD','root');

应该说config.php涉及到的内容并不多,不过是一些基础数据的一些设置,再来看看library下的共用文件shared.php应该怎么写。

<?php
/* 检查是否为开发环境并设置是否记录错误日志 */
function setReporting(){
if (DEVELOPMENT_ENVIRONMENT == true) {
error_reporting(E_ALL);
ini_set('display_errors','On');
} else {
error_reporting(E_ALL);
ini_set('display_errors','Off');
ini_set('log_errors','On');
ini_set('error_log',ROOT.DS. 'tmp' .DS. 'logs' .DS. 'error.log');
}
} /* 检测敏感字符转义(Magic Quotes)并移除他们 */
function stripSlashDeep($value){
$value = is_array($value) ? array_map('stripSlashDeep',$value) : stripslashes($value);
return $value;
}
function removeMagicQuotes(){
if (get_magic_quotes_gpc()) {
$_GET = stripSlashDeep($_GET);
$_POST = stripSlashDeep($_POST);
$_COOKIE = stripSlashDeep($_COOKIE);
}
} /* 检测全局变量设置(register globals)并移除他们 */
function unregisterGlobals(){
if (ini_get('register_globals')) {
$array = array('_SESSION','_POST','_GET','_COOKIE','_REQUEST','_SERVER','_ENV','_FILES');
foreach ($array as $value) {
foreach ($GLOBALS[$value] as $key => $var) {
if ($var === $GLOBALS[$key]) {
unset($GLOBALS[$key]);
}
}
}
}
} /* 主请求方法,主要目的拆分URL请求 */
function callHook() {
global $url;
$urlArray = array();
$urlArray = explode("/",$url);
$controller = $urlArray[0];
array_shift($urlArray);
$action = $urlArray[0];
array_shift($urlArray);
$queryString = $urlArray;
$controllerName = $controller;
$controller = ucwords($controller);
$model = rtrim($controller, 's');
$controller .= 'Controller';
$dispatch = new $controller($model,$controllerName,$action);
if ((int)method_exists($controller, $action)) {
call_user_func_array(array($dispatch,$action),$queryString);
} else {
/* 生成错误代码 */
}
} /* 自动加载控制器和模型 */
function __autoload($className) {
if (file_exists(ROOT . DS . 'library' . DS . strtolower($className) . '.class.php')) {
require_once(ROOT . DS . 'library' . DS . strtolower($className) . '.class.php');
} else if (file_exists(ROOT . DS . 'application' . DS . 'controllers' . DS . strtolower($className) . '.php')) {
require_once(ROOT . DS . 'application' . DS . 'controllers' . DS . strtolower($className) . '.php');
} else if (file_exists(ROOT . DS . 'application' . DS . 'models' . DS . strtolower($className) . '.php')) {
require_once(ROOT . DS . 'application' . DS . 'models' . DS . strtolower($className) . '.php');
} else {
/* 生成错误代码 */
}
} setReporting();
removeMagicQuotes();
unregisterGlobals();
callHook();

接下来的操作就是在library中建立程序所需要的基类,包括控制器、模型和视图的基类。

新建控制器基类为controller.class.php,控制器的主要功能就是总调度,具体具体内容如下:

<?php
class Controller {
protected $_model;
protected $_controller;
protected $_action;
protected $_template;
function __construct($model, $controller,$action) {
$this->_controller = $controller;
$this->_action = $action;
$this->_model = $model;
$this->$model =& new $model;
$this->_template =& new Template($controller,$action);
}
function set($name,$value) {
$this->_template->set($name,$value);
}
function __destruct() {
$this->_template->render();
}
}

新建控制器基类为model.class.php,考虑到模型需要对数据库进行处理,所以可以新建一个数据库基类sqlquery.class.php,模型去继承sqlquery.class.php。

新建sqlquery.class.php,代码如下:

<?php
class SQLQuery {
protected $_dbHandle;
protected $_result;
/** 连接数据库 **/
function connect($address, $account, $pwd, $name) {
$this->_dbHandle = @mysql_connect($address, $account, $pwd);
if ($this->_dbHandle != 0) {
if (mysql_select_db($name, $this->_dbHandle)) {
return 1;
}else {
return 0;
}
}else {
return 0;
}
}
/** 中断数据库连接 **/
function disconnect() {
if (@mysql_close($this->_dbHandle) != 0) {
return 1;
} else {
return 0;
}
}
/** 查询所有数据表内容 **/
function selectAll() {
$query = 'select * from `'.$this->_table.'`';
return $this->query($query);
}
/** 查询数据表指定列内容 **/
function select($id) {
$query = 'select * from `'.$this->_table.'` where `id` = \''.mysql_real_escape_string($id).'\'';
return $this->query($query, 1);
}
/** 自定义SQL查询语句 **/
function query($query, $singleResult = 0) {
$this->_result = mysql_query($query, $this->_dbHandle);
if (preg_match("/select/i",$query)) {
$result = array();
$table = array();
$field = array();
$tempResults = array();
$numOfFields = mysql_num_fields($this->_result);
for ($i = 0; $i < $numOfFields; ++$i) {
array_push($table,mysql_field_table($this->_result, $i));
array_push($field,mysql_field_name($this->_result, $i));
}
while ($row = mysql_fetch_row($this->_result)) {
for ($i = 0;$i < $numOfFields; ++$i) {
$table[$i] = trim(ucfirst($table[$i]),"s");
$tempResults[$table[$i]][$field[$i]] = $row[$i];
}
if ($singleResult == 1) {
mysql_free_result($this->_result);
return $tempResults;
}
array_push($result,$tempResults);
}
mysql_free_result($this->_result);
return($result);
}
}
/** 返回结果集行数 **/
function getNumRows() {
return mysql_num_rows($this->_result);
}
/** 释放结果集内存 **/
function freeResult() {
mysql_free_result($this->_result);
}
/** 返回MySQL操作错误信息 **/
function getError() {
return mysql_error($this->_dbHandle);
}
}

新建model.class.php,代码如下:

<?php
class Model extends SQLQuery{
protected $_model;
function __construct() {
$this->connect(DB_HOST,DB_USER,DB_PASSWORD,DB_NAME);
$this->_model = get_class($this);
$this->_table = strtolower($this->_model)."s";
}
function __destruct() {
}
}

新建视图基类为template.class.php,具体代码如下:

<?php
class Template {
protected $variables = array();
protected $_controller;
protected $_action;
function __construct($controller,$action) {
$this->_controller = $controller;
$this->_action =$action;
}
/* 设置变量 */
function set($name,$value) {
$this->variables[$name] = $value;
}
/* 显示模板 */
function render() {
extract($this->variables);
if (file_exists(ROOT.DS. 'application' .DS. 'views' .DS. $this->_controller .DS. 'header.php')) {
include(ROOT.DS. 'application' .DS. 'views' .DS. $this->_controller .DS. 'header.php');
} else {
include(ROOT.DS. 'application' .DS. 'views' .DS. 'header.php');
}
include (ROOT.DS. 'application' .DS. 'views' .DS. $this->_controller .DS. $this->_action . '.php');
if (file_exists(ROOT.DS. 'application' .DS. 'views' .DS. $this->_controller .DS. 'footer.php')) {
include (ROOT.DS. 'application' .DS. 'views' .DS. $this->_controller .DS. 'footer.php');
} else {
include (ROOT.DS. 'application' .DS. 'views' .DS. 'footer.php');
}
}
}

做完了以上这么多操作,基本上整个MVC框架已经出来了,下面就该制作我们的站点了。我们要做的站点其实很简单,一个ToDo程序。

首先是在我们的/application/controller/ 目录下面新建一个站点控制器类为ItemsController,命名为itemscontroller.php,内容为:

<?php
class ItemsController extends Controller {
function view($id = null,$name = null) {
$this->set('title',$name.' - My Todo List App');
$this->set('todo',$this->Item->select($id));
}
function viewall() {
$this->set('title','All Items - My Todo List App');
$this->set('todo',$this->Item->selectAll());
}
function add() {
$todo = $_POST['todo'];
$this->set('title','Success - My Todo List App');
$this->set('todo',$this->Item->query('insert into items (item_name) values (\''.mysql_real_escape_string($todo).'\')'));
}
function delete($id) {
$this->set('title','Success - My Todo List App');
$this->set('todo',$this->Item->query('delete from items where id = \''.mysql_real_escape_string($id).'\''));
}
}

接下来就是先建站点的模型,在我们的/application/model/ 目录下面先建一个站点模型类为Item,内容直接继承Model,代码如下:

<?php
class Item extends Model {
}

最后一步是设置我们站点的视图部分,我们现在/application/views/目录下新建一个items的文件夹,再在items文件夹下建 立与控制器重Action相同的文件,分别为view.php,viewall.php,add.php,delete.php,考虑到这么页面中可能需 要共用页首和页尾,所以再新建两个文件,命名为header.php,footer.php,每个文件的代码如下:

view.php文件:查看单条待处理事务

<h2><?php echo $todo['Item']['item_name']?></h2>
<a href="../../../items/delete/<?php echo $todo['Item']['id']?>">
<span>Delete this item</span>
</a>

viewall.php文件:查看所有待处理事务

<form action="../items/add" method="post">
<input type="text" value="I have to..." onclick="this.value=''" name="todo"> <input type="submit" value="add">
</form>
<br/><br/>
<?php $number = 0?>
<?php foreach ($todo as $todoitem):?>
<a href="../items/view/<?php echo $todoitem['Item']['id']?>/<?php echo strtolower(str_replace(" ","-",$todoitem['Item']['item_name']))?>">
<span>
<?php echo ++$number?>
<?php echo $todoitem['Item']['item_name']?>
</span>
</a><br/>
<?php endforeach?>

add.php文件:添加待处理事务

<a href="../items/viewall">Todo successfully added. Click here to go back.</a><br/>

delete.php文件:删除事务

<a href="../../items/viewall">Todo successfully deleted. Click here to go back.</a><br/>

header.php:页首文件

<html>
<head>
<title><?php echo $title?></title>
<style>
.item {width:400px;}
input {color:#222222;font-family:georgia,times;font-size:24px;font-weight:normal;line-height:1.2em;color:black;}
a {color:#222222;font-family:georgia,times;font-size:24px;font-weight:normal;line-height:1.2em;color:black;text-decoration:none;}
a:hover {background-color:#BCFC3D;}
h1 {color:#000000;font-size:41px;letter-spacing:-2px;line-height:1em;font-family:helvetica,arial,sans-serif;border-bottom:1px dotted #cccccc;}
h2 {color:#000000;font-size:34px;letter-spacing:-2px;line-height:1em;font-family:helvetica,arial,sans-serif;}
</style>
</head>
<body>
<h1>My Todo-List App</h1>

footer.php:页尾文件

</body>
</html>

当然还有一个必不可少的操作就是在数据中中建立一张表,具体代码如下:

CREATE TABLE IF NOT EXISTS `items` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`item_name` varchar(255) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=17 ;

至此一个使用MVC开发的网站就开发完成了,你现在可以通过访问http://localhost/todo/items/viewall 查看新建的站点。

摘自:http://nonfu.me/p/5284.html

使用PHP搭建自己的MVC框架的更多相关文章

  1. JavaWeb之搭建自己的MVC框架(二)

    1. 前言 在 JavaWeb之搭建自己的MVC框架(一) 中我们完成了URL到JAVA后台方法的最基本跳转.但是实际操作中会发现有一个不方便的地方,现在在com.mvc.controller包中只有 ...

  2. JavaWeb之搭建自己的MVC框架

    https://blog.csdn.net/anita9999/article/details/83378111 自己写一个mvc框架吧(一) https://www.cnblogs.com/heba ...

  3. JavaWeb之搭建自己的MVC框架(三)

    1. 前言         在前两节的内容中,我们完成了一个基本的框架搭建.但是如果我们在前端请求中增加参数,我们要怎么传递到后台方法呢?接下来我们就来研讨这部分内容. 2. 实现         ( ...

  4. JavaWeb之搭建自己的MVC框架(一)

    1. 介绍 MVC全名是Model View Controller,是模型(model)-视图(view)-控制器(controller)的缩写,一种软件设计典范,用一种业务逻辑.数据.界面显示分离的 ...

  5. Spring MVC 框架的架包分析,功能作用,优点

    由于刚搭建完一个MVC框架,决定分享一下我搭建过程中学习到的一些东西.我觉得不管你是个初级程序员还是高级程序员抑或是软件架构师,在学习和了解一个框架的时候,首先都应该知道的是这个框架的原理和与其有关j ...

  6. Java Servlet开发的轻量级MVC框架最佳实践

    在Servlet开发的工程实践中,为了减少过多的业务Servlet编写,会采用构建公共Servlet的方式,通过反射来搭建轻量级的MVC框架,从而加快应用开发. 关于Servlet开发的基础知识,请看 ...

  7. ASP.NET MVC搭建项目后台UI框架—1、后台主框架

    目录 ASP.NET MVC搭建项目后台UI框架—1.后台主框架 ASP.NET MVC搭建项目后台UI框架—2.菜单特效 ASP.NET MVC搭建项目后台UI框架—3.面板折叠和展开 ASP.NE ...

  8. ASP.NET MVC搭建项目后台UI框架—11、自动加载下拉框查询

    ASP.NET MVC搭建项目后台UI框架—1.后台主框架 需求:在查询记录的时候,输入第一个字,就自动把以这个字开头的相关记录查找出来,输入2个字就过滤以这两个子开头的记录,依次类推. 突然要用到这 ...

  9. Spring MVC篇一、搭建Spring MVC框架

    本项目旨在搭建一个简单的Spring MVC框架,了解Spring MVC的基础配置等内容. 一.项目结构 本项目使用idea intellij创建,配合maven管理.整体的目录结构如图: 其中ja ...

随机推荐

  1. VC++中的类的内存分布(上)(通过强制转换,观察地址,以及地址里的值来判断)

    0.序 目前正在学习C++中,对于C++的类及其类的实现原理也挺感兴趣.于是打算通过观察类在内存中的分布更好地理解类的实现.因为其实类的分布是由编译器决定的,而本次试验使用的编译器为VS2015 RC ...

  2. 使用PyQt来编写第一个Python GUI程序

    原文:使用PyQt来编写第一个Python GUI程序 本文由 伯乐在线 - Lane 翻译,Daetalus 校稿.未经许可,禁止转载!英文出处:pythonforengineers.com.欢迎加 ...

  3. Android 优化性能之 如何避免--过度绘制

    可能有些人不明白什么是过度绘制,简单言,我们app一个页面所显示的效果是由像素一帧一帧绘制而成.过度绘制就是意味着这一帧被绘制多次.如果是静态的布局,可能影响不是很大,如果是动态的,比如ListVie ...

  4. cocos2dx tag和zorder

    当一个渲染对象加入到两外一个渲染对象中时,可以有两个可选参数,一个时tag,一个是order virtual void addChild(CCNode * child); virtual void a ...

  5. Html 语法学习笔记二

    1.图像标签(<img>)和源属性(Src) 在 HTML 中.图像由 <img> 标签定义.        <img> 是空标签,意思是说,它仅仅包括属性,而且没 ...

  6. Android首席设计师宣称移动概念已死,开发人员应该面向屏幕编写应用而非移动

    腾讯科技对Android首席设计师Duarte"移动已死"訪谈内容的翻译错得离谱,被到处转载,误人视听. 而要真正理解Duarte所想表达的含义,须要深入了解互联网前沿设计理念以及 ...

  7. .net 微信APP支付接口的开发流程以及坑

    流程 申请APP的微信支付 申请成功之后得到APPID 商户号 以及自己设置商户号的支付密码 这时就可以开发接口了 微信APP支付API:https://pay.weixin.qq.com/wiki/ ...

  8. c 转置字符串You are a so cheap man ->man cheap so a are You

    解题思路: 1.将字符串转置 2.对转置后的字符串中单词转置 #include<stdio.h> #include<string.h> #include<stdlib.h ...

  9. 【Java】 实现一个简单文件浏览器(2)

    接着上篇文章 接下来说下程序右侧的文件内容表格如何实现 FileTable类: FileTable基础于JTable类,构造函数里用setDefaultRenderer设置每行默认的渲染器为FileT ...

  10. cocos2dx中Action汇总

    本文由qinning199原创, 转载请注明:http://www.cocos2dx.net/?p=119 今天总结一下cocos2dx中的一些Action动作,其中To表示到达某个点,而By表示偏移 ...