1、什么是MVC?
MVC全名是Model View Controller,是模型(model)-视图(view)-控制器(controller)的缩写;
- MVC简单示例:
// MVC简单示例理解:
//模型类: 数据库操作
class Model
{
protected $db;
// 连接数据库获取ID为1999的信息
public function getData()
{
$this->db = new PDO('mysql:host=localhost;dbname=php_pro', 'root', 'root');
return $this->db->query('select * from users where id=1999')->fetchAll(PDO::FETCH_ASSOC);
}
}
// 视图: 数据展示(相当于HTML输出的模板)
class View
{
public function fetch($data)
{
$res='hello,我叫:'.$data[0]['username'].',我今年'.$data[0]['age'].'岁了!';
return $res;
}
}
// 控制器:通常是在获取模型(Model)中数据并交给视图(View)去显示
class Controller
{
public function index()
{
// 1. 获取数据
$model = new Model();
$data = $model->getData();
// 2. 渲染模板
$view = new View();
return $view->fetch($data);
}
}
// 客户端调用(测试)
// 创建控制器实例/对象
$controller = new Controller();
echo $controller->index();
输出:hello,我叫:老周,我今年36岁了!
结论:通过简单示例,可以理解MVC各自的作用,就是用控制器(Controller)获取模型(Model)中数据并交给视图(View)去显示;
2、依赖注入
依赖注入就是用来处理类与类之间的依赖关系
比如上面 的MVC方案,控制器(Controller)中直接new()
模型(Model)与视图(View)类,这种方法是最不可取的,控制器严重依赖外部的某些对象,这种现象就是大家常常听说过的: 代码的耦合度过高。
2.1 如何解决呢?—-使用:依赖注入
示例:只需要将控制器方法及客户端代码进行小修改就可以了:
// 控制器:通常是在获取模型(Model)中数据并交给视图(View)去显示
class Controller
{
//使用变量方式,以依赖注入方式解决代码的耦合度过高问题
public function index($model, $view)
{
// 1. 获取数据
$data = $model->getData();
// 2. 渲染模板
return $view->fetch($data);
}
}
// 客户端调用(测试)
// 创建控制器实例/对象
$model = new Model();
$view = new View();
$controller = new Controller();
//index方法传参方式(对象)->依赖注入
echo $controller->index($model, $view);
2.2 上面例子以依赖注入方式解决代码的耦合度过高问题,但是如果控制器存在多个方法,可以使用构造方法实现外部对象在当前类的共享/复用
示例:只需在控制器中加入构造方法并进行小修改就可以了:
// 控制器:通常是在获取模型(Model)中数据并交给视图(View)去显示
class Controller
{
// 外部依赖的对象
protected $model = null;
protected $view = null;
// 通过构造方法将外部对象初始化,实现了外部对象在当前类的共享/复用
public function __construct($model, $view)
{
$this->model = $model;
$this->view = $view;
}
//使用变量方式,以依赖注入方式解决代码的耦合度过高问题
public function index()
{
// 1. 获取数据
$data = $this->model->getData();
// 2. 渲染模板
return $this->view->fetch($data);
}
}
// 客户端调用(测试)
// 创建控制器实例/对象
$model = new Model();
$view = new View();
$controller = new Controller($model, $view);
echo $controller->index();
3、MVC+依赖注入+服务容器示例
使用4个文件分开进行示例:
No | 文件名称 | 说明 |
---|---|---|
1 | Container.php | 服务容器(Container) |
2 | Model.php | 模型(Model) |
3 | View.php | 视图(View) |
4 | Controller.php | 控制器(Controller) |
- Container.php | 服务容器(Container)
将依赖的外部对象,放到一个”服务容器”中进行统一管理;
namespace mvc_demo;
class UsersContainer
{
// 1. 对象容器
protected $instances = [];
// 2. 绑定一个类、闭包、实例、接口实现到容器
// 参数1: 是外部对象在当前对象容器数组中的键名/别名
// 参数2: 是当前需要绑定到容器的对象的实例化过程(函数)
public function bind($abstract, $concrete=null)
{
return $this->instances[$abstract] = $concrete;
}
// 3. 从对象容器中取出对象, 调用它
public function make($abstract, $params=[] ) {
return call_user_func_array($this->instances[$abstract], []);
}
}
// 1、将外部对象: Model, View的实例绑定到服务容器中
$container = new UsersContainer;
// 绑定模型类实例绑定到服务容器中
$container->bind('model', function(){
return new Model();
});
// 绑定视图类实例绑定到服务容器中
$container->bind('view', function(){
return new View();
});
- Model.php |模型(Model)
namespace mvc_demo;
use PDO;
// 模型类: 数据库操作
class Model
{
protected $db;
// 获取数据
public function getData()
{
$this->db = new PDO('mysql:host=localhost;dbname=php_pro', 'root', 'root');
return $this->db->query('select * from users limit 10')->fetchAll(PDO::FETCH_ASSOC);
}
}
- View.php | 视图(View)
namespace mvc_demo;
// 视图: 数据展示
class View
{
// 数据展示
public function fetch($data)
{
// 表格方式展示,使用字符串拼接实现table的html代码
$table = '<table>';
$table .= '<caption>用户信息表</caption>';
$table .= '<tr><th>ID</th><th>用户名</th><th>邮箱</th></tr>';
// 遍历用户表
foreach ($data as $user){
$table .= '<tr>';
$table .= '<td>'.$user['id'].'</td>';
$table .= '<td>'.$user['name'].'</td>';
$table .= '<td>'.$user['email'].'</td>';
$table .= '</tr>';
}
$table .= '</table>';
return $table;
}
}
echo '<style>
table {border-collapse: collapse; border: 1px solid;text-align: center; width: 500px;height: 150px;width: 600px;}
caption {font-size: 1.2rem; margin-bottom: 10px;}
tr:first-of-type { background-color:yellow;}
td,th {border: 1px solid; padding:5px}
</style>';
- Controller.php | 控制器(Controller)
namespace mvc_demo;
//加载容器
require 'Container.php';
require 'Model.php';
require 'View.php';
class UsersController
{
// 获取数据,并展示出来
public function index(UsersContainer $container)
{
// 1. 获取数据
$data = $container->make('model')->getData();
// 2. 渲染模板
return $container->make('view')->fetch($data);
}
}
// 客户端调用(测试)
$controller = new UsersController();
echo $controller->index($container);
4、MVC+依赖注入+服务容器+Facade门面技术示例
Facade门面技术: 就是将服务容器中的对象的访问进行静态接管
- 示例3的基础上,只需要增加
Facede.php
及对控制器进行静态接管修改。
// Facade门面技术, 静态接管服务容器中的成员的访问
namespace mvc_demo;
// 在服务容器与工作的控制器之间再添加一个中间层: Facade
class Facade
{
// 服务容器
protected static $container = null;
// 初始化方法: 就是给当前的Facade类扣$container属性赋值
// 理解为Facade的构造方法(但不是)
// 将外部的服务容器注入到当前的facade中
public static function initialize(UsersContainer $container)
{
static::$container = $container;
}
}
// 模型类成员访问静态化(给成员套一个静态访问的马甲)
class UserModel extends Facade
{
public static function getData()
{
return static::$container->make('model')->getData();
}
}
// 视图类成员访问静态化(给成员套一个静态访问的马甲)
class UserView extends Facade
{
public static function fetch($data)
{
return static::$container->make('view')->fetch($data);
}
}
- 控制器进行静态接管
namespace mvc_demo;
//加载容器
require 'Container.php';
require 'Model.php';
require 'View.php';
require 'Facede.php';
class UsersController
{
// 构造方法,初始化facade
public function __construct(UsersContainer $container)
{
Facade::initialize($container);
}
// 用Facade方式类成员
public function index()
{
// 1. 获取数据
$data = UserModel::getData();
// 2. 渲染模板
return UserView::fetch($data);
}
}
// 客户端调用(测试)
$controller = new UsersController($container);
echo $controller->index();
输出结果与示例3相同。
总结
- MVC : 就是用控制器(Controller)获取模型(Model)中数据并交给视图(View)去显示;
- 依赖注入:依赖注入就是用来处理类与类之间的依赖关系,使用变量方式,以依赖注入方式解决代码的耦合度过高问题;
- 服务容器:将依赖的外部对象,放到一个”服务容器”中进行统一管理;
- Facade门面技术: 就是将服务容器中的对象的访问进行静态接管。
- Ps:大概意思理解了,但容器与Facade门面技术理解不够深入,容器的好处是什么?Facade门面技术相对于使用静态调用,加
static
关键字不就可以吗?