💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、星火、月之暗面及文生图 广告
# 组件 ## 组件 ### 组件说明 - 组件文件所在目录 /plugins/用户名/插件名/components - 每个组件都有一个定义组件类的PHP文件和一个可选的组件partials目录。 - 组件的partials目录名称与以小写形式编写的组件类名称匹配。 - 组件目录结构的示例: ``` plugins/ 用户名/ myplugin/ components/ componentname/ <=== Component partials directory default.htm <=== Component default markup (optional) ComponentName.php <=== Component class file Plugin.php ``` 必须使用该方法将组件[注册在Plugin注册类中](https://octobercms.com/docs/plugin/components#component-registration)`registerComponents`。 ### 组件注册 - 必须通过重写`Plugin注册类中`的`registerComponents`方法来注册组件。 - 注册组件的示例:这将使用默认别名名称**demoTodo**注册Todo组件类 ``` public function registerComponents() { return [ 'October\Demo\Components\Todo' => 'demoTodo' ]; } ``` ### 组件类定义 - **组件类文件**定义了组件功能和组件的属性 - 组件类文件名应与组件类名匹配 - 组件类应继承扩展`\Cms\Classes\ComponentBase`该类。 - 定义组件详情的`componentDetails`方法是必需的。该方法应返回带有两个键的数组:`name`和`description`。名称和描述显示在CMS后端用户界面中。 - 示例: `componentDetails`定义属性,`posts`定义方法 ``` namespace Acme\Blog\Components; class BlogPosts extends \Cms\Classes\ComponentBase { public function componentDetails() { return [ 'name' => 'Blog Posts', 'description' => 'Displays a collection of blog posts.' ]; } // This array becomes available on the page as {{ component.posts }} public function posts() { return ['First Post', 'Second Post', 'Third Post']; } } ``` - 当组件附加到页面或布局时,类属性和方法将通过component变量在页面上可用,该变量与组件的短名称或别名匹配。例如,如果示例中的BlogPost组件是在其短名称页面上定义的: ``` url = "/blog" [blogPosts] == ``` 您将可以`posts`通过`blogPosts`变量访问其方法。请注意,Twig支持方法的属性表示法,因此您无需使用方括号。 ``` {% for post in blogPosts.posts %} {{ post }} {% endfor %} ``` ### 组件属性 - 将组件添加到页面或布局时,可以使用属性对其进行配置 - 这些属性是使用`defineProperties`组件类的方法定义的。下一个示例显示如何定义组件属性: ``` public function defineProperties() { return [ 'maxItems' => [ 'title' => 'Max items', 'description' => 'The most amount of todo items allowed', 'default' => 10, 'type' => 'string', 'validationPattern' => '^[0-9]+$', 'validationMessage' => 'The Max Items property can contain only numeric symbols' ] ]; } ``` - 该方法应返回一个数组,其中属性键为索引,属性参数为值。 - 属性键用于访问组件类内部的组件属性值。使用带有以下键的数组定义属性参数: | 键 | 描述 | | --- | --- | | **title** | 必填属性标题,由CMS后端中的组件检查器使用。 | | **description** | 必需的属性描述,由CMS后端中的组件检查器使用。 | | **default** | 可选,将组件添加到CMS后端中的页面或布局时使用的默认属性值。 | | **type** | 可选,指定属性类型。类型定义了属性在检查器中的显示方式。当前支持的类型是**字符串**,**复选框**,**下拉列表**和**设置**。默认值:**字符串**。(**string**,**checkbox**,**dropdown**and**set**. Default:**string**.) | | **validationPattern** | 用户在检查器中输入属性值时使用的可选正则表达式。验证只能与**字符串**属性一起使用。 | | **validationMessage** | 验证失败时显示的可选错误消息。 | | **required** | 可选,强制填写字段。留空时使用validationMessage。 | | **placeholder** | 字符串和下拉列表属性的可选占位符。 | | **options** | 下拉属性的可选选项数组。 | | **depends** | 下拉属性所依赖的属性名称数组。请参阅下面的[下拉属性](#dropdown-properties)。 | | **group** | 可选的组名。小组在Inspector中创建部分,以简化用户体验。在多个属性中使用相同的组名将它们组合在一起。 | | **showExternalParam** | 指定检查器中属性的“外部参数”编辑器的可见性。默认值:**true**。在组件内部,您可以使用以下`property`方法读取属性值: | ``` $this->property('maxItems'); ``` 如果未定义属性值,则可以提供默认值作为方法的第二个参数`property`: ``` $this->property('maxItems', 6); ``` 您还可以将所有属性加载为数组: ``` $properties = $this->getProperties(); ``` 要从Twig局部访问组件的属性,请使用`__SELF__`引用Component对象的变量: `{{ __SELF__.property('maxItems') }}` ### 下拉菜单和设置属性 - 下拉菜单和设置属性的选项列表可以是静态的或动态的 - 静态选项是使用`options`属性定义的元素定义的。例: ``` public function defineProperties() { return [ 'units' => [ 'title' => 'Units', 'type' => 'dropdown', 'default' => 'imperial', 'placeholder' => 'Select units', 'options' => ['metric'=>'Metric', 'imperial'=>'Imperial'] //定义静态选项 ] ]; } ``` - 如果上面方法返回数组没有`options`键,则选项列表被视为动态列表。 - 组件类必须定义一个返回选项列表的方法。该方法的名称应采用以下格式:`get*Property*Options``get属性名Options` - 如:`getCountryOptions`该方法返回一个选项数组,其中选项值作为键,选项标签作为值 - 示例: ``` public function defineProperties() { return [ 'country' => [ 'title' => 'Country', 'type' => 'dropdown', 'default' => 'us' ] ]; } public function getCountryOptions() { return ['us'=>'United states', 'ca'=>'Canada']; } ``` 动态下拉列表和设置列表可能取决于其他属性。例如,州列表可能取决于所选国家。依赖项`depends`在属性定义中用参数声明。下一个示例定义了两个动态下拉列表属性,状态列表取决于国家/地区: ``` public function defineProperties() { return [ 'country' => [ 'title' => 'Country', 'type' => 'dropdown', 'default' => 'us' ], 'state' => [ 'title' => 'State', 'type' => 'dropdown', 'default' => 'dc', 'depends' => ['country'], 'placeholder' => 'Select a state' ] ]; } ``` 为了加载状态列表,您应该知道检查器中当前选择的国家。检查器将所有属性值POST到`getPropertyOptions`处理程序,因此您可以执行以下操作: ``` public function getStateOptions() { $countryCode = Request::input('country'); // Load the country property value from POST $states = [ 'ca' => ['ab'=>'Alberta', 'bc'=>'British columbia'], 'us' => ['al'=>'Alabama', 'ak'=>'Alaska'] ]; return $states[$countryCode]; } ``` ### 页面列表属性 - 有时组件需要创建指向网站页面的链接。例如,博客文章列表包含指向博客文章详细信息页面的链接 - October包含一个用于创建动态下拉列表的帮助器 - 下一个示例定义了postPage属性,该属性显示页面列表: ``` public function defineProperties() { return [ 'postPage' => [ 'title' => 'Post page', 'type' => 'dropdown', 'default' => 'blog/post' ] ]; } public function getPostPageOptions() { return Page::sortBy('baseFileName')->lists('baseFileName', 'baseFileName'); } ``` ### 路由参数 - 组件可以直接访问页面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'); ``` - 如果需要访问路由参数名称: ``` $this->paramName('id'); // Returns "my_custom_parameter" ``` ### 处理页面执行周期 通过重写`onRun`组件类中的方法,组件可以参与Page执行周期事件。每次加载页面或布局时,CMS控制器都会执行此方法。在方法内部,您可以通过`page`属性将变量注入页面: ``` 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'; // 向页面中注入一些变量 } ``` ### 页面执行生命周期处理程序 页面加载时,October执行可以在布局和页面PHP部分以及组件类中定义的处理程序函数。处理程序的执行顺序如下: 1. 布局`onInit()`功能。 2. 页面`onInit()`功能。 3. 布局`onStart()`功能。 4. 布局组件`onRun()`方法。 5. 布局`onBeforePageStart()`功能。 6. 页面`onStart()`功能。 7. 页面组件`onRun()`方法。 8. 页面`onEnd()`功能。 9. 布局`onEnd()`功能。 ### 组件初始化 - 初始化方法是在首次实例化组件类时执行代码。 - 重写`init`组件类中的方法以处理任何初始化逻辑,都将在AJAX处理程序之前和页面执行生命周期之前执行。 - 此方法可用于将另一个组件动态附加到页面。 ``` public function init() { $this->addComponent('Acme\Blog\Components\BlogPosts', '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事件处理程序与在页面或布局代码中定义的方式完全相同 - 在组件类中定义的示例AJAX处理程序方法: ``` public function onAddItem() { $value1 = post('value1'); $value2 = post('value2'); $this->page['result'] = $value1 + $value2; } ``` - 如果此组件的别名为\*demoTodo,\*则可以通过访问此处理程序`demoTodo::onAddItem`。 - 有关将AJAX与组件一起使用的详细信息,请参见[在组件](https://octobercms.com/docs/ajax/handlers#calling-handlers)文章中[定义](https://octobercms.com/docs/ajax/handlers#calling-handlers)的“[调用AJAX处理程序”](https://octobercms.com/docs/ajax/handlers#calling-handlers)。 ### 默认标记 - 所有组件都可以带有默认标记,默认标记是将其包含在带有`{% component %}`标签的页面上时使用的 - 默认标记保存在**组件partials目录中**,该**目录**与小写的组件类同名。 - 默认组件标记应放置在名为**default.htm**的文件中。 - 例如,在文件\*\*/plugins/october/demo/components/todo/default.htm中\*\*定义了Demo ToDo组件的默认标记。然后可以使用`{% component %}`标签将其插入页面上的任何位置: ``` url = "/todo" [demoTodo] == {% component 'demoTodo' %} ``` 默认标记还可以采用在渲染[组件](https://octobercms.com/docs/plugin/components#component-properties)时覆盖[组件属性的](https://octobercms.com/docs/plugin/components#component-properties)参数。 ``` {% 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'); } ``` ### 零件:入分页 - 分页目录:**/plugins/october/demo/components/todo/pagination.htm中,** - 使用以下命令在页面上显示: ``` {% partial 'demoTodo::pagination' %} ``` - 如果在组件局部内部调用,它将直接引用自身 - 如果在主题部分中调用,它将扫描页面/布局上使用的所有组件以查找匹配的部分名称并使用该名称。 ``` {% partial '@pagination' %} ``` - 多个组件可以通过将部分文件放置在名为**components / partials**的目录中来共享**部分**。 - 如果找不到常用的组件零件,则在此目录中找到的局部用作备用。 - 例如,任何组件都可以使用以下命令在页面上显示\*\*/plugins/acme/blog/components/partials/shared.htm中\*\*的共享部分: ``` {% partial '@shared' %} ``` ### 引用“自我” 组件可以使用该`__SELF__`变量在自己的部分中引用自己。默认情况下,它将返回组件的简称或[别名](https://octobercms.com/docs/cms/components#aliases)。 ``` <form data-request="{{__SELF__}}::onEventHandler"> [...] </form> ``` 组件也可以引用自己的属性。 ``` {% for item in __SELF__.items() %} {{ item }} {% endfor %} ``` 如果在部件局部内部,则需要渲染另一个部件局部`__SELF__`,以部分名称连接变量: ``` {% partial __SELF__~"::screenshot-list" %} ``` ### 唯一标识符 如果在同一页面上两次调用相同的组件,`id`则可以使用属性来引用每个实例。 ``` {{__SELF__.id}} ``` 每次显示组件时,ID都是唯一的。 ``` <!-- ID: demoTodo527c532e9161b --> {% component 'demoTodo' %} <!-- ID: demoTodo527c532ec4c33 --> {% component 'demoTodo' %} ``` ### 渲染主键 - `renderPartial`方法渲染组件,渲染后将结果作为字符串返回 - 第一个方法:组件文件`component-partial.htm` - 第二个参数用于传递视图变量。 ``` $content = $this->renderPartial('component-partial.htm'); $content = $this->renderPartial('component-partial.htm', [ 'name' => 'John Smith' ]); ``` 例如,将部分渲染为对[AJAX处理程序](https://octobercms.com/docs/ajax/handlers)的响应: ``` 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`and`addJs`方法参数中指定的路径以斜杠(/)开头,则它将相对于网站根目录。如果资源路径不是以斜杠开头,则它是相对于组件目录的。 - `addCss`和`addJs`方法提供了您的注入资源的属性定义为一个阵列的第二个参数。有一个特殊的属性--`build`可以为您注入的资源添加指定版本的当前插件后缀。插件升级后,可用于刷新缓存的资产。 ``` public function onRun() { $this->addJs('/plugins/acme/blog/assets/javascript/blog-controls.js', [ 'build' => 'Acme.Test', 'defer' => true ]); } ``` 您也可以使用字符串作为第二个参数,然后默认使用字符串值作为`build`: ``` public function onRun() { $this->addJs('/plugins/acme/blog/assets/javascript/blog-controls.js', 'Acme.Test'); } ```