视图单元
许多应用包含可在页面间或页面不同位置重复使用的视图片段,常见的如提示框、导航控件、广告或登录表单等。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') ?>
简单单元
简单单元是从所选方法返回字符串的类。以下是一个简单的 Alert Message 单元示例:
<?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 为后缀,视图文件则为类名的 snake_case 形式(去掉后缀)。例如,MyCell 类对应的视图文件应为 my.php。
备注
v4.3.5 之前,生成的视图文件以 _cell.php 结尾。v4.3.5 及更新版本生成的视图文件不再包含 _cell 后缀,但已有的 _cell 文件仍可被定位和加载。
创建受控单元
最基础的实现只需在类中定义 public 属性。这些属性将自动在视图文件中可用。
将上方的 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.') ?>
备注
如使用类型化属性,必须设置初始值:
<?php
// app/Cells/AlertMessageCell.php
namespace App\Cells;
use CodeIgniter\View\Cells\Cell;
class AlertMessageCell extends Cell
{
public string $type = '';
public string $message = '';
}
通过命令生成单元
还可以通过 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']);
}
}
计算属性
如需对一个或多个属性执行额外逻辑,可使用计算属性。这需要将属性设为 protected 或 private,并实现一个 public 方法,方法名由 get + 属性名 + Property 组成:
// 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>
重要
初始化单元时,无法设置声明为 private 的属性。
展示方法
有时需要在视图中执行额外逻辑,但不想作为参数传递。此时可实现一个方法,直接从单元视图内部调用。有助于提高视图的可读性:
<?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]) ?>
单元缓存
可通过向 view_cell() 传递第三个参数(缓存秒数)来缓存视图单元调用的结果。将使用当前配置的缓存引擎:
// 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') ?>