博客列表 >PHP 简易MVC框架和容器的实现

PHP 简易MVC框架和容器的实现

Yang_Sir
Yang_Sir原创
2020年05月16日 17:24:02896浏览
  • 在代码越来越多,各种功能创建之后,就需要进行分层分类管理。依赖关系清晰,才能使程序有更好的维护性。

1.MVC框架

  • 分别建立模型类型、控制器类和视图类
  • 在控制器中调用模型获取数据,然后调用视图类输出数据展示

1.1 Model类

  • 该模型类功能主要是查询数据,所以直接继承Db数据库操作类

Model.php

  1. <?php
  2. namespace home;
  3. require 'Db.php';
  4. class Model extends Db
  5. {
  6. }

Db.php

  1. <?php
  2. namespace home;
  3. //数据库操作类
  4. class Db{
  5. public static $link = null;//保存pdo连接
  6. public static $sql = null;//保存语句
  7. //使用构造函数连接数据库
  8. public function __construct($dsn,$username,$password){
  9. try{
  10. self::$link = new \PDO($dsn,$username,$password);
  11. if(empty(self::$link)){
  12. echo '数据库连接失败';
  13. }
  14. }catch(\Exception $e){
  15. echo $e->getMessage();
  16. }
  17. }
  18. //新增数据
  19. public static function add(string $table,array $data){
  20. $val = array_values($data);
  21. $keys = array_map(function($item){
  22. return $item = '`'.$item.'`=?';
  23. },array_keys($data));
  24. $fields = implode(",",$keys);
  25. //预处理sql语句,使用占位符
  26. $sql = "insert {$table} set {$fields}";
  27. $stmt = self::$link->prepare($sql);
  28. //执行查询语句
  29. $stmt->execute($val);
  30. if($stmt->rowCount()===1){
  31. return self::$link->lastInsertId();//获取新增的主键ID
  32. }else{
  33. return $stmt->errorInfo();
  34. }
  35. }
  36. //查询数据
  37. public static function find(string $sql){
  38. //预处理sql语句
  39. $stmt = self::$link->prepare($sql);
  40. //执行查询语句
  41. $stmt->execute();
  42. return $stmt->fetchAll(\PDO::FETCH_ASSOC);
  43. }
  44. //更新数据
  45. public static function update(string $table,string $where,array $data)
  46. {
  47. $val = array_values($data);
  48. $keys = array_map(function($item){
  49. return $item = '`'.$item.'`=?';
  50. },array_keys($data));
  51. $fields = implode(",",$keys);
  52. //预处理sql语句,使用占位符
  53. self::$sql = "update `{$table}` SET {$fields} {$where}";
  54. $stmt = self::$link->prepare(self::$sql);
  55. //执行查询语句
  56. $stmt->execute($val);
  57. if($stmt->rowCount()){
  58. return $stmt->rowCount();//返回受影响的记录数
  59. }else{
  60. return $stmt->errorInfo();
  61. }
  62. }
  63. //删除数据
  64. public static function delete($table,$where)
  65. {
  66. $sql = "DELETE FROM `{$table}` {$where}";
  67. self::$sql=$sql;
  68. $stmt = self::$link->prepare($sql);
  69. $stmt->execute();
  70. if($stmt->rowCount()){
  71. return $stmt->rowCount();//返回受影响的记录数
  72. }else{
  73. return $stmt->errorInfo();
  74. }
  75. }
  76. }

1.2 View类

  • 把数据在goods.php模板文件中展示
  1. <?php
  2. namespace home;
  3. class View
  4. {
  5. public function fetch($data){
  6. include 'goods.php';
  7. }
  8. }

goods.php

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  6. <title>商品信息维护</title>
  7. <style>
  8. body {
  9. display: flex;
  10. flex-flow: column nowrap;
  11. align-items: center;
  12. }
  13. form {
  14. display: flex;
  15. flex-flow: row wrap;
  16. }
  17. form>section {
  18. margin: 10px;
  19. display: flex;
  20. flex-flow: row nowrap;
  21. }
  22. table {
  23. margin-top: 30px;
  24. width: 1000px;
  25. font-family: verdana, arial, sans-serif;
  26. font-size: 11px;
  27. color: #333333;
  28. border-width: 1px;
  29. border-color: #666666;
  30. border-collapse: collapse;
  31. }
  32. table>thead {
  33. background-color: #80ff80;
  34. }
  35. table th {
  36. border-width: 1px;
  37. padding: 8px;
  38. border-style: solid;
  39. border-color: #666666;
  40. }
  41. table td {
  42. border-width: 1px;
  43. padding: 8px;
  44. border-style: solid;
  45. border-color: #666666;
  46. background-color: #ffffff;
  47. text-align: center;
  48. }
  49. tfoot>tr,
  50. tfoot>tr>td {
  51. width: initial;
  52. }
  53. </style>
  54. </head>
  55. <body>
  56. <hr>
  57. <hr>
  58. <form action="<?php echo $_SERVER['PHP_SELF'] ?>" class="queryterms" method="POST">
  59. <section>
  60. <label for="goodsname">商品名称:</label>
  61. <input type="text" name="goodsname" id="goodsname"
  62. value="<?php if(isset($_SESSION['goodsname'])) echo $_SESSION['goodsname']?>">
  63. </section>
  64. <section>
  65. <label for="goodsmodel">商品型号:</label>
  66. <input type="text" name="goodsmodel" id="goodsmodel"
  67. value="<?php if(isset($_SESSION['goodsmodel'])) echo $_SESSION['goodsmodel']?>">
  68. </section>
  69. <section>
  70. <button>查询</button>
  71. </section>
  72. </form>
  73. <div>
  74. <table>
  75. <thead>
  76. <tr>
  77. <th>ID</th>
  78. <th>名称</th>
  79. <th>型号</th>
  80. <th>价格</th>
  81. <th>数量</th>
  82. <th>状态</th>
  83. <th>操作</th>
  84. </tr>
  85. </thead>
  86. <tbody>
  87. <?php foreach($data as $val):?>
  88. <tr>
  89. <td><?php echo $val['id'] ?></td>
  90. <td><?php echo $val['name']?></td>
  91. <td><?php echo $val['model']?></td>
  92. <td><?php echo $val['price']?></td>
  93. <td><?php echo $val['number']?></td>
  94. <td><?php echo $val['status']?></td>
  95. <td><a href="handle.php?act=edit&id=<?php echo $val['id'] ?>">编辑</a>
  96. &nbsp;&nbsp;&nbsp;
  97. <a href="handle.php?act=delete&id=<?php echo $val['id'] ?>">删除</a></td>
  98. </tr>
  99. <?php endforeach; ?>
  100. </tbody>
  101. <tfoot>
  102. <tr>
  103. <td colspan="7"><?php if(isset($page))echo $page; ?></td>
  104. </tr>
  105. </tfoot>
  106. </table>
  107. </div>
  108. </body>
  109. </html>

1.3 Controller类

  • 在Controller中实例化model和view类
  • 然后调用find方法获取数据,调用fetch方法展示数据
  1. <?php
  2. namespace home;
  3. require 'Model.php';
  4. require 'View.php';
  5. //.直接在方法内实例化外部类使用
  6. class Controller
  7. {
  8. public function index($dsn,$username,$password){
  9. $data = (new Model($dsn,$username,$password))::find('select * from goods limit 2');
  10. return (new View)->fetch($data);
  11. }
  12. }
  13. $ctrl = new Controller;
  14. $ctrl->index($dsn,$username,$password);

至此,就完成了一个最简单的mvc框架,Model类负责处理数据、view输出展示数据,controller类负责接收处理请求和返回数据

效果图:

2. 容器

  • 以上mvc框架中的congtroller在类中直接实例化外部类,对外部类依赖性很强,代码耦合度高,如Model类的参数发生变化,congtroller类中也需要同步修改.
  • 如Model类中重写构造方法为不传参,而是引入文件,以上的congtroller类就需要修改
  1. <?php
  2. namespace home;
  3. require 'Db.php';
  4. class Model extends Db
  5. {
  6. public function __construct(){
  7. if(file_exists('config.php')){
  8. include 'config.php';
  9. }
  10. try{
  11. self::$link = new \PDO($dsn,$username,$password);
  12. if(empty(self::$link)){
  13. echo '数据库连接失败';
  14. }
  15. }catch(\Exception $e){
  16. echo $e->getMessage();
  17. }
  18. }
  19. }

2.1 参数依赖

  • 在此,我们可以通过参数方式传递外部类的实例来解耦
  1. // 在参数中传入外部类的实例
  2. class Controller
  3. {
  4. public function index(Model $model,view $view){
  5. $data = $model::find('select * from goods limit 2,3');
  6. return $view->fetch($data);
  7. }
  8. }
  9. $ctrl = new Controller;
  10. $ctrl->index(new Model,new View);

效果图:

通过参数传递类实例的方式,成功把模型和视图类的实例创建放到了客户端,代码涉及改造量也大大降低。但是依赖的类很多时,要一个个的创建类的实例,也比较麻烦

2.2 服务容器

  • 为解决以上问题,我们可以创建一个容器,把所有类的实例化步骤都放到该容器中,使用时直接用该类的别名即可获得该类的实例。
  • 在容器类中有两个方法,一是绑定,将类在容器中注册服务;一是取出,根据注册的类的别名获取类的实例

服务容器:

  1. <?php
  2. namespace home;
  3. //服务容器类
  4. class Container
  5. {
  6. public $binds = [];
  7. //绑定服务到容器
  8. public function bind($alias,\Closure $process){
  9. $this->binds[$alias] = $process;
  10. }
  11. //使用
  12. public function make($alias,array $param=[]){
  13. return call_user_func_array($this->binds[$alias],$param);
  14. }
  15. }

容器依赖:

  1. class Controller3
  2. {
  3. public function index(Container $container){
  4. //从容器中取得model和view类的对象实例
  5. $data = $container->make('Model')::find('select * from goods limit 6,5');
  6. return $container->make('View')->fetch($data);
  7. }
  8. }
  9. $container = new Container;
  10. //注册服务,绑定类到容器中
  11. $container->bind('Model',function(){return new Model();});
  12. $container->bind('View',function(){return new View;});
  13. $container->bind('controller',function(){return new Controller3;});
  14. //使用服务
  15. $container->make('controller')->index($container);

效果图:

服务容器就像一个全局变量,把所有服务类放到容器中,使用时根据注册的名字即可直接获得实例

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