65.9K
CodeProject 正在变化。 阅读更多。
Home

使用类似 .NET 控制器的 PHP MVC

starIconstarIconstarIconstarIconstarIcon

5.00/5 (14投票s)

2012年10月23日

CPOL

4分钟阅读

viewsIcon

52555

downloadIcon

1400

一个模仿 C# 控制器的 PHP MVC 模型。

Sample Image

引言

我主要使用 ASP.NET 工作,因此爱上了其 MVC 模型的简洁性。只需单击“新建控制器”,然后确认创建视图,您就会获得一段控制器代码,它再简单不过了。

但互联网主要由 PHP/MySQL 构建,如果您要为街角的面包店构建一个网站,包含 Zend 或其他大型 MVC 框架可能有点过头了。我想用 PHP 创建一个简单的 MVC 模型,其中控制器类可以像 .NET 变体一样简单。

在 .NET 中,您可以选择多个模板引擎。在我向您展示的模型中,您可以轻松插入任何您喜欢的模板引擎。我选择了 Smarty。 https://smarty.php.ac.cn/

我并没有声称这是独一无二的,有很多很棒的框架可以做同样的事情。 比如 CodeIgniter。 但我认为看看你如何自己动手做会很有趣。

代码

首先要做的是。 因为我们想要一个 MVC 模型,所以我们需要所有请求都通过一个单一的来源进行路由。 这是通过更改 * .htaccess * 文件完成的。

RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_URI} !(\.css|\.js|\.jpg|\.jpeg|\.png|\.gif|\.flv|\.swf)$
RewriteRule ^.*$ index.php

注意例外列表。 你不会想通过控制器路由 CSS。 虽然这可能是将你的 * .sass * 或 * .less * 文件转换为 * .css * 的地方。 对于缩小的 JavaScript 文件或优化的图像也是如此。

引导

我设置了一个 ini 文件。 其中包含一些有价值的设置。 * index.php * 要做的第一件事是解析这个文件。

[settings]
controller_path="controllers/"
view_path="views/"
baseURL="/subdir"

baseUrl 可以为空。 如果它安装在站点的根目录中

*index.php* 然后将包含 smarty 并做一些基本的事情。 然后做解析传入请求 URL 的最重要的部分。

如果 URL 是 home,那么 *homeController.php* 应该包含该控制器。 因此,如果 URL 是 *products/details/10*,则应加载 *productsController.php*。

<?php
    
require_once('libs/Smarty.class.php');    

$config = parse_ini_file("config.ini", TRUE); 

if (isset($_SERVER['REQUEST_URI']) && isset($_SERVER['REQUEST_METHOD'])) {
    //This will hold the request data. If there is any.
    $requestData = '';
    //The method
    $method = $_SERVER['REQUEST_METHOD'];

    if (isset($_SERVER['CONTENT_LENGTH']) && $_SERVER['CONTENT_LENGTH'] > 0) {
        //There is some data in the request. We would like to have this.
        $httpContent = fopen('php://input', 'r');
        while ($data = fread($httpContent, 1024)) {
            $requestData .= $data;
        }
        fclose($httpContent);
    }    
    
    //If we are installed in a subdirectory, we'd like to analyse the part after this directory
    $urlString = substr($_SERVER['REQUEST_URI'], strlen($config['settings']['baseURL']));
    //Split the url in pieces
    $urlParts = explode('/', $urlString); 

    if (isset($urlParts[0]) && $urlParts[0] == '') {
    	array_shift($urlParts);
    }
	
    if (!isset($urlParts[0]) || $urlParts[0] == '') {
        //If the url does not contain more then / we're going to start the default controller. Which is home
        $mainViewPath="home/";
        $currentController = "HomeController";
        $controllerFile = "homeController.php";	    		
    	}
    else	{
        //There is a directive after the / so that is going to be our controller
        $mainViewPath=strtolower($urlParts[0]);
        $currentController = ucfirst($urlParts[0]) . "Controller";
        $controllerFile = $urlParts[0] . "Controller.php";	    			
        //This will make the 'action' part of the url the first directive
        array_shift($urlParts);
    	}    
    
}
else    {
    header($_SERVER["SERVER_PROTOCOL"] . " 400 Bad request");      
}
?>

抽象类控制器

因此,我们现在知道我们想启动哪个控制器类。 如果有人给我们请求 URL/products,我们想要加载 *productsContoller.php* 并创建 ProductsController 的一个实例。

但首先我们需要专注于创建一个抽象类控制器。 为了让我们的控制器具有如此简单的结构,我们需要一个将完成所有繁重工作的超类。

所有控制器都将从此类继承。 控制器类需要做一些事情。 它需要查看请求 URL 剩下的部分,并确定应该启动控制器中的哪个操作。 因此,如果我们得到 /products,则没有定义操作,我们将启动 index 操作。 如果我们得到 /products/details,我们将启动 details 操作。

控制器类需要做的第二件事是创建 viewBag。 这是一个“数据包”,实现控制器可以将数据添加到其中。 但是除此之外,我也想假设 POST 数据是 JSON,并且所有 POST 数据在启动控制器操作之前都会添加到 viewBag。 这将允许从 JavaScript 设置模板变量。 (但这可能更适合下次)。

他做的第三件事是确定 *view.html* 文件。

控制器类做的最后一件事是实现 View() 函数,它将 viewBag 应用于模板。

构造函数

//@contructor
//@param controllerName, The name of the controller. E.g. ME
//@param urlParts The urlParts that are left.
//    If the original request was products,details,10 then this wil hold details,10
//@param data de JSON data, The request data.
public function __construct($controllerName, $urlParts, $data)
{
    $this->fullActionPath=implode("/", $urlParts);
    
    $this->method=$_SERVER['REQUEST_METHOD'];
    
    //We are assuming the data to be a JSON string. In a final version this should have some error handling
    if($data=='')   {
        $data='{}';
    }
    $this->viewBag=json_decode($data);
    
    //The action is the first part	
    $action=$urlParts[0];
    
    if (count($urlParts) > 1 && $urlParts[1] != '') {
        //Now we need to find the identifiers
        array_shift($urlParts);
        foreach ($urlParts as $uid) {
            if ($uid != '') {
                $this->uid[] = $uid;
            }
        }
    }			
    if(!isset($action) || $action=='')	{
        //If there is no action, we'll start the default action.
        $action="index";
    }
    
    //The view html
    $this->viewHtml=strtolower($action) . ".html";
    
    try	{
        //call_user_func gives a fatal error, which we cannot catch.
        //So we cannot handle asking for a unknown action
        //This is why we'll use the ReflectionClass's getMethod which will throw an exception
        $reflector = new ReflectionClass($this);
        $method=$reflector->getMethod($action);
        //If all works. We'll start the action.
        call_user_func($controllerName . "::" . $action, $this->uid);
    }
    catch(Exception $exc)	{
        //If the view doesn't exists, we'll start the default view.
        //In a final version this should start the ViewUnknown action.
        call_user_func($controllerName . "::index");
    }
}

View 函数

//@method View 
//@description Combines the view with the viewBag data. 
public function View()
{
    //Detertime the full path to the view file
    $viewPath=$GLOBALS['config']['settings']['view_path'] . $this->viewHtml;
    
    if	(file_exists($viewPath))	{
        //If the file exists use the smart engine
        //This would be the place to use some other engine like Mustache
        $this->smarty = new Smarty();
        
        foreach($this->viewBag as $key => $value) {
        	$this->smarty->assign($key, $value);
        }
        
        $this->smarty->display($viewPath);
    }
    else	{
        header('HTTP/1.0 404 Not Found');
        echo "404 Not Found";
        echo "The page that you have requested could not be found.";					
    }
}

我们需要将 require_once('classes/Controller.php') 添加到我们的 *index.php*。

主页视图和控制器

现在我们准备创建视图和控制器,但首先我会提醒您具有 controller_pathview_path 的 * .ini * 文件。 这决定了下面的文件结构

  • baseURL
      • Controller.php
      • Smarty.class.php
    • 控制器
      • homeController.php
    • 视图
      • 主页
        • index.html
    • .htaccess
    • config.ini
    • index.php

HomeController

<?php
/*
    @class HomeControlelr
    @extends Controller
    @description Dit is de controller for home
*/
class HomeController extends Controller
{

    //@method index De index action
    public function index()	
    {
        //Lets set some variable to test the template
        $this->viewBag->hellomessage="Hello world!";
        return $this->View();
    }
}
?>

home/index.html

<html>
<head>
    <title>Index</title>
</head>
<body>
    <h1>{$hellomessage}</h1>
</body>
</html>

基本上就是这样。

结论

虽然这需要更多的细节才能在真实的场景中使用,但我相信它表明你可以使用 PHP 创建一个简单的 MVC。 对于小型网站,这几乎就足够了。 在 zip 文件中,我已经将示例详细化了一些。 它包含一个面包店网上商店的详细示例。 它也适用于共享模板,以便我们可以将 html 包装在我们的视图周围。 不过,MVC 最重要的部分之一缺失了。 我将在下一篇文章中介绍这一点。

这是我的第一篇文章,我并不是一个核心 PHP 程序员,欢迎任何评论。

© . All rights reserved.