组件文件和目录位于插件目录下的 components 子目录中。每个组件都含有一个定义组件类的 PHP 文件和一个可选的组件局部文件目录。组件局部文件目录的名称与以小写形式书写的组件类名称匹配。组件目录结构示例:
├── plugins
| └── acme
| └── blog
| ├── components
| | ├── blogposts ← Partials Directory
| | | └── default.htm ← Default Markup (Optional)
| | └── BlogPosts.php ← Component Class
| └── Plugin.php
组件必须通过 registerComponents 方法在插件注册文件中注册。组件可以使用属性以及它们的检查器类型定义进行配置。
此 create:component 命令创建一个新的组件类和默认的组件视图。第一个参数指定作者和插件名称。第二个参数指定组件类名称。
php artisan create:component Acme.Blog BlogPosts组件类文件定义了组件功能和组件属性。组件类文件名应与组件类名匹配。组件类应扩展 Cms\Classes\ComponentBase 类。下一个示例中的组件应定义在 plugins/acme/blog/components/BlogPosts.php 文件中。
namespace Acme\Blog\Components;
class BlogPosts extends \Cms\Classes\ComponentBase
{
public function componentDetails()
{
return [
'name' => 'Blog Posts',
'description' => 'Displays a collection of blog posts.',
'icon' => 'icon-puzzle-piece'
];
}
/**
* posts becomes available on the page as {{ component.posts }}
*/
public function posts()
{
return ['First Post', 'Second Post', 'Third Post'];
}
}这个 componentDetails 方法是必需的. 该方法应返回一个包含两个键的数组: name 和 description. 此名称和描述显示在 CMS 后端用户界面中.
当此组件附加到页面或布局时,该类的属性和方法通过组件变量在页面上可用,其名称与组件短名称或别名匹配。例如,如果前一个示例中的 BlogPost 组件在页面上通过其短名称定义:
url = "/blog"
[blogPosts]
==你将能够通过 blogPosts 变量访问其 posts 方法。请注意,Twig 支持方法的属性表示法,因此你无需使用括号。
{% for post in blogPosts.posts %}
{{ post }}
{% endfor %}组件必须通过覆盖 registerComponents 方法在 [插件注册文件](../extend/extending.md) 内注册。这告知 CMS 关于组件的信息,并提供一个 **短名称** 供其使用。注册组件的示例:
public function registerComponents()
{
return [
\October\Demo\Components\Todo::class => 'demoTodo'
];
}这将注册 Todo 组件类并使用默认别名 demoTodo。有关组件使用的更多信息,请参阅 CMS 组件文章。
当您将组件添加到页面或布局时,您可以使用属性配置它。属性是通过组件类的 defineProperties 方法定义的。下一个示例展示了如何使用 string 检查器类型定义组件属性。
public function defineProperties()
{
return [
'maxItems' => [
'title' => 'Max items',
'description' => 'The most amount of todo items allowed',
'default' => 10,
'type' => 'string',
'validation' => [
'regex' => [
'message' => 'The Max Items property can contain only numeric symbols.',
'pattern' => '^[0-9]+
The method should return an array with the property keys as indexes and property parameters as values. The property keys are used for accessing the component property values inside the component class.
> The property parameters and available types are described in the [inspector types article](../element/inspector-types.md).
:::
Inside the component you can read the property value with the `property` method:
```php
$this->property('maxItems');如果属性值未定义,您可以将默认值作为 property 方法的第二个参数提供:
$this->property('maxItems', 6);你也可以将所有属性作为数组加载:
$properties = $this->getProperties();要从组件的 Twig 局部文件访问属性,请利用 __SELF__ 变量,它指向 Component 对象:
\{\{ __SELF__.property('maxItems') }}组件可以直接访问定义在页面 URL中的路由参数值。
// Returns the URL segment value, eg: /page/:post_id
$postId = $this->param('post_id');在某些情况下,组件属性可能作为硬编码值,或引用 URL 中的值。 此硬编码示例显示了标识符为 2 的博客文章正在被使用:
url = "/blog/hard-coded-page"
[blogPost]
id = "2"或者,该值可以从页面 URL 动态引用,方法是使用一个组件中的外部属性值。
url = "/blog/:my_custom_parameter"
[blogPost]
id = "\{\{ :my_custom_parameter }}"在这两种情况下,该值都可以通过使用 property 方法来获取:
$this->property('id');如果你需要访问路由参数名称:
// Returns "my_custom_parameter"
$this->paramName('id');组件可以通过重写组件类中的 onRun 方法,参与到页面执行周期事件中。CMS 控制器在页面或布局每次加载时都会执行此方法。在该方法内部,您可以通过 page 属性向 Twig 环境注入变量:
public function onRun()
\{
// This code will be executed when the page or layout is
// loaded and the component is attached to it.
$this->page['var'] = 'value'; // Inject some variable to the page
}当页面加载时,October 执行处理函数,这些函数可以在布局和页面 PHP 部分以及组件类中定义。处理函数的执行顺序如下。
onInit() 函数。onInit() 函数。onStart() 函数。onRun() 方法。onBeforePageStart() 函数。onStart() 函数。onRun() 方法。onEnd() 函数。onEnd() 函数。有时您可能希望在组件类首次实例化时执行代码。您可以覆盖组件类中的 init 方法来处理任何初始化逻辑,这将在 AJAX 处理程序和页面执行生命周期之前执行。例如,此方法可用于动态地将另一个组件附加到页面。
public function init()
\{
$this->addComponent(\Acme\Blog\Components\BlogPosts::class, 'blogPosts');
}与 布局执行生命周期 中的所有方法一样, 如果组件中的 onRun 方法返回一个值, 这将在此处停止该生命周期并将响应返回给浏览器. 这里我们使用 Response facade 返回一个访问被拒绝消息:
public function onRun()
\{
if (true) \{
return Response::make('Access denied!', 403);
}
}你还可以从 onRun 方法返回一个 404 响应:
public function onRun()
\{
if (true) \{
$this->setStatusCode(404);
return $this->controller->run('404');
}
}组件可以托管 AJAX 事件处理程序。
它们在组件类中的定义方式,与在页面或布局代码中定义它们的方式完全相同。
一个在组件类中定义的 AJAX 处理程序方法示例:
public function onAddItem()
\{
$value1 = post('value1');
$value2 = post('value2');
$this->page['result'] = $value1 + $value2;
}如果此组件的别名是 demoTodo 此处理程序可以通过 demoTodo::onAddItem 访问。请参阅 AJAX 处理程序文章 以了解如何将 AJAX 与组件结合使用。
所有组件都可以附带默认标记,在页面中使用 {% component %} 标签引入组件时会用到该标记,尽管这是可选的。默认标记保存在组件局部目录中,该目录的名称与组件类的小写形式相同。
默认组件标记应放置在名为 default.htm 的文件中。例如,Demo ToDo 组件的默认标记定义在文件 /plugins/october/demo/components/todo/default.htm 中。然后,可以使用 {% component %} 标签将其插入页面的任意位置:
url = "/todo"
[demoTodo]\{% component 'demoTodo' %}默认标记也可以接受参数,这些参数在渲染时会覆盖组件属性。
\{% component 'demoTodo' maxItems="7" %}这些属性在 onRun 方法中将不可用,因为它们是在页面生命周期完成后建立的。相反,可以通过覆盖组件类中的 onRender 方法来处理它们。CMS 控制器在渲染默认标记之前执行此方法。
public function onRender()
\{
// This code will be executed before the default component
// markup is rendered on the page or layout.
$this->page['var'] = 'Maximum items allowed: ' . $this->property('maxItems');
}除了默认标记之外,组件还可以提供额外的局部文件,这些文件可以在前端使用,或在默认标记本身内部使用。如果 Demo ToDo 组件有一个 分页 局部文件,它将位于 /plugins/october/demo/components/todo/pagination.htm 并使用以下方式在页面上显示:
\{% partial 'demoTodo::pagination' %}可以使用一种上下文相关的宽松方法。如果在组件局部视图内调用,它将直接引用自身。如果在主题局部视图内调用,它将扫描页面/布局中使用的所有组件,以查找匹配的局部视图名称并使用该局部视图。
\{% partial '@pagination' %}多个组件可以通过将局部文件放置在一个名为 components/partials 的目录中来共享局部文件。该目录中找到的局部文件在找不到常规组件局部文件时,将作为备用。例如,位于 /plugins/acme/blog/components/partials/shared.htm 的共享局部文件可以由任何组件使用以下方式在页面上显示:
\{% partial '@shared' %}组件可以在其局部视图中通过使用 __SELF__ 变量引用自身。默认情况下,它将返回组件的短名称或别名。
<form data-request="\{\{__SELF__}}::onEventHandler">
[...]
</form>组件也可以引用自身属性。
\{% for item in __SELF__.items() %}
\{\{ item }}
\{% endfor %}如果在组件局部中你需要渲染另一个组件局部,请将 __SELF__ 变量与局部名称连接起来:
\{% partial __SELF__~"::screenshot-list" %}如果在同一个页面上,一个相同的组件被调用两次,可以使用一个 id 属性来引用每个实例。
\{\{__SELF__.id}}ID 在组件每次显示时都是唯一的。
\{% component 'demoTodo' %}
\{% component 'demoTodo' %}您可以使用 renderPartial 方法在 PHP 代码中以编程方式渲染组件局部。这将检查组件中名为 component-partial.htm 的局部,并以字符串形式返回结果。第二个参数用于传递视图变量。当您在 PHP 中渲染组件局部时,与在 Twig 中一样,应用相同的路径解析逻辑;使用 @ 前缀来引用组件内部的局部。
$content = $this->renderPartial('@component-partial.htm');
$content = $this->renderPartial('@component-partial.htm', [
'name' => 'John Smith'
]);例如,要将局部视图渲染为对 AJAX 处理程序 的响应:
function onGetTemplate()
\{
return ['#someDiv' => $this->renderPartial('@component-partial.htm')];
}另一个例子可以是通过从 onRun 页面执行周期返回一个值来覆盖整个页面视图响应。 这段代码将具体使用 Response facade 返回一个 XML 响应:
public function onRun()
\{
$content = $this->renderPartial('@default.htm');
return Response::make($content)->header('Content-Type', 'text/xml');
}组件可以向它们所附加到的页面或布局注入资源 (CSS 和 JavaScript 文件)。使用控制器的addCss和addJs方法来向 CMS 控制器添加资源。这可以在组件的onRun方法中完成。
public function onRun()
\{
$this->addJs('/plugins/acme/blog/assets/javascript/blog-controls.js');
}如果 addCss 和 addJs 方法参数中指定的路径以斜杠 (/) 开头,那么它将相对于网站根目录。如果资产路径不以斜杠开头,那么它将相对于组件目录。
addCss 和 addJs 方法提供一个第二个参数,用于将您注入资产的属性定义为一个数组。
public function onRun()
\{
$this->addJs('/plugins/acme/blog/assets/javascript/blog-controls.js', ['defer' => true]);
}]
]
]
];
}
The method should return an array with the property keys as indexes and property parameters as values. The property keys are used for accessing the component property values inside the component class.
> The property parameters and available types are described in the [inspector types article](../element/inspector-types).
Inside the component you can read the property value with the `property` method:
If the property value is not defined, you can supply the default value as a second parameter of the `property` method:
You can also load all the properties as array:
To access the property from the Twig partials for the component, utilize the `__SELF__` variable which refers to the Component object:
## Routing Parameters
Components can directly access routing parameter values defined in the [URL of the page](../cms/themes/pages).
In some cases a component property may act as a hard coded value or reference the value from the URL. This hard coded example shows the blog post with an identifier `2` being used:
Alternatively the value can be referenced dynamically from the page URL using an [external property value in a component](../cms/themes/components).
In both cases the value can be retrieved by using the `property` method:
If you need to access the routing parameter name:
## Handling the Page Execution Cycle
Components can be involved in the Page execution cycle events by overriding the `onRun` method in the component class. The CMS controller executes this method every time when the page or layout loads. Inside the method you can inject variables to the Twig environment through the `page` property:
### Page Execution Life Cycle Handlers
When a page loads, October executes handler functions that could be defined in the layout and page PHP section and component classes. The sequence the handlers are executed is as follows.
1. Layout `onInit()` function.
1. Page `onInit()` function.
1. Layout `onStart()` function.
1. Layout components `onRun()` method.
1. Layout `onBeforePageStart()` function.
1. Page `onStart()` function.
1. Page components `onRun()` method.
1. Page `onEnd()` function.
1. Layout `onEnd()` function.
### Component Initialization
Sometimes you may wish to execute code at the time the component class is first instantiated. You may override the `init` method in the component class to handle any initialization logic, this will execute before AJAX handlers and before the page execution life cycle. For example, this method can be used for attaching another component to the page dynamically.
### Halting With a Response
Like all methods in the [Layout Execution Life Cycle](../cms/themes/layouts), if the `onRun` method in a component returns a value, this will stop the cycle at this point and return the response to the browser. Here we return an access denied message using the `Response` facade:
You can also return a 404 response from the `onRun` method:
## AJAX Handlers
Components can host AJAX event handlers. They are defined in the component class exactly like they can be defined in the [page or layout code](../cms/ajax/handlers). An example AJAX handler method defined in a component class:
If the alias for this component was *demoTodo* this handler can be accessed by `demoTodo::onAddItem`. See the [AJAX handlers article](../cms/ajax/handlers) to learn about using AJAX with components.
## Default Markup
All components can come with default markup that is used when including it on a page with the `{% component %}` tag, although this is optional. Default markup is kept inside the **component partials directory**, which has the same name as the component class in lower case.
The default component markup should be placed in a file named **default.htm**. For example, the default markup for the Demo ToDo component is defined in the file **/plugins/october/demo/components/todo/default.htm**. It can then be inserted anywhere on the page by using the `{% component %}` tag:
cmstemplate
:::
The default markup can also take parameters that override the component properties at the time they are rendered.
These properties will not be available in the `onRun` method since they are established after the page cycle has completed. Instead they can be processed by overriding the `onRender` method in the component class. The CMS controller executes this method before the default markup is rendered.
## Component Partials
In addition to the default markup, components can also offer additional partials that can be used on the front-end or within the default markup itself. If the Demo ToDo component had a **pagination** partial, it would be located in **/plugins/october/demo/components/todo/pagination.htm** and displayed on the page using:
A relaxed method can be used that is contextual. If called inside a component partial, it will directly refer to itself. If called inside a theme partial, it will scan all components used on the page/layout for a matching partial name and use that.
Multiple components can share partials by placing the partial file in a directory called **components/partials**. The partials found in this directory are used as a fallback when the usual component partial cannot be found. For example, a shared partial located in **/plugins/acme/blog/components/partials/shared.htm** can be displayed on the page by any component using:
### Referencing "self"
Components can reference themselves inside their partials by using the `__SELF__` variable. By default it will return the [component's short name or alias](../cms/themes/components).
Components can also reference their own properties.
If inside a component partial you need to render another component partial concatenate the `__SELF__` variable with the partial name:
### Unique Identifier
If an identical component is called twice on the same page, an `id` property can be used to reference each instance.
The ID is unique each time the component is displayed.
## Rendering Partials from Code
You may programmatically render component partials inside the PHP code using the `renderPartial` method. This will check the component for the partial named `component-partial.htm` and return the result as a string. The second parameter is used for passing view variables. The same path resolution logic applies when you render a component partial in PHP as it does with Twig; use the `@` prefix to refer to partials within the component itself.
For example, to render a partial as a response to an [AJAX handler](../cms/ajax/handlers):
Another example could be overriding the entire page view response by returning a value from the `onRun` page execution cycle. This code will specifically return an XML response using the `Response` facade:
## Injecting Page Assets with Components
Components can inject assets (CSS and JavaScript files) to pages or layouts they're attached to. Use the controller's `addCss` and `addJs` methods to add assets to the CMS controllers. It could be done in the component's `onRun` method.
If the path specified in the `addCss` and `addJs` method argument begins with a slash (`/`) then it will be relative to the website root. If the asset path does not begin with a slash then it is relative to the component directory.
The `addCss` and `addJs` methods provide a second argument that defines the attributes of your injected asset as an array.
#### See Also
* [Inspector Types](../element/inspector-types)