PHP MVC框架初探

前言

下面准备审一下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目录:应用目录(多模块)
    • 单模块:即一个主界面,其中包含ModelViewController
    • 多模块:有多个主界面,并都包含ModelViewController
  • 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,所以就不用通过::调用方法)

  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值