如何构建OctoberCMS Widget插件

tech2022-10-04  59

In a previous article we talked about the basics of creating an OctoberCMS plugin. In this one, we’re going to go deeper, and we’ll explore how we can extend the OctoberCMS backend using widgets.

在上一篇文章中,我们讨论了创建OctoberCMS插件的基础知识。 在这一篇中,我们将做得更深入,我们将探索如何使用小部件扩展OctoberCMS后端。

我们将要建立的 (What We Are Going To Build)

I will assume that many of you have used WordPress. On the dashboard, we have a quick draft widget. In this article, we’re going to see how we can build a similar one for OctoberCMS.

我假设许多人都使用过WordPress 。 在仪表板上,我们有一个快速草稿小部件。 在本文中,我们将看到如何为OctoberCMS构建类似的数据库。

插件注册 (Plugin Registration)

To start building our plugin, we’ll use a command utility for scaffolding.

要开始构建我们的插件,我们将使用命令实用程序进行脚手架。

php artisan create:plugin RAFIE.quicknote

The command will create a Plugin.php file and an updates folder containing the version.yaml file.

该命令将创建一个Plugin.php文件和一个包含version.yaml文件的updates文件夹。

public function pluginDetails(){ return [ 'name' => 'Quick Note Widget', 'description' => 'Add and manage some drafts when you\'re in a hurry.', 'author' => 'RAFIE Younes', 'icon' => 'icon-pencil' ]; }

After registering our plugin we need to update our version.yaml file.

注册我们的插件后,我们需要更新我们的version.yaml文件。

// uploads/version.yaml 1.0.1: First version of quicknote 1.0.2: - Created Notes Table - create_notes_table.php

使用模型 (Using Models)

To create our migration and model files we can use the php artisan create:model RAFIE.quicknote Note command. Our updates/create_notes_table.php migration file handles the creation of the notes table, and will have the following structure.

要创建我们的迁移和模型文件,我们可以使用php artisan create:model RAFIE.quicknote Note命令。 我们的updates/create_notes_table.php迁移文件处理了notes表的创建,并将具有以下结构。

Because every note belongs to a user, we have a user_id field. Our migration file will look like this:

因为每个注释都属于一个用户,所以我们有一个user_id字段。 我们的迁移文件如下所示:

public function up(){ Schema::create('rafie_quicknote_notes', function($table){ $table->engine = 'InnoDB'; $table->increments('id'); $table->string('title', 255); $table->text('description')->nullable(); $table->integer('user_id')->unsigned()->index(); $table->timestamps(); }); } public function down(){ Schema::dropIfExists('rafie_quicknote_notes'); }

After updating the file we need to refresh our plugin to reflect the changes using the following command, which will reinstall the plugin and create our database table.

更新文件后,我们需要使用以下命令刷新插件以反映更改,该命令将重新安装插件并创建我们的数据库表。

artisan plugin:refresh RAFIE.quicknote

If you are a Laravel fan, you know that every model class must extend the Illuminate\Database\Eloquent\Model to use eloquent, yet OctoberCMS has an October\Rain\Database\Model class that extends the Eloquent model class, and provides more extensibility, like the extend method.

如果您是Laravel爱好者,则知道每个模型类都必须扩展Illuminate\Database\Eloquent\Model才能使用雄辩,但是OctoberCMS具有October\Rain\Database\Model类,该类扩展了Eloquent模型类,并提供了更大的可扩展性,就像extend方法一样。

public function boot(){ User::extend(function($model){ $model->hasMany['notes'] = ['RAFIE\Quicknote\Models\Notes']; }); }

Inside the Plugin.php file, we override the boot method, which gets called on every request, and extend the User model to have many notes.

在Plugin.php文件中,我们重写了boot方法,该方法在每次请求时都会被调用,并扩展User模型以包含许多注释。

class Note extends Model{ // used for automatic validation using the defined rules. use \October\Rain\Database\Traits\Validation; public $table = 'rafie_quicknote_notes'; protected $guarded = ['*']; protected $rules = [ 'title' => 'required|min:4' ]; public $belongsTo = [ 'user' => [ 'Backend\Models\User' ] ]; }

Inside our models/Note.php files we have a basic model definition. Also, the belongsTo attribute defines our model relationship. The validation is automatically handled by the validation trait using the defined rules.

在models/Note.php文件中,我们有一个基本的模型定义。 另外, belongsTo属性定义我们的模型关系。 验证由验证特征使用定义的规则自动处理。

OctoberCMS窗口小部件 (OctoberCMS Widgets)

OctoberCMS widgets are blocks of content that can be integrated in different ways to your font-end or back-end. There are three different kinds of widgets.

OctoberCMS窗口小部件是内容块,可以以不同方式集成到字体端或后端。 共有三种不同的小部件。

通用小部件 (Generic Widgets)

Generic widgets are bundles functionality that can be injected to the page. They behave like components and are stored inside the widgets folder. You can read more in the docs.

通用小部件是可插入页面的捆绑功能。 它们的行为类似于组件,并存储在widgets文件夹内。 您可以在docs中阅读更多内容。

表单小部件 (Form Widgets)

Form widgets are a bit special, they allow you to create new types of controls that can be used by other plugins or by the CMS itself. A good example from the docs is the CodeEditor control used for creating pages, and there is also the search box used in many places, and integrated by default inside our notes management page.

表单小部件有点特殊,它们使您可以创建可以由其他插件或CMS本身使用的新型控件。 文档中的一个很好的例子是用于创建页面的CodeEditor控件,并且搜索框在很多地方都使用过,默认情况下集成在我们的便笺管理页面中。

报告小部件 (Report Widgets)

Report widgets are the most known type of widgets. They add content on a specific context, in this case the dashboard. On a fresh installation of OctoberCMS we have a SYSTEM STATUS widget displaying available updates and the website status.

报表窗口小部件是最著名的窗口小部件类型。 他们在特定的上下文中添加内容,在本例中为仪表板。 在OctoberCMS的全新安装中,我们有一个SYSTEM STATUS小部件,显示可用的更新和网站状态。

脚手架我们的小部件 (Scaffolding Our Widget)

To begin, let’s create a QuickNoteWidget.php file where we can define our QuickNote widget, and we need also to register our widget inside our Plugin.php file.

首先,让我们创建一个QuickNoteWidget.php文件,在其中可以定义QuickNote小部件,并且还需要在我们的Plugin.php文件中注册小部件。

// Plugin.php public function registerReportWidgets(){ return [ 'RAFIE\QuickNote\QuickNoteWidget' => [ 'label' => 'Quick Notes', 'context' => 'dashboard' ] ]; } class QuickNoteWidget extends ReportWidgetBase{ public function render(){ $notes = BackendAuth::getUser()->notes; return $this->makePartial('notes', [ 'notes' => $notes ]); } }

Report widgets must extend the ReportWidgetBase class and override the render method.

报表小部件必须扩展ReportWidgetBase类并覆盖render方法。

The BackendAuth::getUser method returns the logged in user, which has the associated list of notes. This is only possible because we’ve defined the relation inside our models.

BackendAuth::getUser方法返回登录的用户,该用户具有关联的注释列表。 这是唯一可能的,因为我们已经在模型内部定义了关系。

OctoberCMS will look for our assets inside a directory with the same name as our widget file name lowercased. The partials directory holds our views and they must start with an underscore.

OctoberCMS将在目录中查找我们的资产,该目录与小部件文件名小写。 partials目录包含我们的视图,并且它们必须以下划线开头。

//quicknotewidget/partials/_notes.htm <div class="report-widget"> <h3>Quick Note</h3> <div class="pane"> <ul class="list-nostyle"> <?php foreach( $notes as $note ): ?> <li class="list-group-item"><?= $note->title ?></li> <?php endforeach ?> </ul> </div> <br/> <?= Form::open([ 'url' => Backend::url('rafie/quicknote/notes/store'), 'method' => 'POST' ]); ?> <div class="form-group"> <input class="form-control" type="text" name="title" placeholder="Title" required /> </div> <div class="form-group"> <textarea class="form-control" name="description" id="" cols="30" rows="10" placeholder="You have something to say?"></textarea> </div> <div class="form-group"> <input type="submit" class="btn btn-primary" value="Submit" /> <a href="<?= Backend::url('rafie/quicknote/notes/index') ?>">Manage your notes</a> </div> <?= Form::close(); ?> </div>

The widget’s HTML code must be contained within a report-widget class and optionally have an <h3> for the widget title. After that we loop through the list of user notes and show a form for creating a new note.

窗口小部件HTML代码必须包含在report-widget类中,并且可以选择在窗口小部件标题中使用<h3> 。 之后,我们遍历用户注释列表,并显示用于创建新注释的表单。

The Backend::url('rafie/quicknote/notes/store') will generate a link to our notes controller where we can process our form data. We could have used OctoberCMS’ Ajax framework for the form, but to keep things simple we’re going to normally submit the form and talk about using AJAX later.

Backend::url('rafie/quicknote/notes/store')将生成一个指向我们的notes控制器的链接,我们可以在其中处理表单数据。 我们本可以使用OctoberCMS的Ajax框架作为表单,但是为了使事情简单,我们通常将提交表单,然后再讨论使用AJAX。

使用控制器 (Using Controllers)

Controllers are stored within the controllers folder, and can be generated using a scaffolding command.

控制器存储在controllers文件夹中,并且可以使用脚手架命令生成。

php artisan create:controller RAFIE.quicknote Notes

创建笔记 (Creating Notes)

The folder contains our controller class, and another folder for our assets. When someone hits a URL, it’s parsed as the following: author/plugin/controllerClass/method. In our case, it’s rafie/quicknote/notes/store, if no method is specified, the index method is fired by default.

该文件夹包含我们的控制器类,以及我们资产的另一个文件夹。 当有人点击URL时,其解析如下: author/plugin/controllerClass/method 。 在我们的例子中,它是rafie/quicknote/notes/store ,如果未指定任何方法,则默认情况下会触发index方法。

public function store(){ $note = new Models\Note; $note->title = Input::get('title'); $note->description = Input::get('description', null); $note->user_id = BackendAuth::getUser()->id; if( $note->save() ) { \Flash::success('Note added successfully.'); } else{ \Flash::error('Validation error' ); } return \Redirect::to( Backend::url() ); }

We create a new note using our model and save it to the database. The Flash::success() will display a flash message to the user to confirm the insertion.

我们使用模型创建一个新注释,并将其保存到数据库中。 Flash::success()将向用户显示一条Flash消息,以确认插入。

You may have noticed the Manage your notes link pointing to the rafie/quicknote/notes/index url. This is where we’re going to list our notes so that the user can add, edit, and delete notes.

您可能已经注意到“ Manage your notes链接指向了rafie/quicknote/notes/index网址。 这是我们要列出注释的地方,以便用户可以添加,编辑和删除注释。

小部件配置 (Widget Configuration)

Let’s say that we want to give the user the ability to choose how the widget renders. We can give him the choice to hide the list of notes from the dashboard widget, and keep just the form and the link, and even change the widget title.

假设我们要让用户能够选择小部件的呈现方式。 我们可以给他选择隐藏仪表板小部件中的注释列表,仅保留表单和链接,甚至更改小部件标题的可能性。

// QuickNoteWidget.php public function defineProperties(){ return [ 'title' => [ 'title' => 'Widget title', 'default' => 'QUICK NOTE' ], 'showList' => [ 'title' => 'Show notes', 'type' => 'checkbox' ] ]; }

If you don’t know what properties are, be sure to check my article about How to build an OctoberCMS plugin. After defining the properties we can retrieve them inside our view.

如果您不知道什么是属性,请务必查看有关如何构建OctoberCMS插件的文章 。 定义属性后,我们可以在视图中检索它们。

// quicknotewidget/partials/_notes.htm <h3><?= $this->property('title') ?></h3> <?php if( $this->property('showList') ): ?> <ul class="list-nostyle"> <?php foreach( $notes as $note ): ?> <li class="list-group-item"><?= $note->title ?></li> <?php endforeach ?> </ul> <br/> <?php endif; ?>

使用控制器 (Using Controllers)

Up until now, we talked about rendering a report widget and submitting it to the controller. Now, we’re going to build the listing page and talk about extending the OctoberCMS backend by creating the listing and management part.

到目前为止,我们讨论了呈现报告小部件并将其提交给控制器的问题。 现在,我们将构建列表页面,并讨论通过创建列表和管理部分来扩展OctoberCMS后端的方法。

上市须知 (Listing Notes)

Every controller has a config_list.yaml file where you can define your listing, and it’s also mapped to a model using some configuration attributes.

每个控制器都有一个config_list.yaml文件,您可以在其中定义列表,还可以使用一些配置属性将其映射到模型。

// controllers/notes/config_list.yaml # Model List Column configuration list: $/rafie/quicknote/models/note/columns.yaml # Model Class name modelClass: RAFIE\Quicknote\Models\Note # List Title title: Manage Notes # Link URL for each record recordUrl: rafie/quicknote/notes/update/:id # Message to display if the list is empty noRecordsMessage: backend::lang.list.no_records # Records to display per page recordsPerPage: 20 # Displays the list column set up button showSetup: true # Displays the sorting link on each column showSorting: true # Default sorting column defaultSort: column: created_at direction: desc # Display checkboxes next to each record showCheckboxes: true # Toolbar widget configuration toolbar: # Partial for toolbar buttons buttons: list_toolbar # Search widget configuration search: prompt: backend::lang.list.search_prompt

The attributes are generally well explained, but let’s add more clarification to some:

通常已经很好地解释了属性,但让我们对一些属性进行更多说明:

list: path to the list columns definition.

list:列表列定义的路径。 showSetup: little button to toggle the columns display.

showSetup:切换列显示的小按钮。 showCheckboxes: show a checkbox for every row, used for bulk actions.

showCheckboxes:为每行显示一个复选框,用于批量操作。

toolbar -> button: partial name, if you want to show some controls above the table, like New or Delete.

工具栏->按钮:部分名称,如果要在表格上方显示一些控件,例如New或Delete 。

// rafie/quicknote/models/note/columns.yaml columns: id: label: ID title: label: TITLE searchable: true description: label: DESCRIPTION searchable: true created_at: label: CREATED AT type: date invisible: true updated_at: label: UPDATED AT type: date invisible: true

The columns.yaml file maps our database table to the list using label, searchable, etc. Check the docs for the full list of attributes.

columns.yaml文件使用label , searchable等将我们的数据库表映射到列表。检查文档以获取完整的属性列表。

// controllers/Notes.php public function index(){ $this->makeLists(); $this->makeView('index'); } // controllers/notes/index.htm <?= $this->listRender() ?>

Inside our Notes@index method we parse the list configuration and we render it inside our index view. You can also add some extra markup if you have any extra fields.

在Notes@index方法中,我们解析列表配置,并将其呈现在索引视图中。 如果您有任何其他字段,也可以添加一些额外的标记。

If you hit the index controller you’ll see a basic listing of our notes. The list configuration is pulling all notes from the table including other users notes, and that’s not what we want, but we can extend the listExtendQueryBefore method from the ListController class and filter user notes.

如果您点击索引控制器,您将看到我们笔记的基本清单。 列表配置从表中拉出所有笔记,包括其他用户笔记,这不是我们想要的,但是我们可以从ListController类扩展listExtendQueryBefore方法并过滤用户笔记。

// controllers/Notes.php public function listExtendQueryBefore($query){ $user_id = BackendAuth::getUser()->id; $query->where('user_id', '=', $user_id); }

Now you should see that the list is filtered by the logged in user, and just to get familiar with it, let’s try to distinguish the notes that have no description by extending our columns using the listOverrideColumnValue method.

现在您应该看到列表是由登录用户过滤的,并且为了熟悉它,让我们尝试使用listOverrideColumnValue方法扩展列,以区分没有描述的listOverrideColumnValue 。

// controllers/Notes.php public function listOverrideColumnValue($record, $columnName){ if( $columnName == "description" && empty($record->description) ) return "[EMPTY]"; }

You can combine the listOverrideColumnValue with listExtendColumns to add new columns and insert new values, like adding a ‘mark as read’ button for each row, etc.

您可以将listOverrideColumnValue与listExtendColumns以添加新列和插入新值,例如为每行添加“标记为已读”按钮等。

public function listExtendColumns($list){ $list->addColumns([ 'action' => [ 'label' => 'Actions', 'sortable' => false ] ]); }

If you remember, inside our config_list.yaml we had a list_toolbar field:

如果您还记得,在我们的config_list.yaml我们有一个list_toolbar字段:

// controllers/notes/_list_toolbar.htm <div data-control="toolbar"> <a href="<?= Backend::url('rafie/quicknote/notes/create') ?>" class="btn btn-primary oc-icon-plus">New Note</a> <button id = "remove_notes" class="btn btn-primary oc-icon-trash-o" data-request="onDelete" data-trigger-type="enable" data-trigger = ".list-checkbox input[type='checkbox']" data-trigger-condition="checked" data-request-success="$el.attr('disabled', 'disabled');" disabled > Remove Note(s)</button> </div> <script> $("#remove_notes").click(function(){ $(this).data('request-data', { notes: $('.list-checkbox input[type=\'checkbox\']').listWidget('getChecked') }) }); </script>

新笔记 (New Notes)

I know that we’ve already created a form for adding new notes, but let’s see the other way of mapping forms to models.

我知道我们已经创建了一个用于添加新注释的表单,但是让我们看看将表单映射到模型的另一种方法。

The controllers/notes/config_form.yaml file is responsible for displaying the create/update form using the specified model, and it can also be configured using the models/note/fields.yaml file.

controllers/notes/config_form.yaml文件负责使用指定的模型显示创建/更新表单,也可以使用models/note/fields.yaml文件进行配置。

// controllers/notes/config_form.yaml # Record name name: Note # Model Form Field configuration form: $/rafie/quicknote/models/note/fields.yaml # Model Class name modelClass: RAFIE\Quicknote\Models\Note # Default redirect location defaultRedirect: rafie/quicknote/notes # Create page create: title: Create Notes redirectClose: rafie/quicknote/notes # Update page update: title: Edit Notes redirectClose: rafie/quicknote/notes

Configuration attributes are self descriptive. The form attribute maps to the models/note/fields.yaml which decides how the form should be displayed. You can read about the available attribute in the docs.

配置属性是自描述的。 form属性映射到models/note/fields.yaml ,该models/note/fields.yaml决定应如何显示表单。 您可以在docs中阅读有关available属性的信息。

fields: title: placeholder: Title description: type: textarea size: huge placeholder: You have something to say? important: type: hint path: @/plugins/rafie/quicknote/models/note/partials/create_note_hint.htm

We are creating only one text input for the title and a huge textarea for the description. The hint is a partial that you can use to display notifications to the user.

我们只为标题创建一个文本输入,为描述创建一个巨大的文本区域。 提示是部分内容,可用于向用户显示通知。

If you noticed the page url rafie/quicknote/notes/create, you may be wondering where we rendered the form? The answer is the Backend\Behaviors\FormController implemented by our controller – it takes care of displaying the form using our configuration files.

如果您注意到页面网址rafie/quicknote/notes/create ,您可能想知道我们在哪里渲染了表单? 答案是由我们的控制器实现的Backend\Behaviors\FormController –它负责使用我们的配置文件显示表单。

After creating your new note, you can visit the database to verify records. You’ll find that the user_id is set to 0 and that’s not what we want!

创建新笔记后,您可以访问数据库以验证记录。 您会发现user_id设置为0 ,这不是我们想要的!

public function formBeforeCreate($model){ $model->user_id = BackendAuth::getUser()->id; }

The FormController class provides a set of methods to hook into form events. The formBeforeCreate method is used to update the model before saving, and you can also hook to the formAfterCreate to fire some special events.

FormController类提供了一组可以挂接到表单事件中的方法。 formBeforeCreate方法用于在保存之前更新模型,还可以挂钩到formAfterCreate来formAfterCreate一些特殊事件。

更新说明 (Updating Notes)

For the update form, we won’t need any modification because we mapped the form fields to our note model. However, you may need to add some functionality. If you have some code that needs to be executed, you can use the update method and call the parent update method as an extension. Be sure to explore the FormController class to see the list of available methods.

对于更新表单,我们不需要任何修改,因为我们将表单字段映射到我们的笔记模型。 但是,您可能需要添加一些功能。 如果您需要执行一些代码,则可以使用update方法并将父update方法作为扩展名。 确保探索FormController类以查看可用方法的列表。

public function update($recordId, $context = null) { //some code here return $this->asExtension('FormController')->update($recordId, $context); }

删除笔记 (Removing Notes)

We have two ways to implement the remove action:

我们有两种方法来执行删除操作:

Using the update form:

使用更新表格:

When using the update form you have a trash icon at the bottom right of the browser and it’s already configured for you.

使用更新表单时,浏览器右下方有一个垃圾桶图标,并且已经为您配置了该图标。

Using a bulk action:

使用批量操作:

You remember that we’ve set the

您还记得我们已经设置了

showCheckboxes to true inside the config_list.yaml file. We only need to configure the remove notes button.

在config_list.yaml文件showCheckboxes为true 。 我们只需要配置删除注释按钮。

The _list_toolbar.htm partial is where we have our New Note button. We will add our remove notes button using OctoberCMS’ Ajax framework. If you’re not familiar with the AJAX framework, be sure to check my building OctoberCMS theme article.

_list_toolbar.htm部分是我们的“ New Note按钮的位置。 我们将使用OctoberCMS的Ajax框架添加“删除注释”按钮。 如果您不熟悉AJAX框架,请务必查看我的建筑OctoberCMS主题文章。

// controllers/notes/_list_toolbar.htm <button id = "remove_notes" class="btn btn-primary oc-icon-trash-o" data-request="onDelete" data-trigger-type="enable" data-trigger = ".list-checkbox input[type='checkbox']" data-trigger-condition="checked" data-request-success="$el.attr('disabled', 'disabled');" disabled > Remove Note(s)</button>

The only special attributes are: – data-trigger: add an event listener on the specified element. – data-trigger-condition: the condition can be checked or a value if value[myvalue] is set. Check the trigger api for more info. – data-request-success: JavaScript code to be executed after a successful request.

唯一的特殊属性是:–数据触发:在指定元素上添加事件侦听器。 –数据触发条件:可以checked条件,或者如果设置了value[myvalue]则为值。 检查触发器API以获得更多信息。 –数据请求成功:成功请求后要执行JavaScript代码。

// controllers/notes/_list_toolbar.htm <script> $("#remove_notes").click(function(){ $(this).data('request-data', { notes: $('.list-checkbox input[type=\'checkbox\']').listWidget('getChecked') }) }); </script>

On click events, we need to pass the selected IDs to the request-data attribute so that we can process them on the server side. Our controller must have an onDelete method to handle the request.

在点击事件中,我们需要将选定的ID传递给request-data属性,以便我们可以在服务器端对其进行处理。 我们的控制器必须具有onDelete方法来处理请求。

// controllers/Notes.php public function onDelete(){ $user_id = BackendAuth::getUser()->id; $notes = post("notes"); Note::whereIn('id', $notes) ->where('user_id', '=', $user_id) ->delete(); \Flash::success('Notes Successfully deleted.'); return $this->listRefresh(); }

After deleting the notes we show a flash message to the user as a feedback, and we refresh the list using listRefresh, which regenerates the list and shows the new one on the page.

删除注释后,我们会向用户显示一条listRefresh消息作为反馈,然后使用listRefresh刷新列表,该列表将重新生成列表并在页面上显示新列表。

结论 (Conclusion)

OctoberCMS’ widget system is powerful and flexible, and it provides a set of components to create and extend other plugins. You can take a look at the final result on Github, and if you have any question or opinions let me know in the comments!

OctoberCMS的小部件系统功能强大且灵活,它提供了一组用于创建和扩展其他插件的组件。 您可以在Github上查看最终结果,如果有任何疑问或意见,请在评论中告诉我!

翻译自: https://www.sitepoint.com/build-octobercms-widget-plugin/

最新回复(0)