目錄
1 什麼是MVC" >1 什麼是MVC
2 為什麼要自己開發MVC框架" >2 為什麼要自己開發MVC框架
3 準備工作" >3 準備工作
#3.1 環境準備" >#3.1 環境準備
" >3.4 重定向
4 PHP MVC核心文件" >4 PHP MVC核心文件
4.1 入口文件" >4.1 入口文件
4.2 配置文件" >4.2 配置文件
4.3 框架核心类" >4.3 框架核心类
4.4 Controller基类" >4.4 Controller基类
4.5 Model基类" >4.5 Model基类
4.6 View基类" >4.6 View基类
无法找到视图文件
5 应用" >5 应用
5.1 数据库部署" >5.1 数据库部署
5.2 部署模型" >5.2 部署模型
5.3 部署控制器" >5.3 部署控制器
5.4 部署视图" >5.4 部署视图
6 应用测试" >6 应用测试
首頁 後端開發 php教程 寫PHP MVC框架【保母等級教學推薦】

寫PHP MVC框架【保母等級教學推薦】

Jan 07, 2022 pm 01:58 PM

1 什麼是MVC

MVC模式(Model-View-Controller)是軟體工程中的一種軟體架構模式。 MVC把軟體系統分成三個基本部分:模型(Model)、視圖(View)和控制器(Controller)。 PHP中MVC模式也稱為Web MVC,從1970年代演化而來。 MVC的目的是實現一種動態的程式設計,便於後續對程式的修改和擴展簡化,並且使程式某一部分的重複利用成為可能。除此之外,此模式透過對複雜度的簡化,使程式結構更加直觀。 MVC各部分的功能:

  • 模型Model – 管理大部分的業務邏輯所有的資料庫邏輯。模型提供了連接和操作資料庫的抽象層。
  • 控制器Controller – 負責回應使用者要求準備資料,以及決定如何展示資料。
  • 視圖View – 負責渲染資料,透過HTML方式呈現給使用者。

寫PHP MVC框架【保母等級教學推薦】

一個典型的Web MVC流程:

#1、Controller截獲使用者發出的請求;

#2、Controller調用Model完成狀態的讀寫操作;

3、Controller把資料傳遞給View;

4、View渲染最終結果並呈現給使用者。

2 為什麼要自己開發MVC框架

網路上有大量優秀的MVC框架可供使用,本教學並不是為了開發一個全面的、終極的MVC框架解決方案。我們將它看作是一個很好的從內部學習PHP的機會。在這個過程中,你將學習物件導向程式設計MVC設計模式,並學習到開發中的一些注意事項。更重要的是,透過自製MVC框架,每個人都可以完全掌控自己的框架,將你的想法融入你的框架中。這不是很美妙的事情嗎~~~

3 準備工作

#3.1 環境準備

##這裡我們需要最基本的PHP環境:

    Nginx或Apache
  • PHP5.4
  • MySQL
建議使用phpStudy或docker一鍵安裝這樣的LNMP環境。

3.2 程式碼規格

在目錄設定好以後,我們接下來規定程式碼的規格:

1、MySQL的表名要

小寫小寫加上底線,如:itemcar_orders

2、模組名稱(Models)需用

大駝峰命名法,即首字母大寫,並在名稱後面加上Model,如:ItemModelCarModel

3、控制器(Controllers)需以

大駝峰命名法,即首字母大寫,並在名稱後面加上Controller,如:ItemControllerCarController

4、方法名稱(Action)需用

小駝峰命名法,即首字母小寫,如:index indexPost

5、檢視(Views)部署結構為

控制器名稱/行為名稱,如:item/view.phpcar/buy.php

上述規則是為了程式能更好地互相呼叫。接下來就開始真正的PHP MVC程式了。

3.3 目錄準備

在開始開發前,我們先給這個框架先取個名字吧,就叫:

Fastphp框架。然後再根據需要來把專案的目錄建立起來。假設我們建立的專案為project,目錄結構就這樣:

project                 WEB部署根目录
├─app                   应用目录
│  ├─controllers        控制器目录
│  ├─models             模块目录
│  ├─views              视图目录
├─config                配置文件目录
├─fastphp               框架核心目录
│ ├─base                MVC基类目录
│ ├─db                  数据库操作类目录
│ ├─Fastphp.php         内核文件  
├─static                静态文件目录
├─index.php             入口文件
登入後複製

然後依照下一步,把Nginx或Apache的網站根目錄設定到

project目錄, 。

3.4 重定向

重定向的目的有兩個:設定根目錄為

project所在位置,以及將所有請求都傳送給index.php 檔案。如果是Apache伺服器,在project 目錄下新建一個.htaccess 文件,內容為:

<IfModule mod_rewrite.c>
# 打开Rerite功能
RewriteEngine On

    # 如果请求的是真实存在的文件或目录,直接访问
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteCond %{REQUEST_FILENAME} !-d

    # 如果访问的文件或目录不是真事存在,分发请求至 index.php
    RewriteRule . index.php
</IfModule>
登入後複製

如果是Nginx伺服器,修改設定文件,在

server區塊中加入如下的重定向:

<span class="hljs-title">location</span> / {
<span class="hljs-comment">    # 重新向所有非真是存在的请求到index.php
</span><span class="hljs-title">    try_files</span> <span class="hljs-variable">$uri</span> <span class="hljs-variable">$uri</span>/ /index.php<span class="hljs-variable">$args</span>;
}
登入後複製

這樣做的主要原因是:

(1)静态文件能直接访问。如果文件或者目录真实存在,则直接访问存在的文件/目录。 比如,静态文件static/css/main.css真实存在,就可以直接访问它。 (2)程序有单一的入口。 这种情况是请求地址不是真实存在的文件或目录,这样请求就会传到 index.php 上。 例如,访问地址:localhost/item/detail/1,在文件系统中并不存在这样的文件或目录。 那么,Apache或Nginx服务器会把请求发给index.php,并且把域名之后的字符串赋值给REQUEST_URI变量。 这样在PHP中用$_SERVER[&#39;REQUEST_URI&#39;]就能拿到/item/detail/1; (3)可以用来生成美化的URL,利于SEO

4 PHP MVC核心文件

4.1 入口文件

接下来,在 project 目录下新建 index.php 入口文件,文件内容为:

<?php
// 应用目录为当前目录
define(&#39;APP_PATH&#39;, __DIR__ . &#39;/&#39;);

// 开启调试模式
define(&#39;APP_DEBUG&#39;, true);

// 加载框架文件
require(APP_PATH . &#39;fastphp/Fastphp.php&#39;);

// 加载配置文件
$config = require(APP_PATH . &#39;config/config.php&#39;);

// 实例化框架类
(new fastphp\Fastphp($config))->run();
登入後複製

注意,上面的PHP代码中,并没有添加PHP结束符号?>

这么做的主要原因是:

对于只有 PHP 代码的文件,最好没有结束标志?>

PHP自身并不需要结束符号,不加结束符让程序更加安全,很大程度防止了末尾被注入额外的内容。

4.2 配置文件

在入口文件中,我们加载了config.php文件的内容,那它有何作用呢? 从名称不难看出,它的作用是保存一些常用配置config.php 文件内容如下,作用是定义数据库连接参数参数,以及配置默认控制器名和操作名:

<?php

// 数据库配置
$config[&#39;db&#39;][&#39;host&#39;] = &#39;localhost&#39;;
$config[&#39;db&#39;][&#39;username&#39;] = &#39;root&#39;;
$config[&#39;db&#39;][&#39;password&#39;] = &#39;123456&#39;;
$config[&#39;db&#39;][&#39;dbname&#39;] = &#39;project&#39;;

// 默认控制器和操作名
$config[&#39;defaultController&#39;] = &#39;Item&#39;;
$config[&#39;defaultAction&#39;] = &#39;index&#39;;

return $config;
登入後複製

入口中的$config变量接收到配置参数后,再传给框架的核心类,也就是Fastphp类。

4.3 框架核心类

入口文件对框架类做了两步操作:实例化调用run()方法

实例化操作接受$config参数配置,并保存到对象属性中。

run()方法则调用用类自身方法,完成下面几个操作:

  • 类自动加载

  • 环境检查

  • 过滤敏感字符

  • 移除全局变量的老用法

  • 路由处理

fastphp目录下新建核心类文件,名称Fastphp.php,代码:

<?php
namespace fastphp;

// 框架根目录
defined(&#39;CORE_PATH&#39;) or define(&#39;CORE_PATH&#39;, __DIR__);

/**
 * fastphp框架核心
 */
class Fastphp
{
    // 配置内容
    protected $config = [];

    public function __construct($config)
    {
        $this->config = $config;
    }

    // 运行程序
    public function run()
    {
        spl_autoload_register(array($this, &#39;loadClass&#39;));
        $this->setReporting();
        $this->removeMagicQuotes();
        $this->unregisterGlobals();
        $this->setDbConfig();
        $this->route();
    }

    // 路由处理
    public function route()
    {
        $controllerName = $this->config[&#39;defaultController&#39;];
        $actionName = $this->config[&#39;defaultAction&#39;];
        $param = array();

        $url = $_SERVER[&#39;REQUEST_URI&#39;];
        // 清除?之后的内容
        $position = strpos($url, &#39;?&#39;);
        $url = $position === false ? $url : substr($url, 0, $position);
        // 删除前后的“/”
        $url = trim($url, &#39;/&#39;);

        if ($url) {
            // 使用“/”分割字符串,并保存在数组中
            $urlArray = explode(&#39;/&#39;, $url);
            // 删除空的数组元素
            $urlArray = array_filter($urlArray);

            // 获取控制器名
            $controllerName = ucfirst($urlArray[0]);

            // 获取动作名
            array_shift($urlArray);
            $actionName = $urlArray ? $urlArray[0] : $actionName;

            // 获取URL参数
            array_shift($urlArray);
            $param = $urlArray ? $urlArray : array();
        }

        // 判断控制器和操作是否存在
        $controller = &#39;app\\controllers\\&#39;. $controllerName . &#39;Controller&#39;;
        if (!class_exists($controller)) {
            exit($controller . &#39;控制器不存在&#39;);
        }
        if (!method_exists($controller, $actionName)) {
            exit($actionName . &#39;方法不存在&#39;);
        }

        // 如果控制器和操作名存在,则实例化控制器,因为控制器对象里面
        // 还会用到控制器名和操作名,所以实例化的时候把他们俩的名称也
        // 传进去。结合Controller基类一起看
        $dispatch = new $controller($controllerName, $actionName);

        // $dispatch保存控制器实例化后的对象,我们就可以调用它的方法,
        // 也可以像方法中传入参数,以下等同于:$dispatch->$actionName($param)
        call_user_func_array(array($dispatch, $actionName), $param);
    }

    // 检测开发环境
    public function setReporting()
    {
        if (APP_DEBUG === true) {
            error_reporting(E_ALL);
            ini_set(&#39;display_errors&#39;,&#39;On&#39;);
        } else {
            error_reporting(E_ALL);
            ini_set(&#39;display_errors&#39;,&#39;Off&#39;);
            ini_set(&#39;log_errors&#39;, &#39;On&#39;);
        }
    }

    // 删除敏感字符
    public function stripSlashesDeep($value)
    {
        $value = is_array($value) ? array_map(array($this, &#39;stripSlashesDeep&#39;), $value) : stripslashes($value);
        return $value;
    }

    // 检测敏感字符并删除
    public function removeMagicQuotes()
    {
        if (get_magic_quotes_gpc()) {
            $_GET = isset($_GET) ? $this->stripSlashesDeep($_GET ) : &#39;&#39;;
            $_POST = isset($_POST) ? $this->stripSlashesDeep($_POST ) : &#39;&#39;;
            $_COOKIE = isset($_COOKIE) ? $this->stripSlashesDeep($_COOKIE) : &#39;&#39;;
            $_SESSION = isset($_SESSION) ? $this->stripSlashesDeep($_SESSION) : &#39;&#39;;
        }
    }

    // 检测自定义全局变量并移除。因为 register_globals 已经弃用,如果
    // 已经弃用的 register_globals 指令被设置为 on,那么局部变量也将
    // 在脚本的全局作用域中可用。 例如, $_POST[&#39;foo&#39;] 也将以 $foo 的
    // 形式存在,这样写是不好的实现,会影响代码中的其他变量。 相关信息,
    // 参考: http://php.net/manual/zh/faq.using.php#faq.register-globals
    public function unregisterGlobals()
    {
        if (ini_get(&#39;register_globals&#39;)) {
            $array = array(&#39;_SESSION&#39;, &#39;_POST&#39;, &#39;_GET&#39;, &#39;_COOKIE&#39;, &#39;_REQUEST&#39;, &#39;_SERVER&#39;, &#39;_ENV&#39;, &#39;_FILES&#39;);
            foreach ($array as $value) {
                foreach ($GLOBALS[$value] as $key => $var) {
                    if ($var === $GLOBALS[$key]) {
                        unset($GLOBALS[$key]);
                    }
                }
            }
        }
    }

    // 配置数据库信息
    public function setDbConfig()
    {
        if ($this->config[&#39;db&#39;]) {
            define(&#39;DB_HOST&#39;, $this->config[&#39;db&#39;][&#39;host&#39;]);
            define(&#39;DB_NAME&#39;, $this->config[&#39;db&#39;][&#39;dbname&#39;]);
            define(&#39;DB_USER&#39;, $this->config[&#39;db&#39;][&#39;username&#39;]);
            define(&#39;DB_PASS&#39;, $this->config[&#39;db&#39;][&#39;password&#39;]);
        }
    }

    // 自动加载类
    public function loadClass($className)
    {
        $classMap = $this->classMap();

        if (isset($classMap[$className])) {
            // 包含内核文件
            $file = $classMap[$className];
        } elseif (strpos($className, &#39;\\&#39;) !== false) {
            // 包含应用(application目录)文件
            $file = APP_PATH . str_replace(&#39;\\&#39;, &#39;/&#39;, $className) . &#39;.php&#39;;
            if (!is_file($file)) {
                return;
            }
        } else {
            return;
        }

        include $file;

        // 这里可以加入判断,如果名为$className的类、接口或者性状不存在,则在调试模式下抛出错误
    }

    // 内核文件命名空间映射关系
    protected function classMap()
    {
        return [
            &#39;fastphp\base\Controller&#39; => CORE_PATH . &#39;/base/Controller.php&#39;,
            &#39;fastphp\base\Model&#39; => CORE_PATH . &#39;/base/Model.php&#39;,
            &#39;fastphp\base\View&#39; => CORE_PATH . &#39;/base/View.php&#39;,
            &#39;fastphp\db\Db&#39; => CORE_PATH . &#39;/db/Db.php&#39;,
            &#39;fastphp\db\Sql&#39; => CORE_PATH . &#39;/db/Sql.php&#39;,
        ];
    }
}
登入後複製

下面重点讲解主请求方法 route(),它也称路由方法。 路由方法的主要作用是:截取URL,并解析出控制器名、方法名和URL参数。 假设我们的 URL 是这样:

yoursite.com/controllerName/actionName/queryString
登入後複製

当浏览器访问上面的URL,route()从全局变量 $_SERVER[&#39;REQUEST_URI&#39;]中获取到字符串/controllerName/actionName/queryString。 然后,会将这个字符串分割成三部分:controllerNameactionNamequeryString。 例如,URL链接为:yoursite.com/item/detail/1/hello,那么route()分割之后,

  • ControllerName名就是:item
  • actionName名就是:detail
  • URL参数就是:array(1, hello)

分割完成后,路由方法再实例化控制器:itemController,并调用其中的detail方法 。

4.4 Controller基类

接下来,就是在 fastphp 中创建MVC基类,包括控制器模型视图三个基类。 在fastphp/base/目录下新建控制器基类,文件名 Controller.php,功能就是总调度,内容如下:

<?php
namespace fastphp\base;

/**
 * 控制器基类
 */
class Controller
{
    protected $_controller;
    protected $_action;
    protected $_view;

    // 构造函数,初始化属性,并实例化对应模型
    public function __construct($controller, $action)
    {
        $this->_controller = $controller;
        $this->_action = $action;
        $this->_view = new View($controller, $action);
    }

    // 分配变量
    public function assign($name, $value)
    {
        $this->_view->assign($name, $value);
    }

    // 渲染视图
    public function render()
    {
        $this->_view->render();
    }
}
登入後複製

Controller 类用assign()方法实现把变量保存到View对象中。 这样,在调用$this->render() 后视图文件就能显示这些变量。

4.5 Model基类

新建模型基类,继承自数据库操作类Sql类。 因为数据库操作比较复杂,所以SQL操作我们单独创建一个类。 Model基类涉及到3个类:Model基类本身,它的父类SQL,以及提供数据库连接句柄的Db类。 在fastphp/base/目录下新建模型基类文件,名为 Model.php,代码如下:

<?php
namespace fastphp\base;

use fastphp\db\Sql;

class Model extends Sql
{
    protected $model;

    public function __construct()
    {
        // 获取数据库表名
        if (!$this->table) {

            // 获取模型类名称
            $this->model = get_class($this);

            // 删除类名最后的 Model 字符
            $this->model = substr($this->model, 0, -5);

            // 数据库表名与类名一致
            $this->table = strtolower($this->model);
        }
    }
}
登入後複製

fastphp/db/目录下建立一个数据库基类 Sql.php,代码如下:

<?php
namespace fastphp\db;

use \PDOStatement;

class Sql
{
    // 数据库表名
    protected $table;

    // 数据库主键
    protected $primary = &#39;id&#39;;

    // WHERE和ORDER拼装后的条件
    private $filter = &#39;&#39;;

    // Pdo bindParam()绑定的参数集合
    private $param = array();

    /**
     * 查询条件拼接,使用方式:
     *
     * $this->where([&#39;id = 1&#39;,&#39;and title=Web&#39;, ...])->fetch();
     * 为防止注入,建议通过$param方式传入参数:
     * $this->where([&#39;id = :id&#39;], [&#39;:id&#39; => $id])->fetch();
     *
     * @param array $where 条件
     * @return $this 当前对象
     */
    public function where($where = array(), $param = array())
    {
        if ($where) {
            $this->filter .= &#39; WHERE &#39;;
            $this->filter .= implode(&#39; &#39;, $where);

            $this->param = $param;
        }

        return $this;
    }

    /**
     * 拼装排序条件,使用方式:
     *
     * $this->order([&#39;id DESC&#39;, &#39;title ASC&#39;, ...])->fetch();
     *
     * @param array $order 排序条件
     * @return $this
     */
    public function order($order = array())
    {
        if($order) {
            $this->filter .= &#39; ORDER BY &#39;;
            $this->filter .= implode(&#39;,&#39;, $order);
        }

        return $this;
    }

    // 查询所有
    public function fetchAll()
    {
        $sql = sprintf(select * from `%s` %s, $this->table, $this->filter);
        $sth = Db::pdo()->prepare($sql);
        $sth = $this->formatParam($sth, $this->param);
        $sth->execute();

        return $sth->fetchAll();
    }

    // 查询一条
    public function fetch()
    {
        $sql = sprintf(select * from `%s` %s, $this->table, $this->filter);
        $sth = Db::pdo()->prepare($sql);
        $sth = $this->formatParam($sth, $this->param);
        $sth->execute();

        return $sth->fetch();
    }

    // 根据条件 (id) 删除
    public function delete($id)
    {
        $sql = sprintf(delete from `%s` where `%s` = :%s, $this->table, $this->primary, $this->primary);
        $sth = Db::pdo()->prepare($sql);
        $sth = $this->formatParam($sth, [$this->primary => $id]);
        $sth->execute();

        return $sth->rowCount();
    }

    // 新增数据
    public function add($data)
    {
        $sql = sprintf(insert into `%s` %s, $this->table, $this->formatInsert($data));
        $sth = Db::pdo()->prepare($sql);
        $sth = $this->formatParam($sth, $data);
        $sth = $this->formatParam($sth, $this->param);
        $sth->execute();

        return $sth->rowCount();
    }

    // 修改数据
    public function update($data)
    {
        $sql = sprintf(update `%s` set %s %s, $this->table, $this->formatUpdate($data), $this->filter);
        $sth = Db::pdo()->prepare($sql);
        $sth = $this->formatParam($sth, $data);
        $sth = $this->formatParam($sth, $this->param);
        $sth->execute();

        return $sth->rowCount();
    }

    /**
     * 占位符绑定具体的变量值
     * @param PDOStatement $sth 要绑定的PDOStatement对象
     * @param array $params 参数,有三种类型:
     * 1)如果SQL语句用问号?占位符,那么$params应该为
     *    [$a, $b, $c]
     * 2)如果SQL语句用冒号:占位符,那么$params应该为
     *    [&#39;a&#39; => $a, &#39;b&#39; => $b, &#39;c&#39; => $c]
     *    或者
     *    [&#39;:a&#39; => $a, &#39;:b&#39; => $b, &#39;:c&#39; => $c]
     *
     * @return PDOStatement
     */
    public function formatParam(PDOStatement $sth, $params = array())
    {
        foreach ($params as $param => &$value) {
            $param = is_int($param) ? $param + 1 : &#39;:&#39; . ltrim($param, &#39;:&#39;);
            $sth->bindParam($param, $value);
        }

        return $sth;
    }

    // 将数组转换成插入格式的sql语句
    private function formatInsert($data)
    {
        $fields = array();
        $names = array();
        foreach ($data as $key => $value) {
            $fields[] = sprintf(`%s`, $key);
            $names[] = sprintf(:%s, $key);
        }

        $field = implode(&#39;,&#39;, $fields);
        $name = implode(&#39;,&#39;, $names);

        return sprintf((%s) values (%s), $field, $name);
    }

    // 将数组转换成更新格式的sql语句
    private function formatUpdate($data)
    {
        $fields = array();
        foreach ($data as $key => $value) {
            $fields[] = sprintf(`%s` = :%s, $key, $key);
        }

        return implode(&#39;,&#39;, $fields);
    }
}
登入後複製

应该说,Sql基类是框架的核心部分。为什么? 因为通过它,我们创建了一个 SQL 抽象层,可以大大减少了数据库的编程工作。 虽然 PDO 接口本来已经很简洁,但是抽象之后框架的可灵活性更高。 Sql类里面有用到Db:pdo()方法,这是我们创建的Db类,它提供一个PDO单例。 在fastphp/db/目录下创建Db.php文件,内容:

<?php
namespace fastphp\db;

use PDO;
use PDOException;

/**
 * 数据库操作类。
 * 其$pdo属性为静态属性,所以在页面执行周期内,
 * 只要一次赋值,以后的获取还是首次赋值的内容。
 * 这里就是PDO对象,这样可以确保运行期间只有一个
 * 数据库连接对象,这是一种简单的单例模式
 * Class Db
 */
class Db
{
    private static $pdo = null;

    public static function pdo()
    {
        if (self::$pdo !== null) {
            return self::$pdo;
        }

        try {
            $dsn    = sprintf(&#39;mysql:host=%s;dbname=%s;charset=utf8&#39;, DB_HOST, DB_NAME);
            $option = array(PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC);

            return self::$pdo = new PDO($dsn, DB_USER, DB_PASS, $option);
        } catch (PDOException $e) {
            exit($e->getMessage());
        }
    }
}
登入後複製

4.6 View基类

fastphp/base/目录下新建视图基类 View.php 内容如下:

<?php
namespace fastphp\base;

/**
 * 视图基类
 */
class View
{
    protected $variables = array();
    protected $_controller;
    protected $_action;

    function __construct($controller, $action)
    {
        $this->_controller = strtolower($controller);
        $this->_action = strtolower($action);
    }

    // 分配变量
    public function assign($name, $value)
    {
        $this->variables[$name] = $value;
    }

    // 渲染显示
    public function render()
    {
        extract($this->variables);
        $defaultHeader = APP_PATH . &#39;app/views/header.php&#39;;
        $defaultFooter = APP_PATH . &#39;app/views/footer.php&#39;;

        $controllerHeader = APP_PATH . &#39;app/views/&#39; . $this->_controller . &#39;/header.php&#39;;
        $controllerFooter = APP_PATH . &#39;app/views/&#39; . $this->_controller . &#39;/footer.php&#39;;
        $controllerLayout = APP_PATH . &#39;app/views/&#39; . $this->_controller . &#39;/&#39; . $this->_action . &#39;.php&#39;;

        // 页头文件
        if (is_file($controllerHeader)) {
            include ($controllerHeader);
        } else {
            include ($defaultHeader);
        }

        //判断视图文件是否存在
        if (is_file($controllerLayout)) {
            include ($controllerLayout);
        } else {
            echo <h1 id="无法找到视图文件">无法找到视图文件</h1>;
        }

        // 页脚文件
        if (is_file($controllerFooter)) {
            include ($controllerFooter);
        } else {
            include ($defaultFooter);
        }
    }
}
登入後複製

这样,核心的PHP MVC框架核心就完成了。 下面我们编写应用来测试框架功能。

5 应用

5.1 数据库部署

在 SQL 中新建一个 project 数据库,增加一个item 表、并插入两条记录,命令如下:

CREATE DATABASE `project` DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;
USE `project`;

CREATE TABLE `item` (
    `id` int(11) NOT NULL auto_increment,
    `item_name` varchar(255) NOT NULL,
    PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

INSERT INTO `item` VALUES(1, &#39;Hello World.&#39;);
INSERT INTO `item` VALUES(2, &#39;Lets go!&#39;);
登入後複製

5.2 部署模型

然后,我们还需要在app/models/目录中创建一个 ItemModel.php 模型,内容如下:

<?php
namespace app\models;

use fastphp\base\Model;
use fastphp\db\Db;

/**
 * 用户Model
 */
class ItemModel extends Model
{
    /**
     * 自定义当前模型操作的数据库表名称,
     * 如果不指定,默认为类名称的小写字符串,
     * 这里就是 item 表
     * @var string
     */
    protected $table = &#39;item&#39;;

    /**
     * 搜索功能,因为Sql父类里面没有现成的like搜索,
     * 所以需要自己写SQL语句,对数据库的操作应该都放
     * 在Model里面,然后提供给Controller直接调用
     * @param $title string 查询的关键词
     * @return array 返回的数据
     */
    public function search($keyword)
    {
        $sql = select * from `$this->table` where `item_name` like :keyword;
        $sth = Db::pdo()->prepare($sql);
        $sth = $this->formatParam($sth, [&#39;:keyword&#39; => %$keyword%]);
        $sth->execute();

        return $sth->fetchAll();
    }
}
登入後複製

因为 Item 模型继承了 Model基类,所以它拥有 Model 类的所有功能。

5.3 部署控制器

app/controllers/ 目录下创建一个 ItemController.php 控制器,内容如下:

<?php
namespace app\controllers;

use fastphp\base\Controller;
use app\models\ItemModel;

class ItemController extends Controller
{
    // 首页方法,测试框架自定义DB查询
    public function index()
    {
        $keyword = isset($_GET[&#39;keyword&#39;]) ? $_GET[&#39;keyword&#39;] : &#39;&#39;;

        if ($keyword) {
            $items = (new ItemModel())->search($keyword);
        } else {
            // 查询所有内容,并按倒序排列输出
            // where()方法可不传入参数,或者省略
            $items = (new ItemModel)->where()->order([&#39;id DESC&#39;])->fetchAll();
        }

        $this->assign(&#39;title&#39;, &#39;全部条目&#39;);
        $this->assign(&#39;keyword&#39;, $keyword);
        $this->assign(&#39;items&#39;, $items);
        $this->render();
    }

    // 查看单条记录详情
    public function detail($id)
    {
        // 通过?占位符传入$id参数
        $item = (new ItemModel())->where([id = ?], [$id])->fetch();

        $this->assign(&#39;title&#39;, &#39;条目详情&#39;);
        $this->assign(&#39;item&#39;, $item);
        $this->render();
    }

    // 添加记录,测试框架DB记录创建(Create)
    public function add()
    {
        $data[&#39;item_name&#39;] = $_POST[&#39;value&#39;];
        $count = (new ItemModel)->add($data);

        $this->assign(&#39;title&#39;, &#39;添加成功&#39;);
        $this->assign(&#39;count&#39;, $count);
        $this->render();
    }

    // 操作管理
    public function manage($id = 0)
    {
        $item = array();
        if ($id) {
            // 通过名称占位符传入参数
            $item = (new ItemModel())->where([id = :id], [&#39;:id&#39; => $id])->fetch();
        }

        $this->assign(&#39;title&#39;, &#39;管理条目&#39;);
        $this->assign(&#39;item&#39;, $item);
        $this->render();
    }

    // 更新记录,测试框架DB记录更新(Update)
    public function update()
    {
        $data = array(&#39;id&#39; => $_POST[&#39;id&#39;], &#39;item_name&#39; => $_POST[&#39;value&#39;]);
        $count = (new ItemModel)->where([&#39;id = :id&#39;], [&#39;:id&#39; => $data[&#39;id&#39;]])->update($data);

        $this->assign(&#39;title&#39;, &#39;修改成功&#39;);
        $this->assign(&#39;count&#39;, $count);
        $this->render();
    }

    // 删除记录,测试框架DB记录删除(Delete)
    public function delete($id = null)
    {
        $count = (new ItemModel)->delete($id);

        $this->assign(&#39;title&#39;, &#39;删除成功&#39;);
        $this->assign(&#39;count&#39;, $count);
        $this->render();
    }
}
登入後複製

5.4 部署视图

app/views/目录下新建 header.php 和 footer.php 两个页头页脚模板,如下。 header.php 内容:

<html>
<head>
    <meta http-equiv=Content-Type content=text/html; charset=utf-8 />
    <title><?php echo $title ?></title>
    <link rel=stylesheet href=/static/css/main.css type=text/css />
</head>
<body>
    <h1><?php echo $title ?></h1>
登入後複製

footer.php 内容:

</body>
</html>
登入後複製

页头文件用到了main.css样式文件,内容:

html, body {
margin: 0;
padding: 10px;
font-size: 20px;
}

input {
font-family:georgia,times;
font-size:24px;
line-height:1.2em;
}

a {
color:blue;
font-family:georgia,times;
line-height:1.2em;
text-decoration:none;
}

a:hover {
text-decoration:underline;
}

h1 {
color:#000000;
font-size:41px;
border-bottom:1px dotted #cccccc;
}

td {padding: 1px 30px 1px 0;}
登入後複製

然后,在 app/views/item 目录下创建以下几个视图文件。 index.php,浏览数据库内 item 表的所有记录,内容:

<form action= method=get>
    <input type=text value=<?php echo $keyword ?> name=keyword>
    <input type=submit value=搜索>
</form>

<p><a href=/item/manage>新建</a></p>

<table>
    <tr>
        <th>ID</th>
        <th>内容</th>
        <th>操作</th>
    </tr>
    <?php foreach ($items as $item): ?>
        <tr>
            <td><?php echo $item[&#39;id&#39;] ?></td>
            <td><?php echo $item[&#39;item_name&#39;] ?></td>
            <td>
                <a href=/item/manage/<?php echo $item[&#39;id&#39;] ?>>编辑</a>
                <a href=/item/delete/<?php echo $item[&#39;id&#39;] ?>>删除</a>
            </td>
        </tr>
    <?php endforeach ?>
</table>
登入後複製

add.php,添加记录后的提示,内容:

<a class=big href=/item/index>成功添加<?php echo $count ?>条记录,点击返回</a>
登入後複製

manage.php,管理记录,内容:

<form action=<?php echo $postUrl; ?> method=post>
    <?php if (isset($item[&#39;id&#39;])): ?>
        <input type=hidden name=id value=<?php echo $item[&#39;id&#39;] ?>>
    <?php endif; ?>
    <input type=text name=value value=<?php echo isset($item[&#39;item_name&#39;]) ? $item[&#39;item_name&#39;] : &#39;&#39; ?>>
    <input type=submit value=提交>
</form>

<a class=big href=/item/index>返回</a>
登入後複製

update.php,更改记录后的提示,内容:

<a class=big href=/item/index>成功修改<?php echo $count ?>项,点击返回</a>
登入後複製

delete.php,删除记录后的提示,内容:

<a href=/item/index>成功删除<?php echo $count ?>项,点击返回</a>
登入後複製

6 应用测试

这样,在浏览器中访问 project程序:http://localhost/item/index/,就可以看到效果了。
寫PHP MVC框架【保母等級教學推薦】

以上代码已经全部发布到 github 上,关键部分加了注释:

  • 仓库地址:https://github.com/yeszao/fastphp
  • 源码打包:https://github.com/yeszao/fastphp/archive/master.zip

以上是寫PHP MVC框架【保母等級教學推薦】的詳細內容。更多資訊請關注PHP中文網其他相關文章!

本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

AI Hentai Generator

AI Hentai Generator

免費產生 AI 無盡。

熱門文章

R.E.P.O.能量晶體解釋及其做什麼(黃色晶體)
3 週前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳圖形設置
3 週前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.如果您聽不到任何人,如何修復音頻
3 週前 By 尊渡假赌尊渡假赌尊渡假赌
WWE 2K25:如何解鎖Myrise中的所有內容
4 週前 By 尊渡假赌尊渡假赌尊渡假赌

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

php中的捲曲:如何在REST API中使用PHP捲曲擴展 php中的捲曲:如何在REST API中使用PHP捲曲擴展 Mar 14, 2025 am 11:42 AM

PHP客戶端URL(curl)擴展是開發人員的強大工具,可以與遠程服務器和REST API無縫交互。通過利用Libcurl(備受尊敬的多協議文件傳輸庫),PHP curl促進了有效的執行

在Codecanyon上的12個最佳PHP聊天腳本 在Codecanyon上的12個最佳PHP聊天腳本 Mar 13, 2025 pm 12:08 PM

您是否想為客戶最緊迫的問題提供實時的即時解決方案? 實時聊天使您可以與客戶進行實時對話,並立即解決他們的問題。它允許您為您的自定義提供更快的服務

解釋PHP中晚期靜態結合的概念。 解釋PHP中晚期靜態結合的概念。 Mar 21, 2025 pm 01:33 PM

文章討論了PHP 5.3中介紹的PHP中的晚期靜態結合(LSB),允許靜態方法的運行時間分辨率調用以更靈活的繼承。 LSB的實用應用和潛在的觸摸

在PHP API中說明JSON Web令牌(JWT)及其用例。 在PHP API中說明JSON Web令牌(JWT)及其用例。 Apr 05, 2025 am 12:04 AM

JWT是一種基於JSON的開放標準,用於在各方之間安全地傳輸信息,主要用於身份驗證和信息交換。 1.JWT由Header、Payload和Signature三部分組成。 2.JWT的工作原理包括生成JWT、驗證JWT和解析Payload三個步驟。 3.在PHP中使用JWT進行身份驗證時,可以生成和驗證JWT,並在高級用法中包含用戶角色和權限信息。 4.常見錯誤包括簽名驗證失敗、令牌過期和Payload過大,調試技巧包括使用調試工具和日誌記錄。 5.性能優化和最佳實踐包括使用合適的簽名算法、合理設置有效期、

框架安全功能:防止漏洞。 框架安全功能:防止漏洞。 Mar 28, 2025 pm 05:11 PM

文章討論了框架中的基本安全功能,以防止漏洞,包括輸入驗證,身份驗證和常規更新。

自定義/擴展框架:如何添加自定義功能。 自定義/擴展框架:如何添加自定義功能。 Mar 28, 2025 pm 05:12 PM

本文討論了將自定義功能添加到框架上,專注於理解體系結構,識別擴展點以及集成和調試的最佳實踐。

如何用PHP的cURL庫發送包含JSON數據的POST請求? 如何用PHP的cURL庫發送包含JSON數據的POST請求? Apr 01, 2025 pm 03:12 PM

使用PHP的cURL庫發送JSON數據在PHP開發中,經常需要與外部API進行交互,其中一種常見的方式是使用cURL庫發送POST�...

See all articles