前言
下面准备审一下Yii、laravel、tp,但这些框架都是基于MVC模式的,所以先简单了解下MVC模式。
MVC是什么
MVC(Model-View-Controller)是软件工程的一种软件架构模式。
在MVC模式设计下,软件系统被分来三个模块:模型(Model)、视图(VIew)、控制器(Controller)。
-
Model(模型):是应用程序中用于处理应用程序数据逻辑的部分,通常负责与数据库直接进行 curd 的交互。
-
View(视图):是应用程序处理数据显示的部分,通过将处理好的数据进行渲染。
-
Controller(控制器):是应用程序中处理用户交互的部分,通常用于处理数据逻辑的分发,并相应的反送给视图和控制器。
简而言之View就是我们所看到的界面,Model就是View所显示界面的后端数据,而Controller就相当于是View和Model之间的桥梁
为什么使用MVC框架
在PHP中使用MVC框架,可以实现了分层、分类开发,实现了web的分离,使前端代码与后端分离,某一层的调整,不会对另一层的代码和逻辑造成影响,使用MVC开发框架更加方便程序的扩展,使开发的代码整体更加清晰
MVC框架
目录结构
- app目录:应用目录(多模块)
- 单模块:即一个主界面,其中包含
Model
、View
、Controller
- 多模块:有多个主界面,并都包含
Model
、View
、Controller
- 单模块:即一个主界面,其中包含
- config目录: 配置文件目录
- Sentiment目录:核心文件目录
- public目录: WEB目录(对外访问目录)
url访问控制器
先在app目录下写个控制器,这时通过public下的index.php直接访问控制器是访问不到的
现在就是需要对入口文件index.php进行配置,先获取下边三个路径,方便对mvc路径进行构造
<?php
define('APP_PATH',dirname(__DIR__));
$script_name=$_SERVER['SCRIPT_NAME']; // /public/index.php
$request_url=$_SERVER['REQUEST_URI']; // /public/index.php/index/home/index?id=123&titile=456
$query_string=$_SERVER['QUERY_STRING']; // id=123&titile=456
?>
之后就是需要取出mvc中需要的部分,并通过$action调用对应的index方法
<?php
define('APP_PATH',dirname(__DIR__));
$script_name = $_SERVER['SCRIPT_NAME']; // /public/index.php
$request_url = $_SERVER['REQUEST_URI']; // /public/index.php/index/home/index?id=123&titile=456
$query_string = $_SERVER['QUERY_STRING']; // id=123&titile=456
// 需要 index/home/index
$url = str_replace($script_name,'',$request_url); // /index/home/index?id=123&titile=456
$url = str_replace($query_string,'',$url); // /index/home/index?
$url = ltrim($url,'/'); // index/home/index?
$url = rtrim($url,'?'); // index/home/index
$url = explode('/',$url);
$model = $url[0];
$controller = $url[1];
$action = $url[2];
$path = APP_PATH.'/app/'.$model.'/controller/'.$controller.'.php';
require_once $path;
$new = new $controller;
$new ->$action();
?>
结果:
之后可以优化一下mvc的赋值模式(if/else 避免因用户传参问题导致无法访问对应的index方法),但这种方式若想调用不同的方法 每次都需要修改else中的值,因此可以通过config配置文件 进行修改
<?php
define('APP_PATH',dirname(__DIR__));
$script_name = $_SERVER['SCRIPT_NAME']; // /public/index.php
$request_url = $_SERVER['REQUEST_URI']; // /public/index.php/index/home/index?id=123&titile=456
$query_string = $_SERVER['QUERY_STRING']; // id=123&titile=456
// 需要 index/home/index
$url = str_replace($script_name,'',$request_url); // /index/home/index?id=123&titile=456
$url = str_replace($query_string,'',$url); // /index/home/index?
$url = ltrim($url,'/'); // index/home/index?
$url = rtrim($url,'?'); // index/home/index
$url = explode('/',$url);
if(count($url) >= 3){
$model = $url[0];
$controller = $url[1];
$action = $url[2];
} else {
$model = 'index';
$controller = 'home';
$action = 'index';
}
$path = APP_PATH.'/app/'.$model.'/controller/'.$controller.'.php';
require_once $path;
$new = new $controller;
$new ->$action();
?>
/config/app.php
<?php
return[
'default_model'=>'index',
'default_controller'=>'Home',
'default_action'=>'index'
];
之后将else部分替换成
else {
$config=require_once APP_PATH.'/config/app.php';
$model = $config['default_model'];
$controller = $config['default_controller'];
$action = $config['default_action'];
}
此时访问/public/index.php/
,即可默认调用/app/index/Home/的index方法
但其实不需要很多入口文件,所以这里就把原本的index.php
入口文件构建到核心文件Sentiment中,除此之外如果count >=3时,若访问不存在的控制器例如/index/index/index,他还是会报错,所以通过下边的三个if语句,再次进行优化
/Sentient/Sentiment.php
<?php
class Sentiment
{
public static function init()
{
$script_name = $_SERVER['SCRIPT_NAME']; // /public/index.php
$request_url = $_SERVER['REQUEST_URI']; // /public/index.php/index/home/index?id=123&titile=456
$query_string = $_SERVER['QUERY_STRING']; // id=123&titile=456
// 需要 index/home/index
$url = str_replace($script_name,'',$request_url); // /index/home/index?id=123&titile=456
$url = str_replace($query_string,'',$url); // /index/home/index?
$url = ltrim($url,'/'); // index/home/index?
$url = rtrim($url,'?'); // index/home/index
$url = explode('/',$url);
if(count($url) >= 3){
$model = $url[0];
$controller = $url[1];
$action = $url[2];
} else {
$config=require_once APP_PATH.'/config/app.php';
$model = $config['default_model'];
$controller = $config['default_controller'];
$action = $config['default_action'];
}
//判断应用模块
if(!is_dir(APP_PATH.'/app/'.$model)){
exit("应用模块<".$model.">不存在!");
}
$path = APP_PATH.'/app/'.$model.'/controller/'.$controller.'.php';
//判断控制器
if(!file_exists($path)){
exit("控制器<".$controller.">不存在!");
}
require_once $path;
$new = new $controller;
//判断方法
if(!method_exists($new,$action)){
exit("方法< ".$action ." >不存在");
}
return ['new'=> $new,'action'=>$action];
}
}
/Sentiment/App.php
<?php
define('APP_PATH',dirname(__DIR__));
require_once APP_PATH.'/Sentiment/Sentiment.php';
$init = Sentiment::init();
$new =$init['new'];
$action=$init['action'];
$new->$action();
/public/index.php
<?php
define('APP_PATH',dirname(__DIR__));
require_once APP_PATH.'/Sentiment/App.php';
?>
此时就可以通过index.php,访问到核心文件的入口了,访问不存在的应用模块、控制器、方法时会返回提示信息,如下图访问方法ccc时,则会输出提示信息
访问视图层、页面跳转、函数助手
访问视图
在/app/index/view/
下建一个home文件,再在其中建一个index.php
<?php
echo "this is view";
?>
此时只需要修改Home.php的index方法,便可直接访问到视图层
<?php
class Home
{
public function index(){
require_once APP_PATH.'/app/index/view/home/index.php';
}
}
这里可以做一下优化,把require_once的内容写到核心文件Sentiment中
/Sentiment/Sentiment.php
<?php
class Sentiment
{
public static function init()
{
$script_name = $_SERVER['SCRIPT_NAME']; // /public/index.php
$request_url = $_SERVER['REQUEST_URI']; // /public/index.php/index/home/index?id=123&titile=456
$query_string = $_SERVER['QUERY_STRING']; // id=123&titile=456
// 需要 index/home/index
$url = str_replace($script_name,'',$request_url); // /index/home/index?id=123&titile=456
$url = str_replace($query_string,'',$url); // /index/home/index?
$url = ltrim($url,'/'); // index/home/index?
$url = rtrim($url,'?'); // index/home/index
$url = explode('/',$url);
if(count($url) >= 3){
$model = $url[0];
$controller = $url[1];
$action = $url[2];
} else {
$config=require_once APP_PATH.'/config/app.php';
$model = $config['default_model'];
$controller = $config['default_controller'];
$action = $config['default_action'];
}
define('MODULE',$model); //view的module路径
//判断应用模块
if(!is_dir(APP_PATH.'/app/'.$model)){
exit("应用模块<".$model.">不存在!");
}
$path = APP_PATH.'/app/'.$model.'/controller/'.$controller.'.php';
//判断控制器
if(!file_exists($path)){
exit("控制器<".$controller.">不存在!");
}
require_once $path;
$new = new $controller;
//判断方法
if(!method_exists($new,$action)){
exit("方法< ".$action ." >不存在");
}
return ['new'=> $new,'action'=>$action];
}
public static function view($path,$data){
$path= APP_PATH.'/app/'.MODULE.'/view/'.$path.'.php';
require_once $path;
}
}
较之前多出的部分
define('MODULE',$model); //view的module路径
public static function view($path,$data){
$path= APP_PATH.'/app/'.MODULE.'/view/'.$path.'.php';
require_once $path;
}
/app/index/controller/Home.php
直接调用Sentiment的view方法,并传参path,和data
<?php
class Home
{
public function index(){
Sentiment::view('home/index','456');
}
}
/app/index/view/home/index.php
两种调用$data值得方法选一个即可
<?= $data ?>
<? echo $data;?>
此时在访问就是我们传入的456值
页面跳转
先创建个跳转文件/Sentiment/Load.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>Document</title>
</head>
<body>
<h1><?=$msg?></h1>
<h3>页面自动跳转,等待 <b id="msg">3</b>秒,<a href="javascript:li()">跳转</a></h3>
<script>
var msg = document.getElementById("msg");
var i = 3;
setInterval(function(){
i--;
msg.innerHTML = i;
if(i<=0){
window.location.href= "../<?= $path?>";
}
},1000);
function li(){
window.location.href = "../<?= $path ?>";
}
</script>
</body>
</html>
/Sentiment/Sentiment.php,创建load方法
public static function load($path,$msg){
$url=APP_PATH.'/Sentiment/Load.php';
require_once $url;
}
/app/index/controller/Home.php,通过abc方法跳转到index方法调用view
<?php
class Home
{
public function index(){
Sentiment::view('home/index','456');
}
public function abc(){
Sentiment::load('home/index',"Sentiment");
}
}
结果
三秒后自动跳转index
函数助手
Sentiment/Common.php,将之前写的view、load函数写到其中
<?php
function dump($data){
echo "<pre>";
print_r($data);
echo "</pre>";
}
function view($path,$data){
Sentiment::view($path,$data);
}
function load($path,$msg){
Sentiment::load($path,$msg);
}
在/Sentiment/App.php,进行文件包含
require_once APP_PATH.'/Sentiment/Common.php';
这时/app/index/controller/Home.php
,就不需要通过Sentiment::view()这种方式调用了,而是直接view();即可
除此外dump方法便于输出数组,当index方法中,是数组时调用dump
<?php
class Home
{
public function index(){
$data=['Sentiment'=>1,'Tana'=>2];
dump($data);
}
public function abc(){
load('home/index',"Sentiment");
}
}
结果
其实整个运行的流程就是:/public/index.php
—>/Sentiment/App.php
(在Sentiment.php获取控制器和方法值)—>/app/index/controller/Home.php(由于App.php包含了Common.php,所以就不用通过::调用方法)