13-mvc框架原理与实现方式
1、mvc原理
# mvc 与框架## 1.mvc 是什么1. m:model,模型(即数据来源),主要是针对数据库操作
2. v:view,视图,html 页面。视图由一个一个模板构成(模板是视图的一个具体展现或载体,视图是模板的一个抽象)
3. c:controller,控制器,用于mv之间的数据交互## 2.最简单的 mvc
就是一个可以显示数据库内容的模板## 3.分层后的mvc### 2.1 控制器(以下三个)1. 接受请求: 路由
2. 选择模型: CURD
3. 加载视图: 模板### 2.2 模型(功能是操作数据库)1. 查询构造器
2. 模型操作## 2.3 视图(以下两个)1. 模板赋值
2. 渲染视图
2、mvc的极简实现方式(一个页面)
<?php
//!数据库查询(model)
//第一步:连接数据库
$db = new PDO('mysql:dbname=phpedu','root','root');// 第二步:对数据库进行查询
$stmt = $db->prepare('SELECT * FROM `staff` LIMIT ?');// 第三步:数据绑定,获取指定的数据,确定获取的数据条数
$stmt->bindValue(1,5,PDO::PARAM_INT);// 第四步:执行上述操作,如果不进行数据绑定就需要在execute里面加入参数
$stmt->execute();// 第五步:把查询到的数据集保存到一个变量里备用
$staffs = $stmt->fetchAll(PDO::FETCH_ASSOC);
?><!-- 视图(view) -->
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>员工列表</title>
</head>
<body><!-- 第六步:foreach渲染数据 --><h3>员工列表</h3><?php foreach ($staffs as $staff) :extract($staff) ?><li><?=$id?>:<?=$name?> , <?=$sex ? '女':'男'?>(<?=$email?>)</li><?php endforeach?>
</body>
</html>
上述代码中,模型model和视图view都已经存在了,而控制器controller实际上已经有了,但是我们看不到,因为我们上面的业务逻辑太简单,直接把数据写进去了。没有通过controller让model和view进行数据交互。
3、分层的mvc
先写控制器controller,控制器写完写模型model,模型写完最后再写视图view。

其中,core里面包含模型类(Model.php),视图类(View.php),控制器类(controller.php)
Model.php
<?php// 模型类// 命名空间遵循psr-4
// 类名与文件名同名
// 当前类的命名空间与当前类所在的路径应该是一一对应的
namespace core;use PDO;// 视图,控制器和模型不能直接使用,因为这是底层的业务逻辑
// 是框架源码的一部分,不要让用户直接用,因为框架一旦更新
// 新的框架源码会把core目录下的所有代码全部覆盖
// 所以视图,控制器和模型里面的内容和类尽量把它们转为抽象的
// 或者干脆写个接口,让用户来进行实例化
// 模型抽象化(每个用户一张表),只允许通过子类使用
abstract class Model
{// 要把连接对象写成属性,因为这个对象它会在当前的模型类中多个方法中使用protected $db = null;// 1.连接数据库// 在实例化时能够自动连接,可以写在构造函数里public function __construct($dsn,$username,$password){$this->db = new PDO($dsn,$username,$password);}// 2.内置一些基本的底层操作,供用户的自定义模型用// 自定义模型:与某一个或某一张数据表相关的类// 2.1获取全部数据public function select($num){$sql = 'SELECT *FROM `staff` LIMIT ?;';$stmt = $this->db->prepare($sql);$stmt->bindParam(1,$num,PDO::PARAM_INT); // !这种方法可以确保当出错时我们知道错误在哪里// if($stmt->execute()){// return $stmt->fetchAll(PDO::FETCH_ASSOC);// }else{// print_r($stmt->errorInfo());// }// !也可以用简化版,因为出错概率较低$stmt->execute();return $stmt->fetchAll(PDO::FETCH_ASSOC);}// 2.2获取某个数据(id)public function getOne($id){$sql = 'SELECT *FROM `user` WHERE `id` = ?;';$stmt = $this->db->prepare($sql);$stmt->bindParam(1,$id,PDO::PARAM_INT); $stmt->execute();return $stmt->fetch(PDO::FETCH_ASSOC);}
}
View.php
<?php// 视图类namespace core;class view
{// 1.模板变量容器protected array $data = [];// 2.模板赋值public function assign(string $key,$value){$this->data[$key] = $value;}// 3.渲染视图// 渲染与传参可以同步完成// $path告诉数据显示在哪个页面中public function render(string $path,array $data = []){if($data){foreach($data as $key=>$value){$this->assign($key,$value);}}// 将模板变量数组展开为独立的变量,以方便传入到模板中使用extract($this->data);// 渲染/加载模板文件file_exists($path) ? include $path : die('模板不存在');}
}
Controller.php
<?php// 控制器类namespace core;abstract class Controller
{// 1.模型对象protected Model $model;// 2.视图对象protected View $view;// 3.实例化,初始化上面的模型对象,视图对象public function __construct(Model $model,View $view){$this->model = $model;$this->view = $view;}
}
autoload.php(自动加载器)
<?php// 注册类的自动加载器方法
spl_autoload_register(function($class){// require str_replace('\\','/',$class) . 'php';// 为了系统的兼容性,可以使用require str_replace('\\',DIRECTORY_SEPARATOR,$class) . '.php';// 这是一个可以代替 composer 的方法,参数是function,传入了一个类名,// 然后用str_replace这个函数,把类里面的命名空间,也就是反斜线替换成路径符,// 然后在后面加扩展名 .php 转变为类就行了// 这样就实现了了一个类的自动加载了(相当于require了一个命名空间+类名)
});
如果想要把这些类一个一个加载到项目中去,则需要针对不同的业务类型创建不同的控制器和模型,所以要创建一个controller文件夹
StaffController.php(用户自定义控制器)
虽然不是控制器基类,但是要求必须继承自控制器基类(父类)
<?php// 自定义控制器,必须继承自控制器基类(超类/父类)namespace controller;use core\Controller;
use core\Model;
use core\View;
use model\StaffModel;class StaffController extends Controller
{public function __construct(Model $model,View $view){// 里面这样写就冗余了// $this->model = $model;// $this->view = $view;//子类的构造函数直接调用父类的构造方法就可以了parent::__construct($model,$view);}// 自定义方法:默认方法// index():列出所有数据public function index($num = 10){// 1.选择模型:获取数据$staff = $this->model->getAll($num);// 2.加载视图// 路径约定:view/控制器/方法名.php// key值可以理解为变量名,相当于把$staff接收到的值赋给了名为staffs的变量$this->view->render('view/staff/index.php',['staffs'=>$staff]);}
}
创建类文件夹,里面都是自定义模型类
StaffModel.php(用户自定义模型类)
<?php
// 自定义模型namespace model;use core\Model;class StaffModel extends Model
{public function __construct($dsn,$username,$password){parent::__construct($dsn,$username,$password);}// 获取全部数据public function getAll($num){return $this->select($num);}
}
创建视图文件夹,在视图里再创建文件夹,staff文件夹对应是当前的控制器,里面的文件对应当前的方法。

staff>index.php
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>员工列表</title>
</head>
<body><!-- 第六步:foreach渲染数据 --><h3>员工列表</h3><?php foreach ($staffs as $staff) :extract($staff) ?><li><?=$id?>:<?=$name?> , <?=$sex ? '女':'男'?>(<?=$email?>)</li><?php endforeach?>
</body>
</html>
在mvc2文件夹下创建测试文件index.php
mvc2>index.php
<?php
namespace mvc2;use controller\StaffController;
use model\StaffModel;
use core\view;
// 入口文件:测试// 1.类的自动加载器
// 自动加载器主要用来加载间接用到的类,但是直接用到的类还是需要用use
require __DIR__ . '/core/autoload.php';// 路由解析// 2.实例化控制器
$model = new StaffModel('mysql:dbname=phpedu','root','root');
$view = new View();
// 实例化控制器对象
$staff = new StaffController($model,$view);// 3.调用控制器中的方法
$staff->index(3);
上述运行结果:
4、路由原理及应用
<?php// 路由的本质:是从url中解析出控制器,控制方法,以及方法的参数// 1.controller:控制器类名
// 2.method:控制器中的某个方法名
// 3.parameter:参数列表,以数组形式// 以上三种数据在url中的展示方式有两种
// 1.queryString:查询字符串
// 2.PATH_INFO:路径信息// phpedu.io/one/two/demo1.php?查询字符串,以键值对方式,和&分开
// !c:controller,m:method,p:parameter
// phpedu.io/one/two/demo1.php?c=hello&m=method&p=aaa
// 得到这个查询字符串以后,我们可以通过一些方法,将该字符串解析成数组,从而得到控制器,方法和参数//! 在我们的脚本名称 `phpedu.io/one/two/demo1.php` 和查询字符串 `c=hello&m=method&p=aaa`之间
// !如果又出现路径,我们用 PATH_INFO 表示 /user/index/100/200
// phpedu.io/one/two/demo1.php PATH_INFO ?c=hello&m=method&p=aaa
// phpedu.io/one/two/demo1.php/user/index/100/200?c=hello&m=method&p=aaa// /user/index/100/200:PATH_INFO
function p($data)
{echo is_array($data) ? sprintf('<pre>%s</pre>',print_r($data,true)) : $data;
}
p([1,2,3]);
p('Hello');
echo '<hr>';
// !QueryString: 查询字符串
// 超全局数组$_SERVER的QUERY_STRING键可以返回当前的查询字符串
p($_SERVER['QUERY_STRING']);
// 将查询字符串解析到数组里面
parse_str($_SERVER['QUERY_STRING'],$request);
p($request);// 人为认定
// c controller, m:method,name:parameter
$controller = array_shift($request);
$method = array_shift($request);
$params = array_shift($request);// 控制器类:测试专用
class HelloController
{public function world($name){return 'Hello ,' . $name;}
}// 生成控制器类名
$controller = ucfirst($controller) . 'Controller';
echo (new $controller)->$method($params);
// 一般用回调的方式来调用
// echo call_user_func_array([new $controller(),$method],[$params]);
echo call_user_func([new $controller(),$method],$params);
echo '<hr>';// ! 2.PATH_INFO:查询字符串与脚本之间的路径信息
//* http://phpedu.io/0824/router.php/hello/world/admin?c=hello&m=world&name=peter
//* PATH_INFO:/hello/world/admin
p($_SERVER['PATH_INFO']);
p(explode('/',$_SERVER['PATH_INFO']));
// 但这样之后发现索引0对应的值为空
// p(array_filter(explode('/',$_SERVER['PATH_INFO'])));
// 去除空字符也可以这样
$request = explode('/',trim($_SERVER['PATH_INFO'],'/'));$controller = array_shift($request);
$method = array_shift($request);
$params = array_shift($request);$controller = ucfirst($controller) . 'Controller';
echo call_user_func([new $controller(),$method],$params);
// 也可以 echo call_user_func_array([new $controller(),$method],[$params]);// !推荐使用PATH_INFO
// 通过url重写功能,可以将脚本的扩展名php隐藏,也可以在末尾自定义一个扩展名
//* 隐藏后的地址具有欺骗性:http://phpedu.io/0824/router/hello/world/admin.html/*** 总结:* 1.$_SERVER['QUERY_STRING']以键值对方式返回当前字符串,返回值是数组,需要用parse_str转换成字符串* 2.1.$_SERVER['PATH_INFO']返回带有/的字符串,返回值是字符串,需要用explode切割转换成数组*/
上述运行结果:
在mvc3文件夹下创建测试文件index.php
<?php
namespace mvc3;use model\UserModel;
use core\view;
use core\Router;
// 入口文件:测试// 1.类的自动加载器
require __DIR__ . '/core/autoload.php';// 路由解析
$request = Router::parse();$controller = array_shift($request);
$method = array_shift($request);
$params = array_shift($request);// 生成控制器名称
$controller ='controller\\' . ucfirst($controller) . 'Controller';// echo $controller;
// die;// 2.实例化控制器
$model = new UserModel('mysql:dbname=phpedu','root','root');
$view = new View();
// 实例化控制器对象
$user = new $controller($model,$view);// 3.调用控制器中的方法
call_user_func_array([$user,'get'],$params);
mvc3>core>router.php
<?phpnamespace core;// 路由器类
class Router
{public static function parse(): array{// 默认控制器,实际项目,应该来自配置文件,而不是在写死$controller = 'Index';$action = 'index';// 参数列表$params = [];// 判断是否存在pathinfoif (array_key_exists('PATH_INFO', $_SERVER) && $_SERVER['PATH_INFO'] !== '/') {// 为什么要判断 $_SERVER['PATH_INFO'] !== '/' ?// 因为: admin.php/ 时,$_SERVER['PATH_INFO'] = '/', 导致解析控制器失败$pathinfo = array_filter(explode('/', $_SERVER['PATH_INFO']));// dump($pathinfo);// 考虑到index.php/ 情况, 这时pathinfo为空数组if (count($pathinfo) >= 2) {$controller = array_shift($pathinfo);$action = array_shift($pathinfo);$params = $pathinfo;// $params = array_shift($pathinfo);} else {$controller = array_shift($pathinfo);}}// 查看控制器,方法,参数// dump($controller, $action, $params);// 将这些数据返回出去return [$controller, $action, $params];}
}
mvc3>model>UserModel
<?php
// 自定义模型namespace model;use core\Model;class UserModel extends Model
{public function __construct($dsn,$username,$password){parent::__construct($dsn,$username,$password);}// 获取全部数据public function get($id){return $this->getOne($id);}
}
mvc3>view>user>get
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>员工信息</title>
</head><body><!-- 第六步:foreach渲染数据 --><h3>员工信息</h3><?php if (is_array($user)) : ?><?php foreach ($user as $key => $value) : ?><!-- 如果不想拿 --><?php// if($value = null){// echo '该用户不存在';// }if ($key === 'password' || $key === 'register_time') {continue;}?><li>[<?= $key ?>] => <?= $value ?></li><?php endforeach ?><?php endif ?><?php if (is_array($user) == null) : ?><h3>此用户不存在</h3><?php endif ?>
</body></html>
上述运行结果:
相关文章:
13-mvc框架原理与实现方式
1、mvc原理 # mvc 与框架## 1.mvc 是什么1. m:model,模型(即数据来源),主要是针对数据库操作 2. v:view,视图,html 页面。视图由一个一个模板构成(模板是视图的一个具体展现或载体,视图是模板的一个抽象) 3. c:controller,控制器,用于mv之间的数据交互## 2.最简单的 mvc 就是一…...
弹性盒子布局
目录一、弹性盒子属性二、认识flex的坐标轴三、简单学习父级盒子属性三、属性说明3.1、flex-grow一、弹性盒子属性 说明: div的默认样式:display:block 块盒子 display:flex弹性盒子(可以控制下级盒子的位置) 当两种盒子单独出现…...
C# Sqlite数据库加密
sqlite官方的数据库加密是收费的,而且比较贵。 幸亏微软提供了一种免费的方法。 1 sqlite加密demo 这里我做了一个小的demo演示如下: 在界面中拖入数据库名、密码、以及保存的路径 比如我选择保存路径桌面的sqlite目录,数据库名guigutool…...
高压放大器在声波谐振电小天线收发测试系统中的应用
实验名称:高压放大器在声波谐振电小天线收发测试系统中的应用研究方向:信号传输测试目的:声波谐振电小天线颠覆了传统电小天线以电磁波谐振作为理论基础的天线发射和接收模式,它借助声波谐振实现电磁信号的辐射或接收。因为同频的…...
锁相环的组成和原理及应用
一.锁相环的基本组成 许多电子设备要正常工作,通常需要外部的输入信号与内部的振荡信号同步,利用锁相环路就可以实现这个目的。 锁相环路是一种反馈控制电路,简称锁相环(PLL)。锁相环的特点是:利用外部输入的参考信号控制环路内…...
[C++]string类模拟实现
目录 前言: 1. string框架构造 2. 默认函数 2.1 构造函数 2.2 析构函数 2.3 拷贝构造 2.4 赋值重载 3. 迭代器 4. 整体程序 前言: 本篇文章模拟实现了C中string的部分功能,有助于大家了解和熟悉string类,虽然这个类不难实…...
一个更适合Java初学者的轻量级开发工具:BlueJ
Java是世界上最流行的编程语言之一,它被广泛用于从Web开发到移动应用的各种应用程序。大部分Java工程师主要是用IDEA、Eclipse为主,这两个开发工具由于有强大的能力,所以复杂度上就更高一些。如果您刚刚开始使用Java,或者您更适合…...
从程序员到项目组长,要经历六重修炼
最近和粉丝朋友们交流时发现,有很多刚刚开始做项目组长的朋友自我认可度非常低,感觉做组长之后天天打杂,技术也荒废了。领导天天找你要成果,下属天天找你说困难,你在中间受领导和下属的夹板气。时间久了,你…...
我的 System Verilog 学习记录(5)
、 引言 本文简单介绍 System Verilog 语言的 控制流。 前文链接: 我的 System Verilog 学习记录(1) 我的 System Verilog 学习记录(2) 我的 System Verilog 学习记录(3) 我的 System Ver…...
多芯片设计 Designing For Multiple Die
Why a system-level approach is essential, and why its so challenging作者:Ann MutschlerAnn Mutschler is executive editor at Semiconductor Engineering.将多个裸片或芯粒集成到一个封装中,与将它们放在同一硅片上有着很大的区别。在同一硅片上&a…...
2022年全国职业院校技能大赛(中职组)网络安全竞赛试题A(10)
目录 竞赛内容 模块A 基础设施设置与安全加固 一、项目和任务描述: 二、服务器环境说明 三、具体任务(每个任务得分以电子答题卡为准) A-1任务一 登录安全加固(Windows, Linux) 1.密码策略(Windows, …...
数据结构-简介
目录 1、简介 2、作用 3、分类 4、实现分类 1、简介 数据结构指的是组织和存储数据的方法。它涉及到一系列的算法和原则,用来设计和实现不同种类的数据类型,如数组、链表、树、图等等。数据结构的目的是在计算机程序中有效地管理和操作数据ÿ…...
python装饰器及其用法
python装饰器是什么? Python装饰器是一种语法结构,它可以让开发者在不修改原函数的基础上,在函数的前后运行额外的代码,这些代码可以达到修改函数行为的目的。Python装饰器的实质是一个可调用的对象,它可以接收函数作为参数…...
Appium自动化测试之启动时跳过初始化设置
Appium每次启动时都会检查和安装Appium Settings,这是完全没有必要的,在首次使用Appium连接设备是Appium Settings便已经安装好。怎样跳过安装Appium Settings呢?之前的做法是修改appium中的源文件中的android-helpers.js实现,如M…...
JavaScript DOM【快速掌握知识点】
目录 DOM简介 获取元素 修改元素 添加和移除元素 事件处理 DOM简介 JavaScript DOM 是指 JavaScript 中的文档对象模型(Document Object Model);它允许 JavaScript 与 HTML 页面交互,使开发者可以通过编程方式动态地修改网页…...
不需要高深技术,只需要Python:创建一个可定制的HTTP服务器!
目录 1、编写服务端代码,命名为httpserver.py文件。 2、编写网页htmlcss文件,命名为index.html和style.css文件。 3、复制htmlcss到服务端py文件同一文件夹下。 4、运行服务端程序。 5、浏览器中输入localhost:8080显示如下: 要编写一个简单的能发布…...
渗透测试常用浏览器插件汇总
1、shodan这个插件可以自动探测当前网站所属的国家、城市,解析IP地址以及开放的服务和端口,包括但不限于FTP、DNS、SSH或者其他服务等,属被动信息搜集中的一种。2、hackbar(收费之后用Max Hackerbar代替)这个插件可用于…...
社区1月月报|OceanBase 4.1 即将发版,哪些功能将会更新?
我们每个月都会和大家展开一次社区进展的汇报沟通会,希望通过更多的互动交流让OceanBase 开源社区更加透明,实现信息共享,也希望能营造更加轻松的氛围,为大家答疑解惑,让大家畅所欲言。如果您对我们的社区有任何建议&a…...
Javascript的API基本内容(二)
一、事件监听 结合 DOM 使用事件时,需要为 DOM 对象添加事件监听,等待事件发生(触发)时,便立即调用一个函数。 addEventListener 是 DOM 对象专门用来添加事件监听的方法,它的两个参数分别为【事件类型】和…...
ChatGPT热度“狂飙”,OceanBase也去找它唠了唠
最近互联网的关键字 非 ChatGPT 莫属 就是这个小东西 集唠嗑、提问、答疑、科普、写作于一体 让我看看哪个孤独的打工人 还没和 ChatGPT 聊上一聊 有人说 ChatGPT 这么智能 或将取代人类的工作 OceanBase 的小编表示不服气 于是,抱着好奇之心试了一试 对 …...
第19节 Node.js Express 框架
Express 是一个为Node.js设计的web开发框架,它基于nodejs平台。 Express 简介 Express是一个简洁而灵活的node.js Web应用框架, 提供了一系列强大特性帮助你创建各种Web应用,和丰富的HTTP工具。 使用Express可以快速地搭建一个完整功能的网站。 Expre…...
TDengine 快速体验(Docker 镜像方式)
简介 TDengine 可以通过安装包、Docker 镜像 及云服务快速体验 TDengine 的功能,本节首先介绍如何通过 Docker 快速体验 TDengine,然后介绍如何在 Docker 环境下体验 TDengine 的写入和查询功能。如果你不熟悉 Docker,请使用 安装包的方式快…...
ubuntu搭建nfs服务centos挂载访问
在Ubuntu上设置NFS服务器 在Ubuntu上,你可以使用apt包管理器来安装NFS服务器。打开终端并运行: sudo apt update sudo apt install nfs-kernel-server创建共享目录 创建一个目录用于共享,例如/shared: sudo mkdir /shared sud…...
mongodb源码分析session执行handleRequest命令find过程
mongo/transport/service_state_machine.cpp已经分析startSession创建ASIOSession过程,并且验证connection是否超过限制ASIOSession和connection是循环接受客户端命令,把数据流转换成Message,状态转变流程是:State::Created 》 St…...
django filter 统计数量 按属性去重
在Django中,如果你想要根据某个属性对查询集进行去重并统计数量,你可以使用values()方法配合annotate()方法来实现。这里有两种常见的方法来完成这个需求: 方法1:使用annotate()和Count 假设你有一个模型Item,并且你想…...
Python实现prophet 理论及参数优化
文章目录 Prophet理论及模型参数介绍Python代码完整实现prophet 添加外部数据进行模型优化 之前初步学习prophet的时候,写过一篇简单实现,后期随着对该模型的深入研究,本次记录涉及到prophet 的公式以及参数调优,从公式可以更直观…...
基于数字孪生的水厂可视化平台建设:架构与实践
分享大纲: 1、数字孪生水厂可视化平台建设背景 2、数字孪生水厂可视化平台建设架构 3、数字孪生水厂可视化平台建设成效 近几年,数字孪生水厂的建设开展的如火如荼。作为提升水厂管理效率、优化资源的调度手段,基于数字孪生的水厂可视化平台的…...
Robots.txt 文件
什么是robots.txt? robots.txt 是一个位于网站根目录下的文本文件(如:https://example.com/robots.txt),它用于指导网络爬虫(如搜索引擎的蜘蛛程序)如何抓取该网站的内容。这个文件遵循 Robots…...
ip子接口配置及删除
配置永久生效的子接口,2个IP 都可以登录你这一台服务器。重启不失效。 永久的 [应用] vi /etc/sysconfig/network-scripts/ifcfg-eth0修改文件内内容 TYPE"Ethernet" BOOTPROTO"none" NAME"eth0" DEVICE"eth0" ONBOOT&q…...
用机器学习破解新能源领域的“弃风”难题
音乐发烧友深有体会,玩音乐的本质就是玩电网。火电声音偏暖,水电偏冷,风电偏空旷。至于太阳能发的电,则略显朦胧和单薄。 不知你是否有感觉,近两年家里的音响声音越来越冷,听起来越来越单薄? —…...
