# 视图

视图本质上是网页或页面片段（如页眉、页脚、侧边栏等）。实际上，视图支持灵活嵌套；如有层级需求，可将视图嵌入其他视图，甚至实现多层嵌套。

视图无法直接调用，必须通过控制器或 视图路由 加载。

在 MVC 架构中，控制器承担着“交通指挥”的角色，负责调取特定视图。如尚未阅读 控制器 章节，建议在继续前先行阅读。

下面以此前创建的示例控制器为例，演示如何添加视图。

## 创建视图

使用文本编辑器创建名为 blog_view.php 的文件，内容如下:

    <html>
        <head>
            <title>My Blog</title>
        </head>
        <body>
            <h1>Welcome to my Blog!</h1>
        </body>
    </html>

然后将文件保存到 app/Views 目录。

## 显示视图

要加载并显示特定视图文件，在控制器中使用 `view()` 函数，代码如下：

    return view('name');

其中 name 为视图文件名。

重要

省略文件扩展名时，视图文件默认以 .php 为扩展名。

备注

`view()` 函数内部使用 视图渲染器。

在 app/Controllers 目录中创建名为 Blog.php 的文件，内容如下：

    <?php

    namespace App\Controllers;

    class Blog extends BaseController
    {
        public function index()
        {
            return view('blog_view');
        }
    }

打开位于 app/Config/Routes.php 的路由文件，找到"Route Definitions"部分， 添加以下代码：

    use App\Controllers\Blog;

    $routes->get('blog', [Blog::class, 'index']);

访问站点即可看到新视图。URL 大致如下:

    example.com/index.php/blog/

## 加载多个视图

CodeIgniter 会智能处理控制器中对 `view()` 的多次调用。如果发生多次调用，结果会拼接在一起。

例如，可能需要一个页眉视图、一个菜单视图、一个内容视图和一个页脚视图。大致如下：

    <?php

    namespace App\Controllers;

    use CodeIgniter\Controller;

    class Page extends Controller
    {
        public function index()
        {
            $data = [
                'page_title' => 'Your title',
            ];

            return view('header')
                . view('menu')
                . view('content', $data)
                . view('footer');
        }
    }

上例中使用了"动态添加的数据"，见下文说明。

## 将视图存储在子目录中

如果希望更有条理，视图文件也可以存储在子目录中。 此时加载视图时需要在文件名中包含目录路径。示例：

    return view('directory_name/file_name');

## 命名空间视图

可以将视图存储在命名空间下的 Views 目录中，然后像使用命名空间一样加载该视图。 虽然 PHP 不支持从命名空间加载非类文件，CodeIgniter 提供了此功能， 使视图可以像模块一样打包，便于复用或分发。

如果 example/blog 目录在 自动加载器 中配置了 PSR-4 映射， 命名空间为 `Example\Blog`，则可以像使用命名空间一样加载视图文件。

参考此示例，通过在视图名称前添加命名空间前缀，即可从 example/blog/Views 加载 blog_view.php 文件（无需像 `Example\Blog\Views\` 这样指定末尾的 `Views` 目录）：

    <?php

    return view('Example\Blog\blog_view');

备注

引用带命名空间的视图时必须使用反斜杠（`\`），因为 CodeIgniter 借此区分命名空间视图与常规文件路径。正斜杠（`/`）仅用于标准 app/Views 文件夹内的子目录。

## 重写命名空间视图

Added in version 4.7.0.

通过在应用的 app/Views 目录下创建匹配的目录结构，可重写命名空间视图。由此即可在不修改模块或扩展包核心源码的情况下自定义其输出。

### 配置

默认情况下，系统会在 app/Views/overrides 目录中查找重写视图。可通过 app/Config/View.php 中的 `$appOverridesFolder` 属性配置此位置：

    public string $appOverridesFolder = 'overrides';

若希望将命名空间直接映射到 app/Views 根目录（不使用子目录），可将此值设为空字符串（`''`）。

### 示例

假设有一个名为 Blog 的模块，其命名空间为 `Example\Blog`。原始视图文件位于：

    /modules
    └── Example
        └── Blog
            └── Views
                └── blog_view.php

要重写此视图（使用默认配置），请在 app/Views/overrides 内的匹配路径下创建一个文件：

    /app
    └── Views
        └── overrides                 <-- 配置的 $appOverridesFolder
            └── Example               <-- 匹配命名空间的第一部分
                └── Blog              <-- 匹配命名空间的第二部分
                    └── blog_view.php <-- 自定义视图

现在，调用 `view('Example\Blog\blog_view')` 时，CodeIgniter 将自动从 app/Views/overrides/Example/Blog/blog_view.php 加载自定义视图，而非原始模块视图文件。

备注

重写路径必须与引用视图的方式完全一致。若以 `view('Example\Blog\blog_view')` 形式加载视图，则重写路径为 app/Views/overrides/Example/Blog/blog_view.php。然而，若在调用时明确包含了 `Views` 目录（例如 `view('Example\Blog\Views\blog_view')`），则重写路径也必须包含该目录：app/Views/overrides/Example/Blog/Views/blog_view.php。

## 缓存视图

使用 `view()` 函数可以缓存视图，在第三个参数中传入 `cache` 选项， 指定缓存秒数：

    // Cache the view for 60 seconds
    return view('file_name', $data, ['cache' => 60]);

默认情况下，视图以视图文件名作为缓存名。可通过传入 `cache_name` 和期望的缓存 ID 来自定义：

    // Cache the view for 60 seconds
    return view('file_name', $data, ['cache' => 60, 'cache_name' => 'my_cached_view']);

## 向视图添加动态数据

数据通过 `view()` 函数的第二个参数（数组形式）从控制器传递给视图。

示例如下：

    $data = [
        'title'   => 'My title',
        'heading' => 'My Heading',
        'message' => 'My Message',
    ];

    return view('blog_view', $data);

在控制器文件中试试。打开控制器并添加以下代码：

    <?php

    namespace App\Controllers;

    class Blog extends BaseController
    {
        public function index()
        {
            $data['title']   = 'My Real Title';
            $data['heading'] = 'My Real Heading';

            return view('blog_view', $data);
        }
    }

然后打开视图文件，将文本替换为与数据数组键对应的变量:

    <html>
        <head>
            <title><?= esc($title) ?></title>
        </head>
        <body>
            <h1><?= esc($heading) ?></h1>
        </body>
    </html>

通过之前使用的 URL 加载页面，即可看到变量被替换为对应值。

### saveData 选项

传入的数据会在后续 `view()` 调用中保留。如果 在同一次请求中多次调用该函数，无需在每次 `view()` 时重复传入数据。

但这可能导致数据"渗透"到其他视图中，潜在引发问题。如果希望在每次调用后清理数据， 可在第三个参数的 `$option` 数组中传入 `saveData` 选项。

    $data = [
        'title'   => 'My title',
        'heading' => 'My Heading',
        'message' => 'My Message',
    ];

    return view('blog_view', $data, ['saveData' => false]);

此外，如果希望 `view()` 函数的默认行为就是每次调用后清除数据， 可以在 app/Config/Views.php 中将 `$saveData` 设置为 `false`。

## 创建循环

传递给视图文件的数组不限于简单变量，还可以传递多维数组， 通过循环生成多行数据。例如，从数据库获取的数据通常是多维数组格式。

简单示例如下。在控制器中添加以下代码：

    <?php

    namespace App\Controllers;

    class Blog extends BaseController
    {
        public function index()
        {
            $data = [
                'todo_list' => ['Clean House', 'Call Mom', 'Run Errands'],
                'title'     => 'My Real Title',
                'heading'   => 'My Real Heading',
            ];

            return view('blog_view', $data);
        }
    }

然后打开视图文件，创建循环：

    <html>
    <head>
        <title><?= esc($title) ?></title>
    </head>
    <body>
        <h1><?= esc($heading) ?></h1>

        <h2>My Todo List</h2>

        <ul>
        <?php foreach ($todo_list as $item): ?>

            <li><?= esc($item) ?></li>

        <?php endforeach ?>
        </ul>

    </body>
    </html>