组件文件和目录位于插件目录的 components 子目录中。每个组件都有一个定义组件类的 PHP 文件,以及一个可选的组件局部目录。组件局部目录的名称与小写形式的组件类名称相匹配。以下是一个组件目录结构示例:
├── plugins
| └── acme
| └── blog
| ├── components
| | ├── blogposts ← 局部目录
| | | └── default.htm ← 默认标记 (可选)
| | └── BlogPosts.php ← 组件类
| └── 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 方法来注册。这会告知 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 partials 访问属性,请利用 __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外观返回一个访问被拒绝的消息:
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;
} If the alias for this component was *demoTodo* this handler can be accessed bydemoTodo::onAddItem`. See the AJAX handlers article to learn about using AJAX with components.
所有组件都可以带有默认标记,该标记在页面中包含它时使用 {% 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' %}你可以在 PHP 代码中使用 renderPartial 方法以编程方式渲染组件局部视图。这会检查组件中名为 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 外观返回一个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('assets/javascript/blog-controls.js');
}如果 addCss 和 addJs 方法中指定的路径相对于组件目录。 addCss 和 addJs 方法提供第二个参数,该参数将注入资源的属性定义为一个数组。
public function onRun()
\{
$this->addJs('assets/javascript/blog-controls.js', ['defer' => true]);
}如果第一个参数以斜杠 (/) 开头,那么它将相对于网站根目录。如果您需要引用组件上下文之外的资源,请使用此方法。
public function onRun()
\{
$this->addJs('/plugins/acme/blog/assets/javascript/blog-controls.js');
}]
]
]
];
}
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](../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](../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:
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](../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 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.
If the first argument begins with a slash (`/`) then it will be relative to the website root. Use this if you need to reference an asset outside the context of the component.
#### See Also
* [Inspector Types](../element/inspector-types)