icanboogie / view
为 ICanBoogie 提供视图支持
Requires
- php: >=7.2
- ext-json: *
- icanboogie/event: ^4.0
- icanboogie/render: ^0.7|^0.8
- icanboogie/routing: ^5.0|^4.0
Requires (Dev)
- phpunit/phpunit: ^8.5
Suggests
- icanboogie/bind-view: Binds the package to ICanBoogie
README
icanboogie/view 包提供了模型-视图-控制器 (MVC) 架构模式中的视图部分。它扩展了 icanboogie/routing 包的功能——更准确地说,是其控制器——与 icanboogie/render 包一起,帮助将表现层与逻辑层分离。
安装
composer require icanboogie/<name>
入门
在开始之前,您需要定义一些原型方法来将一些 render 组件绑定到 View 实例,并将 View 实例绑定到使用它们的 Controller 实例。
如果您使用 ICanBoogie 与 icanboogie/view 包,您可以简单地 require icanboogie/bind-view 包,并让它处理绑定。
以下代码演示了如何绑定 Controller 实例的 view
原型属性。绑定由 ControllerBindings 特性定义。
<?php use ICanBoogie\Prototype; use ICanBoogie\Routing\Controller; use ICanBoogie\View\View; use function ICanBoogie\Render\get_renderer; Prototype::bind([ Controller::class => [ 'lazy_get_view' => function(Controller $controller) { $view = new View($controller, get_renderer()); new View\AlterEvent($view); return $view; } ] ]);
视图和控制器
视图通过惰性获取器 view
与控制器相关联,因此只需 $this->view
就可以在控制器内部启动视图。然后视图等待 Controller::action
事件,以执行其渲染。
以下示例演示了如何将一些文章的查询设置为视图内容,并在视图变量中添加一个标题
<?php use ICanBoogie\Routing\Controller; use ICanBoogie\Module\ControllerBindings as ModuleBindings; use ICanBoogie\View\ControllerBindings as ViewBindings; class ArticlesController extends Controller { use Controller\ActionTrait, ViewBindings, ModuleBindings; protected function action_index() { $this->view->content = $this->model->visible->ordered->limit(10); $this->view['title'] = "Last ten articles"; } }
注意:
model
获取器由 icanboogie/module 包提供,并且仅在路由有module
属性时才可用,对于由模块定义的路由是自动的。
可以使用 assign()
方法在单个调用中将多个值分配给视图
<?php $content = new SignupForm; $title = "Sign up"; $params = $_POST; $this->view->assign(compact('content', 'title', 'params'));
在返回到控制器之前修改视图
当通过 view
获取器创建实例时,会触发类 View\AlterEvent 的 View::alter
事件。事件钩子可以使用此事件在将视图返回给控制器之前修改视图。
以下示例演示了如何修改在返回给控制器之前可以修改视图。如果路由有 module
属性,则将模块的 "template" 目录添加到模板解析器中
<?php use ICanBoogie\PropertyNotDefined; use ICanBoogie\View\View; $app->events->attach(function(View\AlterEvent $event, View $view) use ($app) { try { $module_id = $view->controller->route->module; } catch (PropertyNotDefined $e) { // if the property is not defined we just return return; } // adding a template path $view->template_resolver->add_path($app->modules[$module_id]->path . 'templates'); // adding a variable $view['log'] = $app->log->messages; // altering the layout if ($app->is_mobile) { $view->layout .= '.mobile'; } });
渲染视图
视图使用 模板 和 布局 进行渲染。模板渲染视图的内容,而布局则装饰模板。例如,使用 "articles/list" 模板来渲染文章集合,而使用 "page" 布局来用网站的布局装饰渲染后的集合。
用于呈现视图内容的模板按以下方式解析
- 从视图的
template
属性。 - 从路由的
template
属性。 - 从控制器的
template
属性。 - 从控制器的名称和动作来看,如果控制器具有
action
属性,例如 "articles/show"。
用于装饰模板的布局解决方式如下
- 来自视图的
layout
属性。 - 来自路由的
layout
属性。 - 来自控制器的
layout
属性。 - 如果路由的标识符以 "admin:" 开头,则使用 "admin"。
- 如果路由的模式为 "/" 且模板存在,则使用 "home"。
- 如果模板存在,则使用 "page"。
- 否则使用 "default"。
由于 template
和 layout
属性是延迟创建的,您可以定义它们而不是让 View 查找正确的模板名称。以下示例演示了如何 取消 模板并定义 "admin" 作为布局
<?php use ICanBoogie\Routing\Controller; use ICanBoogie\View\ControllerBindings as ViewBindings; class ArticlesController extends Controller { use Controller\ActionTrait; use ViewBindings; // … protected function action_index() { $this->view->content = $this->model->visible->ordered->limit(10); $this->view->template = null; $this->view->layout = "admin"; } // … }
模板和布局通常指定为 名称,例如 "page" 或 "articles/show",而不是路径,例如 "/path/to/my/template.phtml"。模板解析器和引擎集合用于将这些名称解析为路径名,并使用引擎集合使用适当的引擎渲染模板。这样做的原因是模板通常在您的应用程序中定义为层次结构,并且使用这个层次结构可以更好地满足您的应用程序。
例如,框架 ICanBoogie 装饰默认模板解析器以添加附加功能,并还将应用程序目录添加到模板解析器中。
请参阅 icanboogie/render 包以获取有关模板解析器和引擎集合的更多详细信息。
提供缓存的输出结果
在渲染视图之前,将触发类 View\BeforeRenderEvent 的 View::render:before
事件。事件钩子可以使用此事件提供缓存的输出结果并节省渲染成本。
以下示例演示了事件钩子如何提供先前渲染的视图的缓存输出结果。由于视图实例的 JSON 包含其模板、布局和变量,其哈希值是完美的缓存键。
<?php use ICanBoogie\View\View; /* @var $storage \ICanBoogie\Storage\Storage */ $app->events->attach(function(View\BeforeRenderEvent $event, View $view) use ($storage) { $hash = hash('sha256', json_encode($view)); $result = $storage->retrieve($hash); if ($result !== null) { $event->result = $result; return; } $event->result = $result = $view->render(); $storage->store($hash, $result); $event->stop(); });
渲染 JSON 和其他内容
视图通常用于渲染 HTML,但它们也可以渲染 JSON、XML 等其他内容,而且由于只需根据您要渲染的内容更改控制器的 Response 实例,所以这相当简单。这不是 View 功能,但这是您需要注意的事情。
以下示例演示了如何更改响应以适应 JSON 响应。
<?php // templates/json.php /* @var $content mixed */ echo json_encode($content);
<?php // … protected function action_any_json() { $this->view->content = $this->model->one; $this->view->template = 'json'; $this->response->content_type = "application/json"; } // …
取消视图
当您需要返回不同的结果或想要取消其渲染时,可以 取消 视图。当控制器返回结果时,视图会自动取消。还可以通过将控制器的 view
属性设置为 null
来取消视图。
以下示例演示了如何使用这些方法取消视图。
<?php use ICanBoogie\Routing\Controller; use ICanBoogie\View\ControllerBindings as ViewBindings; use ICanBoogie\Module\ControllerBindings as ModuleBindings; class ArticlesController extends Controller { use Controller\ActionTrait, ViewBindings, ModuleBindings; protected function action_any_index() { $this->view->content = $this->model->visible->ordered->limit(10); $this->view['title'] = "Last ten articles"; } protected function action_any_json() { $this->action_any_index(); $this->response->content_type = "application/json"; // The view is cancelled to return JSON text return json_encode($this->view->content); } protected function action_head_index() { $this->action_any_index(); // The view is cancelled although no result is returned $this->view = null; } }
渲染部分
partial()
方法使用视图的渲染器渲染部分。
请记住,视图包含在传递给模板的变量中。
<?php $view->partial('articles/overview', [ 'article' => $article ]);
原型方法
以下原型方法被使用。可以使用 ControllerBindings 特性来帮助提示代码。
ICanBoogie\Routing\Controller::lazy_get_view
:返回与控制器关联的 View 实例,并启动视图 魔法。
事件
-
ICanBoogie\View\View::alter
类的 View\AlterEvent:当实例由Controller::view
属性创建时触发。事件钩子可以使用此事件在返回给控制器之前修改视图。 -
ICanBoogie\View\View::render:before
类的 View\BeforeRenderEvent:在渲染视图之前触发。事件钩子可以使用此事件提供缓存结果。
持续集成
该项目由 GitHub actions 持续测试。
行为准则
本项目遵循 贡献者行为准则。参与本项目及其社区时,您应遵守此准则。
贡献
请参阅 CONTRIBUTING 获取详细信息。
许可证
icanboogie/view 采用 BSD-3-Clause 许可证发布。