博客列表 >PHP之MVC框架原理与实现方式

PHP之MVC框架原理与实现方式

吴长清
吴长清原创
2022年08月25日 15:14:42635浏览

1.MVC是什么

MVC的全名是Model View Controller,是一种使用“模型-视图-控制器”设计创建Web应用程序的模式,同时提供了对HTML、CSS和JavaScript的完全控制,它是一种软件设计典范

  1. m: model,业务模型, 数据库操作
  2. v: view, 用户界面(视图), html 页面
  3. c: controller,控制器, model与view数据交互的中间层

2.分层后的MVC

2.1 控制器

  1. 接受请求: 路由
  2. 选择模型: CURD
  3. 加载视图: 模板

2.2 模型

  1. 查询构造器
  2. 模型操作

2.3 视图

  1. 模板赋值
  2. 渲染视图

2.4 目录结构

3.MVC实现与路由

3.1 核心原理

3.1.1 业务模型Model.php

  1. <?php
  2. // 模型类
  3. namespace core;
  4. use PDO;
  5. abstract class Model
  6. {
  7. protected $db = null;
  8. //1.连接数据库 在实例化时自动连接
  9. public function __construct($dsn, $username, $password)
  10. {
  11. $this->db = new PDO($dsn, $username, $password);
  12. }
  13. // 2.内置一些底层操作
  14. // 2.1 获取全部数据 $table 查询的表名
  15. public function getAll($table)
  16. {
  17. $sql = "SELECT * FROM `$table` ;";
  18. $stmt = $this->db->prepare($sql);
  19. // $stmt->execute();
  20. if ($stmt->execute()) {
  21. return $stmt->fetchAll(PDO::FETCH_ASSOC);
  22. } else {
  23. print_r($stmt->errorInfo());
  24. }
  25. }
  26. // 2.2 指定获取多条数据 此案例只调用了此方法
  27. public function getData($table, $num)
  28. {
  29. $sql = "SELECT * FROM `$table` LIMIT ?;";
  30. $stmt = $this->db->prepare($sql);
  31. $stmt->bindParam(1, $num, PDO::PARAM_INT);
  32. $stmt->execute();
  33. return $stmt->fetchAll(PDO::FETCH_ASSOC);
  34. }
  35. // 2.3 根据id查询一条数据
  36. public function getOne($table, $id)
  37. {
  38. $sql = "SELECT * FROM `$table` WHERE `id`= ? ;";
  39. $stmt = $this->db->prepare($sql);
  40. $stmt->bindParam(1, $id, PDO::PARAM_INT);
  41. $stmt->execute();
  42. return $stmt->fetchAll(PDO::FETCH_ASSOC);
  43. }
  44. }

3.1.2 视图 View.php

  1. <?php
  2. // 视图类
  3. namespace core;
  4. class View
  5. {
  6. // 1. 模板变量容器
  7. protected array $data = [];
  8. // 2. 模板赋值
  9. public function assign(string $key, $value)
  10. {
  11. $this->data[$key] = $value;
  12. }
  13. // 3. 渲染视图同时传参 $path 视图路径 $data存放参数的容器
  14. public function render(string $path, array $data = [])
  15. {
  16. if ($data) {
  17. foreach ($data as $key => $value) {
  18. $this->assign($key, $value);
  19. }
  20. }
  21. // 解构数组容器 以数组的键作为变量名 方便在模板中使用
  22. extract($this->data);
  23. // 渲染/加载模板文件
  24. file_exists($path) ? include $path : die('模板不存在');
  25. }
  26. }

3.1.3 控制器 Controller.php

  1. <?php
  2. // 控制器类
  3. namespace core;
  4. abstract class Controller
  5. {
  6. // 1.模型对象
  7. protected Model $model;
  8. // 2.视图对象
  9. protected View $view;
  10. // 3.在实例化时初始化模型对象与视图对象
  11. public function __construct(Model $model, View $view)
  12. {
  13. $this->model = $model;
  14. $this->view = $view;
  15. }
  16. }

3.1.4 路由 Router.php

  1. <?php
  2. namespace core;
  3. // 路由类
  4. class Router
  5. {
  6. public static function parse(): array
  7. {
  8. // 默认控制器,实际项目,应该来自配置文件,而不是在写死
  9. $controller = 'Index';
  10. $action = 'index';
  11. // 参数列表
  12. $params = [];
  13. // 判断是否存在pathinfo
  14. if (array_key_exists('PATH_INFO', $_SERVER) && $_SERVER['PATH_INFO'] !== '/') {
  15. // 为什么要判断 $_SERVER['PATH_INFO'] !== '/' ?
  16. // 因为: admin.php/ 时,$_SERVER['PATH_INFO'] = '/', 导致解析控制器失败
  17. $pathinfo = array_filter(explode('/', $_SERVER['PATH_INFO']));
  18. // dump($pathinfo);
  19. // 考虑到index.php/ 情况, 这时pathinfo为空数组
  20. if (count($pathinfo) >= 2) {
  21. $controller = array_shift($pathinfo);
  22. $action = array_shift($pathinfo);
  23. $params = $pathinfo;
  24. } else {
  25. $controller = array_shift($pathinfo);
  26. }
  27. }
  28. // 查看控制器,方法,参数
  29. // dump($controller, $action, $params);
  30. // 将这些数据返回出去
  31. return [$controller, $action, $params];
  32. }
  33. }

3.2 自定义User的实现

3.2.1 自定义模型 UserModel.php

  1. <?php
  2. // 自定义User模型
  3. namespace model;
  4. use core\Model;
  5. class UserModel extends Model
  6. {
  7. public function __construct($dsn, $username, $password)
  8. {
  9. parent::__construct($dsn, $username, $password);
  10. }
  11. // 根据条件获取数据
  12. public function getLimitData($table,$num=10)
  13. {
  14. return $this->getData($table,$num);
  15. }
  16. }

3.2.2 自定义控制器 UserController.php

  1. <?php
  2. namespace controller;
  3. use core\Controller;
  4. use core\Model;
  5. use core\View;
  6. class UserController extends Controller
  7. {
  8. public function __construct(Model $model, View $view)
  9. {
  10. parent::__construct($model, $view);
  11. }
  12. // 获取user表中所有数据
  13. public function getLimitData($table,$num)
  14. {
  15. // 1. 选择模型: 获取数据
  16. $users = $this->model->getLimitData($table,$num);
  17. // 2. 加载视图
  18. // 路径约定: view/控制器/方法名.php
  19. $this->view->render('view/user/getLimitData.php', ['users' => $users]);
  20. }
  21. }

3.2.3 视图模板 getLimitData.php

  1. <!DOCTYPE html>
  2. <html lang="zh-CN">
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta http-equiv="X-UA-Compatible" content="IE=edge">
  6. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  7. <title>分页展示数据</title>
  8. <style>
  9. table {
  10. margin: auto;
  11. width: 600px;
  12. border-collapse: collapse;
  13. text-align: center;
  14. }
  15. table th,
  16. table td {
  17. border: 1px solid;
  18. padding: 5px;
  19. }
  20. table thead {
  21. background-color: lightcyan;
  22. }
  23. table caption {
  24. font-size: larger;
  25. margin-bottom: 8px;
  26. }
  27. body>p {
  28. display: flex;
  29. }
  30. p>a {
  31. text-decoration: none;
  32. color: #555;
  33. border: 1px solid;
  34. padding: 5px 10px;
  35. margin: 10px 2px;
  36. }
  37. .active {
  38. background-color: seagreen;
  39. color: white;
  40. border: 1px solid seagreen;
  41. }
  42. </style>
  43. </head>
  44. <body>
  45. <table>
  46. <caption><h3>用户信息表</h3></caption>
  47. <thead>
  48. <tr>
  49. <th>ID</th>
  50. <th>姓名</th>
  51. <th>性别</th>
  52. <th>邮箱</th>
  53. <th>注册时间</th>
  54. </tr>
  55. </thead>
  56. <tbody>
  57. <?php foreach ($users as $user) :extract($user) ?>
  58. <tr>
  59. <td><?=$id?>
  60. </td>
  61. <td><?=$name?>
  62. </td>
  63. <td><?=$sex?'女':'男' ?>
  64. </td>
  65. <td><?=$email?>
  66. </td>
  67. <td><?=date('Y-m-d H:i:s',$register_data)?>
  68. </td>
  69. </tr>
  70. <?php endforeach ?>
  71. </tbody>
  72. </table>
  73. </body>
  74. </html>

3.2.4 入口文件 index.php

  1. <?php
  2. namespace myMVC;
  3. use controller\UserController;
  4. use model\UserModel;
  5. use core\View;
  6. // 入口文件: 测试
  7. // 1.类的自动 composer的自动加载器
  8. require 'vendor/autoload.php';
  9. // 2. 实例化控制器
  10. $model = new UserModel('mysql:dbname=phpedu', 'root', 'root');
  11. $view = new View();
  12. $users = new UserController($model, $view);
  13. // 3. 调用控制器中的方法
  14. $users->getLimitData('user',15);

3.2.5 composer.json配置文件,实现自动加载

  1. {
  2. "autoload": {
  3. "psr-4": {
  4. "core\\": "core",
  5. "model\\": "model",
  6. "controller\\": "controller"
  7. }
  8. },
  9. "require": {}
  10. }

配置完成后需执行composer dumpautoload使之生效

3.3 预览

3.4 路由

路由的本质: 是从url中解析出控制器,控制器方法,以及方法的参数

  1. controller: 控制器类名
  2. method: 控制器中的某个方法名
  3. params: 参数列表,以数组形式

以路由的方式获取数据
http://phpedu.io/0824/myMVC/indexRouter.php/UserController/getLimitData/user&10

indexRouter.php

  1. <?php
  2. namespace myMVC;
  3. use controller\UserController;
  4. use model\UserModel;
  5. use core\View;
  6. use core\Router;
  7. // 入口文件: 测试
  8. // 1.类的自动 composer的自动加载器
  9. require 'vendor/autoload.php';
  10. $request = Router::parse();
  11. // 路由解析
  12. $controller = array_shift($request);
  13. $action = array_shift($request);
  14. // 将参数分割转为数组
  15. $params = explode('&', array_shift($request)[0]);
  16. // print_r($params);
  17. //生成新的控制器名称
  18. $controller = 'controller\\' . ucfirst($controller) . 'Controller';
  19. // 2. 实例化控制器
  20. $model = new UserModel('mysql:dbname=phpedu', 'root', 'root');
  21. $view = new View();
  22. $users = new UserController($model, $view);
  23. // 3. 调用控制器中的方法
  24. call_user_func_array([$users, 'getLimitData'], $params);

预览

声明:本文内容转载自脚本之家,由网友自发贡献,版权归原作者所有,如您发现涉嫌抄袭侵权,请联系admin@php.cn 核实处理。
全部评论
文明上网理性发言,请遵守新闻评论服务协议