博客列表 >1206_迷你MVC框架 第30课

1206_迷你MVC框架 第30课

叮叮当当
叮叮当当原创
2019年12月16日 14:08:48610浏览

1.独立完成一个迷你的MVC框架

目录概览:

1. config目录里dbconfig.php

  1. <?php
  2. $db_params = array(
  3. 'charset' => 'utf8',
  4. 'port' => 3306,
  5. 'type' => 'mysql',
  6. 'host' => '127.0.0.1',
  7. 'user' => 'root',
  8. 'pwd' => 'root',
  9. 'dbname' => 'SqlTest'
  10. );
  11. return $db;

此目录里专门放一些配置文件

2.1 model目录里db.php

  1. namespace homework\model;
  2. use PDO;
  3. # 单例模式
  4. class Db{
  5. # 实例
  6. private static $instance;
  7. # pdo连接
  8. private $pdo = null;
  9. # 数据表名
  10. private $table = '';
  11. # 字段列表
  12. private $field = '';
  13. # 查询条件
  14. private $where = '';
  15. # 排序条件
  16. private $order = '';
  17. # 新增/修改条件
  18. private $data = '';
  19. # execute条件
  20. private $exe = [];
  21. # 显示数量
  22. private $limit = 0;
  23. private function __construct( $db ){
  24. $dsn = "mysql:host={$db['host']};dbname={$db['dbname']};charset={$db['charset']}";
  25. try {
  26. $this->pdo = new PDO( $dsn, $db['user'], $db['pwd'] );
  27. } catch (\PDOException $e) {
  28. die('数据库错误:'.$e->getMessage());
  29. }
  30. }
  31. # 获取实例
  32. public static function getInstance( $db ){
  33. # 因这里是单例,没有必要用后期绑定static
  34. if( !(self::$instance instanceof self) ){
  35. self::$instance = new self( $db );
  36. }
  37. return self::$instance;
  38. }
  39. # 禁用克隆方法
  40. private function __clone(){
  41. }
  42. # 获取表名
  43. public function table( $table ){
  44. $this->table = '`' .$table. '`';
  45. # 返回当前对象,便于链式调用该对象的其它方法
  46. return $this;
  47. }
  48. # 设查询字段
  49. public function field( $fields ){
  50. # 若参数是数组,['id','user_name'],拆开拼接
  51. # 若是字符串,'id, user_name' 直接拼接
  52. if( is_array($fields) ){
  53. foreach( $fields as $field ){
  54. $this->field .= '`' .$field. '`, ';
  55. }
  56. # 去掉多余逗号
  57. $this->field = rtrim( trim($this->field),',' );
  58. }
  59. else{
  60. $this->field = $fields;
  61. }
  62. return $this;
  63. }
  64. # 设查询条件 (参数个数不定,将参数压到 $where 里)
  65. public function where( ...$where ){
  66. $w = $this->conditionHandler( $where, $condition =' AND ');
  67. if( empty($this->where) ){
  68. $this->where = ltrim( trim($w),$condition );
  69. }
  70. else{
  71. $this->where .= $w;
  72. }
  73. return $this;
  74. }
  75. # 设查询条件 (参数个数不定,将参数压到 $where 里)
  76. public function whereOr( ...$where ){
  77. $w = $this->conditionHandler( $where, $condition =' OR ');
  78. if( empty($this->where) ){
  79. $this->where = ltrim( trim($w),$condition );
  80. }
  81. else{
  82. $this->where .= $w;
  83. }
  84. return $this;
  85. }
  86. # 设排序条件 (参数个数不定,将参数压到 $order 里)
  87. public function order( ...$order ){
  88. # [ 'id','desc' ]
  89. # [ 'id' ] / [ 'id desc' ]
  90. if( count($order)>1 ){
  91. $this->order = '`' .$order[0]. '` '.$order[1];
  92. }
  93. else{
  94. $arr = explode( ' ',trim($order[0]));
  95. if( count( $arr )==1 ){
  96. $this->order = '`' .$order[0]. '` asc';
  97. }
  98. else{
  99. $this->order = '`' .$arr[0]. '` '.$arr[1];
  100. }
  101. }
  102. return $this;
  103. }
  104. # 设显示数量
  105. public function limit( $limit ){
  106. $this->limit = intval( $limit );
  107. return $this;
  108. }
  109. /**
  110. * @param $where 条件: [ ['id'=>1] ] 或 [ 'id', 1 ]
  111. * @param $condition 拼接关键词: and 或 or
  112. * @return string
  113. */
  114. private function conditionHandler( $where, $condition ){
  115. $w = '';
  116. # 传的数组 (多用于同时多个条件),分2种,[ 'id'=>1 ] / [ 'id'=>['>',1 ] ]
  117. # 传的不是数组 (多用于一个条件),'id', 1 / 'id', '>', 1
  118. if( is_array($where[0]) ){
  119. foreach( $where[0] as $k => $v ){
  120. $w .= $condition;
  121. if( is_array($v) ){
  122. $w .= '`' .$k. '`' .$v[0]. ':' .$k;
  123. $this->exe[ $k ] = $v[1];
  124. }
  125. else{
  126. $w .= '`' .$k. '`=:' .$k;
  127. $this->exe[ $k ] = $v;
  128. }
  129. }
  130. }
  131. elseif( is_string($where[0]) ){
  132. $w .= $condition;
  133. if( count($where)==3 ){
  134. $w .= '`' .$where[0]. '`' .$where[1]. ':' .$where[0];
  135. $this->exe[ $where[0] ] = $where[2];
  136. }
  137. elseif( count($where)==2 ){
  138. $w .= '`' .$where[0]. '`=:' .$where[0];
  139. $this->exe[ $where[0] ] = $where[1];
  140. }
  141. }
  142. return $w;
  143. }
  144. # 新增/修改数据条件
  145. private function data( $where ){
  146. $w = '';
  147. foreach( $where as $k => $v ){
  148. $w .= ', `' .$k. '`=:' .$k;
  149. $this->exe[ $k ] = $v;
  150. }
  151. if( empty($this->data) ){
  152. $this->data = ltrim( trim($w), ',' );
  153. }
  154. else{
  155. $this->data .= $w;
  156. }
  157. return $this->data;
  158. }
  159. # 查询结果
  160. public function find( $multi=false ){
  161. $fields = empty($this->field) ? '*' : $this->field;
  162. $where = empty($this->where) ? '' : ' WHERE '.$this->where;
  163. $order = empty($this->order) ? '' : ' ORDER BY '.$this->order;
  164. $limit = empty($this->limit) ? '' : ' LIMIT '.$this->limit;
  165. # 接装SQL语句
  166. $sql = 'SELECT '.$fields.' FROM '.$this->table. $where .$order. $limit;
  167. # 预处理查询
  168. $stmt = $this->pdo->prepare($sql);
  169. $stmt->execute($this->exe);
  170. if( $multi ){
  171. return $stmt->fetchAll(PDO::FETCH_ASSOC);
  172. }
  173. else{
  174. return $stmt->fetch(PDO::FETCH_ASSOC);
  175. }
  176. }
  177. # 查询结果
  178. public function select(){
  179. return $this->find(true);
  180. }
  181. public function get( $obj,$multi=false ){
  182. $fields = empty($this->field) ? '*' : $this->field;
  183. $where = empty($this->where) ? '' : ' WHERE '.$this->where;
  184. $order = empty($this->order) ? '' : ' ORDER BY '.$this->order;
  185. $limit = empty($this->limit) ? '' : ' LIMIT '.$this->limit;
  186. # 接装SQL语句
  187. $sql = 'SELECT '.$fields.' FROM '.$this->table. $where .$order. $limit;
  188. # 预处理查询
  189. $stmt = $this->pdo->prepare($sql);
  190. $stmt->setFetchMode(PDO::FETCH_CLASS, $obj );
  191. $stmt->execute($this->exe);
  192. if( $multi ){
  193. return $stmt->fetchAll();
  194. }
  195. else{
  196. return $stmt->fetch();
  197. }
  198. }
  199. public function getAll( $obj ){
  200. return $this->get( $obj,true);
  201. }
  202. # 插入数据
  203. public function insert( $where ){
  204. $this->data = $this->data( $where );
  205. $data = ' SET '.$this->data;
  206. # 接装SQL语句
  207. $sql = 'INSERT INTO ' .$this->table. $data;
  208. # 预处理查询
  209. $stmt = $this->pdo->prepare($sql);
  210. $stmt->execute($this->exe);
  211. if( $stmt->rowCount()>0 ){
  212. echo '插入成功';
  213. }
  214. }
  215. # 更新数据
  216. public function update( $where ){
  217. $this->data = $this->data( $where );
  218. $data = ' SET '.$this->data;
  219. $where = empty($this->where) ? '' : ' WHERE '.$this->where;
  220. # 接装SQL语句
  221. $sql = 'UPDATE ' .$this->table. $data . $where;
  222. # 预处理查询
  223. $stmt = $this->pdo->prepare($sql);
  224. $stmt->execute($this->exe);
  225. if( $stmt->rowCount()>0 ){
  226. echo '更新成功';
  227. }
  228. }
  229. # 更新数据
  230. public function delete(){
  231. $where = empty($this->where) ? '' : ' WHERE '.$this->where;
  232. # 接装SQL语句
  233. $sql = 'DELETE FROM ' .$this->table. $where;
  234. # 预处理查询
  235. $stmt = $this->pdo->prepare($sql);
  236. $stmt->execute($this->exe);
  237. if( $stmt->rowCount()>0 ){
  238. echo '删除成功';
  239. }
  240. }
  241. # 数据数量
  242. public function count(){
  243. $where = empty($this->where) ? '' : ' WHERE '.$this->where;
  244. # 接装SQL语句
  245. $sql = 'SELECT count(*) as count FROM ' .$this->table. $where;
  246. # 预处理查询
  247. $stmt = $this->pdo->prepare($sql);
  248. $stmt->execute($this->exe);
  249. $row = $stmt->fetch(PDO::FETCH_ASSOC);
  250. return $row['count'];
  251. }
  252. }

这里使用单例,查询获取时采用链式调用,主要进行数据库的增删改查

2.2 model目录里ZsgcModel.php

  1. namespace homework\model;
  2. class ZsgcModel{
  3. private $id;
  4. private $user_name;
  5. private $pwd;
  6. private $email;
  7. private $sex;
  8. private $des;
  9. private $hobby;
  10. private $create_time;
  11. private $update_time;
  12. private $status;
  13. # 属性重载
  14. public function __get( $name ){
  15. # 自行需要进行一些验证
  16. return $this->$name;
  17. }
  18. public function __set($name, $value)
  19. {
  20. $this->$name = $value;
  21. }
  22. public function __construct()
  23. {
  24. # 设置属性值的自动转换
  25. $this->create_time = $this->create_time ? date('Y/m/d', $this->create_time): null;
  26. $this->update_time = $this->update_time ? date('Y/m/d', $this->update_time): null;
  27. $this->sex = $this->sex ? ((intval($this->sex)-1) ? '女':'男') : '未知';
  28. $this->status = $this->status ? '正常' : '异常';
  29. }
  30. }

这里是模型与数据表之间的映射,自动转换处理一些特定字段

3.1 view目录里iView.php

  1. namespace homework\view;
  2. # 视图接口
  3. interface iView{
  4. public function fetch( ...$params );
  5. }

这里是视图接口,后续继承此接口的实例,都需实现fetch方法

3.2 view目录里ZsgcView.php

  1. namespace homework\view;
  2. class ZsgcView implements iView {
  3. public function fetch( ...$params ){
  4. $fields = [];
  5. # 是否传第二个参数$fields
  6. if( count($params)>1 ) {
  7. # 数组格式还是string格式
  8. if (is_array($params[1])) {
  9. $fields = $params[1];
  10. }
  11. elseif (is_string($params[1])) {
  12. $arr = explode(',', trim($params[1]));
  13. for ($i = 0; $i < count($arr); $i++) {
  14. array_push($fields, trim($arr[$i]));
  15. }
  16. }
  17. }
  18. $head = '<table>';
  19. $head .= '<caption>Zsgc用户表</caption>';
  20. $body = '';
  21. $flag = true; # 是否进入标题switch
  22. # 拼接 table
  23. if( $fields ){
  24. foreach( $params[0] as $user ){
  25. $body .= '<tr>';
  26. foreach( $fields as $v ){
  27. if( $v =='pwd' ) continue; # 不允许显示pwd
  28. if( $flag ){
  29. switch( $v ){
  30. case 'id': $head .= '<th>ID</th>';break;
  31. case 'user_name': $head .= '<th>用户名</th>'; break;
  32. case 'email': $head .= '<th>邮箱</th>'; break;
  33. case 'hobby': $head .= '<th>爱好</th>'; break;
  34. case 'des': $head .= '<th>描述</th>'; break;
  35. case 'sex': $head .= '<th>性别</th>'; break;
  36. case 'status': $head .= '<th>状态</th>'; break;
  37. case 'create_time': $head .= '<th>添加时间</th>'; break;
  38. case 'update_time': $head .= '<th>更新时间</th>'; break;
  39. }
  40. }
  41. if( is_object($user) ){
  42. $body .= '<td>' .$user->$v. '</td>';
  43. }
  44. else{
  45. $body .= '<td>' .$user[$v]. '</td>';
  46. }
  47. }
  48. $body .= '</tr>';
  49. $flag = false; # 第一遍循环后就可以关掉了
  50. }
  51. }
  52. else{
  53. $head .= '<tr><th>ID</th><th>用户名</th><th>邮箱</th><th>爱好</th><th>描述</th>';
  54. $head .= '<th>性别</th><th>状态</th><th>添加时间</th><th>更新时间</th></tr>';
  55. if( is_object( $params[0][0] ) ){
  56. foreach( $params[0] as $user ) {
  57. $body .= '<tr>';
  58. $body .= '<td>' .$user->id. '</td>';
  59. $body .= '<td>' .$user->user_name. '</td>';
  60. $body .= '<td>' .$user->email. '</td>';
  61. $body .= '<td>' .$user->hobby. '</td>';
  62. $body .= '<td>' .$user->des. '</td>';
  63. $body .= '<td>' .$user->sex. '</td>';
  64. $body .= '<td>' .$user->status. '</td>';
  65. $body .= '<td>' .$user->create_time. '</td>';
  66. $body .= '<td>' .$user->update_time. '</td>';
  67. $body .= '</tr>';
  68. }
  69. }
  70. else{
  71. foreach( $params[0] as $user ) {
  72. $body .= '<tr>';
  73. $body .= '<td>' .$user['id']. '</td>';
  74. $body .= '<td>' .$user['user_name']. '</td>';
  75. $body .= '<td>' .$user['email']. '</td>';
  76. $body .= '<td>' .$user['hobby']. '</td>';
  77. $body .= '<td>' .$user['des']. '</td>';
  78. $body .= '<td>' .$user['sex']. '</td>';
  79. $body .= '<td>' .$user['status']. '</td>';
  80. $body .= '<td>' .$user['create_time']. '</td>';
  81. $body .= '<td>' .$user['update_time']. '</td>';
  82. $body .= '</tr>';
  83. }
  84. }
  85. }
  86. $body .= '</table>';
  87. return $head.$body;
  88. }
  89. }
  90. echo '<style>
  91. table {border-collapse: collapse; border: 1px solid; width: auto; height: 150px; }
  92. caption {font-size: 1.2rem; margin-bottom: 10px; }
  93. tr:first-of-type {background-color: lightblue; }
  94. td,th {border: 1px solid}
  95. td:first-of-type {text-align: center}
  96. </style>';

1)这里参数,第一个为数据,第二个为列fields [可选]
2)若第二个参数没传,默认展示所有列数据,若传了,则展示fields里指定的列数据
3)第一个参数为数组,其第二层数据分为数组和object两种:
若为数组:是PDO获取时没有setFetchMode,直接返回数据库里原样数据
若为Object,是PDO获取时设置了setFetchMode为ZsgcModel类,会自动转换一些字段

4 controller目录里ZsgcController.php

  1. namespace homework\controller;
  2. use homework\model\Db;
  3. use homework\view\iView;
  4. use homework\model\ZsgcModel;
  5. class ZsgcController{
  6. private $db;
  7. private $view;
  8. public function __construct(Db $db, iView $view){
  9. $this->db = $db;
  10. $this->view = $view;
  11. }
  12. public function index(){
  13. # 两种表达方式都可以
  14. //$fields = ['id','user_name','email','hobby'];
  15. $fields = ' id , user_name , email,hobby';
  16. # 获取的是数组
  17. $data = $this->db->table('zsgc')
  18. ->field( $fields )
  19. ->where('id','>=',35)
  20. ->whereOr('email','!=','')
  21. ->select();
  22. return $this->view->fetch( $data, $fields );
  23. // $data = $this->db->table('zsgc')
  24. // ->where('id','>=',35)
  25. // ->whereOr('email','!=','')
  26. // ->select();
  27. //
  28. // return $this->view->fetch( $data );
  29. # 获取的是object
  30. // $data = $this->db->table('zsgc')
  31. // ->field( $fields )
  32. // ->where('id','>=',35)
  33. // ->whereOr('email','!=','')
  34. // ->getAll(ZsgcModel::class);
  35. //
  36. // return $this->view->fetch( $data, $fields );
  37. // $data = $this->db->table('zsgc')
  38. // ->where('id','>=',35)
  39. // ->whereOr('email','!=','')
  40. // ->getAll(ZsgcModel::class);
  41. //
  42. // return $this->view->fetch( $data );
  43. }
  44. }

1)这里仅举例了查询类的几种情况,可自行在此控制器里增加其他方法,写入新增/更新/删除等情况,客户端再指定调用即可
2)这里控制器需注入两对象,一个是Db对象,用来获取数据;一个是接口对象即视图对象,用来展示数据

5 自动加载autoload.php

  1. spl_autoload_register( function ($className){
  2. $path = str_replace('\\', DIRECTORY_SEPARATOR, $className);
  3. require dirname(__DIR__) . DIRECTORY_SEPARATOR . $path . '.php';
  4. });

自动加载 是让命名空间的路径和文件所在路径一致

6 容器层Container.php

  1. namespace homework;
  2. use Closure;
  3. require __DIR__ . '/autoload.php';
  4. # 服务容器
  5. class Container{
  6. # 实例数组
  7. public $instance = [];
  8. # 闭包数组
  9. public $binds = [];
  10. # 将实例/闭包绑定到对应的数组中
  11. # $alias 别名,$concrete 实例/闭包
  12. public function bind( $alias, $concrete ){
  13. # 编码约定,它的构造方法第一个参数必须是容器
  14. if( $concrete instanceof Closure){
  15. $this->binds[$alias] = $concrete;
  16. }
  17. else{
  18. $this->instance[$alias] = $concrete;
  19. }
  20. }
  21. # 将实例/闭包从容器中取出来
  22. # $alias 别名,$parameters 依赖对象
  23. public function make($alias,$parameters = [] ){
  24. if( isset($this->instance[$alias]) ){
  25. return $this->instance[$alias];
  26. }
  27. # 用容器来创建所依赖的对象/类实例
  28. # $this 是当前容器对象, 将$this插入到$parameters里最前面
  29. array_unshift( $parameters, $this );
  30. # 执行闭包,自动生成所依赖的外部对象/类实例
  31. # 执行 $this->binds[$alias]数组里对应的闭包,并传参数 $parameters
  32. return call_user_func_array( $this->binds[$alias], $parameters );
  33. }
  34. }

1)这里使用容器绑定,所有实例通过容器统一处理
2)先通过bind方法将对应实例/闭包方法放到实例或闭包数组中,且指定key为自定义别名
3)待执行make方法时,通过回调方法call_user_func_array,执行闭包数组里指定key的闭包,并传入参数
4)若为依赖对象,参数只有一个为容器对象;若为调用类,除容器对象,还会有依赖对象参数

7 客户端index.php

  1. use homework\model\Db;
  2. use homework\view\ZsgcView;
  3. use homework\controller\ZsgcController;
  4. use homework\Container;
  5. require __DIR__. '/config/dbconfig.php';
  6. require __DIR__. '/autoload.php';
  7. # 客户端调用
  8. # 实例化容器类
  9. $container = new Container();
  10. # 闭包里没用到$container,也可以不传$container
  11. # 这里用到配置文件里的参数,用use传入
  12. $container->bind('Db', function (Container $container) use ($db_params) {
  13. return Db::getInstance( $db_params );
  14. });
  15. $container->bind('ZsgcView', function (Container $container) {
  16. return new ZsgcView();
  17. });
  18. # 调用类,肯定会传依赖对象,这里有两个依赖对象 $db, $view
  19. # 实例化ZsgcController,需注入两参数,而该两参数,也由make方法获取 (通过回调方法执行对应的闭包)
  20. $container->bind( 'ZsgcController', function( Container $container, $db, $view ){
  21. return new ZsgcController( $container->make( $db ), $container->make( $view ) );
  22. } );
  23. $zsgc = $container->make('ZsgcController', ['Db','ZsgcView']);
  24. echo $zsgc->index();

这里使用容器的控制反转,除了绑定依赖对象,调用类也绑定到其中,统一处理

8 运行( 控制器里只写了查询方法,所以展示的都是查询小案例, 代码在Zsgc控制器里 )

case1:数据库原样数据,指定列

case2:数据库原样数据,不指定列

case3:通过模型转换过一些字段数据,指定列

case4:通过模型转换过一些字段数据,不指定列

小结:老师给的要求很宽泛,知识点单个拎出来也能明白,就是感觉这也可能组合,那也可能组合,反而不知到从何处下手,拖拖拉拉好几天,就是不想动手,拖到最后没办法了,硬着头皮做,开始思绪还是比较杂的,后面理着理着就顺了,mvc小框架就先到这吧,还是赶紧去补剩下的作业了

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