视图单元

许多应用程序都有小的视图片段,这些片段可以在页面之间重复,或者在页面的不同位置重复。这些通常是帮助框、导航控件、广告、登录表单等。CodeIgniter 允许您将这些表示块的逻辑封装在视图单元中。它们基本上是可以在其他视图中包含的迷你视图。它们可以包含用于处理任何单元格特定显示逻辑的逻辑。它们可以用于通过将每个单元格的逻辑分离到它自己的类中来使您的视图更易读和更易维护。

简单和受控单元格

CodeIgniter 支持两种类型的视图单元格:简单和受控。

简单视图单元格 可以从您选择的任何类和方法生成,并且不需要遵循任何规则,除了它必须返回一个字符串。

受控视图单元格 必须从扩展 Codeigniter\View\Cells\Cell 类的类生成,该类提供了额外的功能,使您的视图单元格更灵活,使用起来更快。

调用视图单元格

无论您使用哪种类型的视图单元格,都可以使用 view_cell() 辅助函数从任何视图中调用它。

第一个参数是 (1) 类和方法的名称(简单单元格)或 (2) 类和可选方法的名称(受控单元格)来调用,第二个参数是传递给方法的参数数组或字符串。

// In a View.

// Simple Cell
<?= view_cell('App\Cells\MyClass::myMethod', ['param1' => 'value1', 'param2' => 'value2']) ?>

// Controlled Cell
<?= view_cell('App\Cells\MyCell', ['param1' => 'value1', 'param2' => 'value2']) ?>

单元格返回的字符串将插入到调用 view_cell() 函数的视图中。

命名空间省略

版本 4.3.0 中的新功能。

如果您不包含类的完整命名空间,它将假设它可以在 App\Cells 命名空间中找到。因此,以下示例将尝试在 app/Cells/MyClass.php 中找到 MyClass 类。如果在那里找不到,将扫描所有命名空间直到找到它,在每个命名空间的 Cells 子目录中搜索。

// In a View.
<?= view_cell('MyClass::myMethod', ['param1' => 'value1', 'param2' => 'value2']) ?>

将参数作为键值对字符串传递

您也可以将参数作为键值对字符串传递。

// In a View.
<?= view_cell('MyClass::myMethod', 'param1=value1, param2=value2') ?>

简单单元格

简单单元格是返回所选方法的字符串的类。一个简单的警报消息单元格示例可能如下所示

<?php

namespace App\Cells;

class AlertMessage
{
    public function show(array $params): string
    {
        return "<div class=\"alert alert-{$params['type']}\">{$params['message']}</div>";
    }
}

您可以在视图中调用它,例如

// In a View.
<?= view_cell('AlertMessage::show', ['type' => 'success', 'message' => 'The user has been updated.']) ?>

此外,您可以使用与方法中的参数变量匹配的参数名称,以提高可读性。当您以这种方式使用它时,所有参数都必须始终在视图单元格调用中指定。

// In a View.
<?= view_cell('Blog::recentPosts', 'category=codeigniter, limit=5') ?>
<?php

// In a Cell.

namespace App\Cells;

class Blog
{
    // ...

    public function recentPosts(string $category, int $limit): string
    {
        $posts = $this->blogModel->where('category', $category)
            ->orderBy('published_on', 'desc')
            ->limit($limit)
            ->get();

        return view('recentPosts', ['posts' => $posts]);
    }
}

受控单元格

版本 4.3.0 中的新功能。

受控单元格有两个主要目标:(1)尽可能快地构建单元格,以及(2)如果需要,为您的视图提供额外的逻辑和灵活性。

该类必须扩展 CodeIgniter\View\Cells\Cell。它们应该在同一个文件夹中有一个视图文件。按照惯例,类名应该以 PascalCase 结尾,后缀为 Cell,视图应该是类名的蛇形命名版本,没有后缀。例如,如果您有一个 MyCell 类,视图文件应该是 my.php

注意

在 v4.3.5 之前,生成的视图文件以 _cell.php 结尾。虽然 v4.3.5 及更高版本将生成没有 _cell 后缀的视图文件,但现有视图文件仍将被定位和加载。

创建受控单元格

在最基本的层面上,您只需要在类中实现公共属性。这些属性将自动提供给视图文件。

将上面的 AlertMessage 实现为受控单元将如下所示

<?php

// app/Cells/AlertMessageCell.php

namespace App\Cells;

use CodeIgniter\View\Cells\Cell;

class AlertMessageCell extends Cell
{
    public $type;
    public $message;
}
// app/Cells/alert_message.php
<div class="alert alert-<?= esc($type, 'attr') ?>">
    <?= esc($message) ?>
</div>
// Called in main View:
<?= view_cell('AlertMessageCell', 'type=warning, message=Failed.') ?>

通过命令生成单元

您也可以通过 CLI 中的内置命令创建受控单元。该命令是 php spark make:cell。它接受一个参数,即要创建的单元的名称。该名称应为 PascalCase,该类将在 **app/Cells** 目录中创建。视图文件也将创建在 **app/Cells** 目录中。

php spark make:cell AlertMessageCell

使用不同的视图

您可以通过在类中设置 view 属性来指定自定义视图名称。该视图将像任何其他视图一样正常定位。

<?php

namespace App\Cells;

use CodeIgniter\View\Cells\Cell;

class AlertMessageCell extends Cell
{
    public $type;
    public $message;

    protected string $view = 'my/custom/view';
}

自定义渲染

如果您需要对 HTML 的渲染有更多控制,您可以实现一个 render() 方法。此方法允许您执行额外的逻辑并将额外的数据传递给视图(如果需要)。render() 方法必须返回一个字符串。

为了利用受控单元的全部功能,您应该使用 $this->view() 而不是普通的 view() 辅助函数

<?php

namespace App\Cells;

use CodeIgniter\View\Cells\Cell;

class AlertMessageCell extends Cell
{
    public $type;
    public $message;

    public function render(): string
    {
        return $this->view('my/custom/view', ['extra' => 'data']);
    }
}

计算属性

如果您需要对一个或多个属性执行额外的逻辑,可以使用计算属性。这需要将属性设置为 protectedprivate,并实现一个公共方法,其名称由 getProperty 围绕属性名称组成。

// In a View. Initialize the protected properties.
<?= view_cell('AlertMessageCell', ['type' => 'note', 'message' => 'test']) ?>
<?php

// app/Cells/AlertMessageCell.php

namespace App\Cells;

use CodeIgniter\View\Cells\Cell;

class AlertMessageCell extends Cell
{
    protected $type;
    protected $message;
    private $computed;

    public function mount(): void
    {
        $this->computed = sprintf('%s - %s', $this->type, $this->message);
    }

    public function getComputedProperty(): string
    {
        return $this->computed;
    }

    public function getTypeProperty(): string
    {
        return $this->type;
    }

    public function getMessageProperty(): string
    {
        return $this->message;
    }
}
// app/Cells/alert_message.php
<div>
    <p>type - <?= esc($type) ?></p>
    <p>message - <?= esc($message) ?></p>
    <p>computed: <?= esc($computed) ?></p>
</div>

重要

您不能在单元格初始化期间设置声明为私有的属性。

展示方法

有时您需要对视图执行额外的逻辑,但您不想将其作为参数传递。您可以实现一个方法,该方法将在单元格视图本身内部调用。这可以帮助提高视图的可读性。

<?php

// app/Cells/RecentPostsCell.php

namespace App\Cells;

use CodeIgniter\View\Cells\Cell;

class RecentPostsCell extends Cell
{
    protected $posts;

    public function linkPost($post): string
    {
        return anchor('posts/' . $post->id, $post->title);
    }
}
// app/Cells/recent_posts.php
<ul>
    <?php foreach ($posts as $post): ?>
        <li><?= $this->linkPost($post) ?></li>
    <?php endforeach ?>
</ul>

执行设置逻辑

如果您需要在渲染视图之前执行额外的逻辑,可以实现一个 mount() 方法。此方法将在类实例化后立即调用,可用于设置其他属性或执行其他逻辑。

<?php

namespace App\Cells;

use CodeIgniter\View\Cells\Cell;

class RecentPostsCell extends Cell
{
    protected $posts;

    public function mount(): void
    {
        $this->posts = model('PostModel')->orderBy('created_at', 'DESC')->findAll(10);
    }
}

您可以通过将参数作为数组传递给 view_cell() 辅助函数,将其他参数传递给 mount() 方法。发送的任何与 mount() 方法的参数名称匹配的参数都将被传递。

<?php

// app/Cells/RecentPostsCell.php

namespace App\Cells;

use CodeIgniter\View\Cells\Cell;

class RecentPostsCell extends Cell
{
    protected $posts;

    public function mount(?int $categoryId): void
    {
        $this->posts = model('PostModel')
            ->when(
                $categoryId,
                static fn ($query, $categoryId) => $query->where('category_id', $categoryId)
            )
            ->orderBy('created_at', 'DESC')
            ->findAll(10);
    }
}
// Called in main View:
<?= view_cell('RecentPostsCell', ['categoryId' => 5]) ?>

单元格缓存

您可以通过将要缓存数据的秒数作为第三个参数传递来缓存视图单元格调用的结果。这将使用当前配置的缓存引擎。

// Cache the view for 5 minutes
<?= view_cell('App\Cells\Blog::recentPosts', 'limit=5', 300) ?>

如果您愿意,可以提供一个自定义名称来代替自动生成的名称,方法是将新名称作为第四个参数传递。

// Cache the view for 5 minutes
<?= view_cell('App\Cells\Blog::recentPosts', 'limit=5', 300, 'newcacheid') ?>