php之mvc设计模式的原理和实现
一、MVC设计模式概述
二、 MVC典型实现
1.模型
数据库操作类
模型类
2.控制器
模块
控制器类
3.视图
4.前端控制器(入口文件)
一、MVC设计模式概述
1.什么是MVC设计模式
MVC是Xerox PRAC(施乐帕克研究中心)在80年代为编程语言 Smalltalk-80发明的一种软件设计模式,至今已被广泛使用。MVC设计模式强制性的使应用程序中的输入、处理和输出分开,将软件系统分成了三个核心部件:模型(Model)、视图(View)、控制器(Controller) 它们各自处理自己的任务。MVC是一种开发设计规范,使代码组织更加合理、流程更加清晰ThinkPHP框架是基于MVC模式来做为基本运行流程,因此,MVC部分一定要理解掌握,以便于后续的框架学习。
2)缺点:
系统结构和实现复杂;
视图与控制器过于紧密;
不适用于小型甚至中型应用程序;
二、 MVC典型实现
- 创建模型,生成并返回数据
- 创建控制器,将模型和视图整合
- 创建视图,展示模型生成并返回的数据
- 创建入口文件,调用控制器完成操作
1.创建模型,生成并返回数据
在面对复杂问题时,面向对象编程可以更好的描述现实中的业务逻辑,所以MVC的程序也是通过面向对象的方式实现的m模型是处理数据的,而数据是存储在数据库里的。在项目中,所有对数据库的直接操作,都应该封装到一个数据库操作类中。
运用学过的面向对象、单例模式、PDO等相关知识,就可以封装一个PDO的数据库操作类。
1)封装一个PDO数据库操作类(Db.php)
<?php
/**
* PDO-MySQL数据库操作类
*/
class MySQLPDO{
//数据库默认连接信息
private $dbConfig = array(
'db' => 'mysql', //数据库类型
'host' => 'localhost', //服务器地址
'port' => '3306', //端口
'user' => 'root', //用户名
'pass' => 'weicunbin123', //密码
'charset' => 'utf8', //字符集
'dbname' => 'testguest', //默认数据库
);
//单例模式 本类对象引用
private static $instance;
//PDO实例
private $db;
/**
* 私有构造方法
* @param $params array 数据库连接信息
*/
private function __construct($params){
//初始化属性
$this->dbConfig = array_merge($this->dbConfig,$params);
//连接服务器
$this->connect();
}
/**
* 获得单例对象
* @param $params array 数据库连接信息
* @return object 单例的对象
*/
public static function getInstance($params = array()){
if(!self::$instance instanceof self){
self::$instance = new self($params);
}
return self::$instance; //返回对象
}
/**
* 私有克隆
*/
private function __clone() {}
/**
* 连接目标服务器
*/
private function connect(){
try{
//连接信息
$dsn = "{$this->dbConfig['db']}:host={$this->dbConfig['host']};port={$this->dbConfig['host']};dbname={$this->dbConfig['dbname']};charset={$this->dbConfig['charset']}";
//实例化PDO
$this->db = new PDO($dsn,$this->dbConfig['user'],$this->dbConfig['pass']);
//设定字符集
$this->db->query("set names {$this->dbConfig['charset']}");
echo 1;
}catch (PDOException $e){
//错误提示
die("数据库操作失败:{$e->getMessage()}");
}
}
/**
* 执行SQL
* @param $sql string 执行的SQL语句
* @return object PDOStatement
*/
public function query($sql){
$rst = $this->db->query($sql);
if($rst===false){
$error = $this->db->errorInfo();
die("数据库操作失败:ERROR {$error[1]}({$error[0]}): {$error[2]}");
}
return $rst;
}
/**
* 取得一行结果
* @param $sql string 执行的SQL语句
* @return array 关联数组结果
*/
public function fetchRow($sql){
return $this->query($sql)->fetch(PDO::FETCH_ASSOC);
}
/**
* 取得所有结果
* @param $sql string 执行的SQL语句
* @return array 关联数组结果
*/
public function fetchAll($sql){
return $this->query($sql)->fetchAll(PDO::FETCH_ASSOC);
}
public function demo(){
echo '测试内容';
}
}
// // 调用方式
// // 连接数据库
// $pdo = MySQLPDO::getInstance();
// // 调用方法
// $pdo -> demo()
2)模型类
在实际项目中,通常是在一个数据库中建立多个表来管理数据,如下图。MVC中的模型,其实就是为项目中的每个表建立一个模型。如果用面向对象的思想,那么每个模型都是一个模型类,对表的所有操作,都要放到模型类中完成。
前面学习了数据库操作类,实例化数据库操作类是所有模型类都要经历的一步,因此需要一个基础模型类来完成这个任务。
例:
- 创建基础模型类:
- 创建Student模型类
<?php
/**
* 基础模型类
*/
class model {
protected $db; //保存数据库对象
public function __construct(){
$this->initDB(); // 初始化数据库
}
private function initDB(){
//配置数据库连接信息
$dbConfig = array('dbname'=>'mvc_study');
//实例化数据库操作类
$this->db = MySQLPDO::getInstance($dbConfig);
}
}
<?php
/**
* student表的操作类,继承基础模型类
*/
class studentModel extends model{
/* 查询所有学生 */
public function getAll(){
$data = $this->db->fetchAll('select * from `student`');
return $data;
}
/* 查询指定id的学生 */
public function getByID($id){
$data = $this->db->fetchRow("select * from `student` where id={$id}");
return $data;
}
}
3、创建控制器,将模型和视图整合
控制器是MVC应用程序中的指挥官,它接收用户的请求,并决定需要调用哪些模型进行处理,再用相应的视图显示从模型返回的数据,最后通过浏览器呈现给用户。
1)模块
如果用面向对象的方式实现控制器,就需要先理解模块(Module)的概念。一个成熟的项目是由多个模块组成的,每个模块又是一系列相关功能的集合。接下来通过一个图例来演示项目中的模块,如图所示:
2)控制器类
控制器是根据模块创建的,即每个模块对应一个控制器类 创建StudentController.class.php控制器类,实现的功能:
- 查看所有学生信息
- 查看指定学生信息
<?php
<?php
/*
* @Descripttion:
* @version:
* @Author: wei
* @Date: 2020-04-09 10:23:14
* @LastEditors: wei
* @LastEditTime: 2020-04-17 16:23:37
*/
/**
* 学生模块控制器类
*/
class studentController{
/**
* 学生列表
*/
public function listAction(){
//实例化模型,取出数据
$stu = new studentModel();
$data = $stu->getAll();
//载入视图文件
require 'student_list.html';
}
/**
* 查看指定学生信息
*/
public function infoAction(){
//接收请求参数
$id = $_GET['id'];
//实例化模型,取出数据
$stu = new studentModel();
$data = $stu->getById($id);
//载入视图文件
require 'student_info.html';
}
}
3)视图文件
根据控制器类要实现的功能方法,创建视图文件,这里没有创建,直接打印出数据代替了
- 查看所有学生信息视图文件:student_list.html
- 查看指定学生信息视图文件:student_info.html
- student_list.html
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<style type="text/css">
table{border-collapse:collapse;text-align:center;}
a{text-decoration:none;}
</style>
</head>
<body>
<h1>学生列表</h1>
<table width="300" border="1">
<tr><th>ID</th><th>姓名</td><th>操作</th></tr>
<?php foreach($data as $v): ?>
<tr>
<td><?php echo $v['id']; ?></td>
<td><?php echo $v['name']; ?></td>
<td><a href="index.php?c=student&a=info&id=<?php echo $v['id']; ?>">查看</a></td>
</tr>
<?php endForeach; ?>
</table>
</body>
</html>
student_info.html
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<style type="text/css">
table{border-collapse:collapse;text-align:center;}
a{text-decoration:none;}
</style>
</head>
<body>
<h1>查看学生信息</h1>
<table width="300" border="1">
<tr><th>ID</th><td><?php echo $data['id']; ?></td></tr>
<tr><th>姓名</th><td><?php echo $data['name']; ?></td></tr>
<tr><th>性别</th><td><?php echo $data['gender']; ?></td></tr>
<tr><th>年龄</th><td><?php echo $data['age']; ?></td></tr>
</table>
<a href="index.php">返回</a>
</body>
</html>
5、前端控制器(入口文件index.php)
前端控制器也称为请求分发器(dispather),通过URL参数判断用户请求了哪个功能,然后完成相关控制器的加载、实例化、方法调用等操作。
使用MVC开发的是一种单一入口的应用程序,传统的Web程序是多入口的,即通过访问不同的文件来完成用户请求。
例如教务管理系统,管理学生时访问student.php,管理教师时访问teacher.php。
单入口程序只有一个index.php提供用户访问。
<?php
/**
* 前端控制器
*/
header('Content-Type:text/html; charset=utf8');
//载入数据库操作类
require('MySQLPDO.class.php');
//载入模型文件
require 'model.class.php';
require 'studentModel.class.php';
//得到控制器名
$c = isset($_GET['c']) ? $_GET['c'] : 'student';
//载入控制器文件
require './'.$c.'Controller.class.php';
//实例化控制器(可变变量)
$controller_name = $c.'Controller';
$controller = new $controller_name;
//得到方法名
$action = isset($_GET['a']) ? $_GET['a'] : 'list';
//调用方法(可变方法)
$action_name = $action.'Action';
$controller->$action_name();
效果展示