crud插件_OctoberCMS CRUD-建立团队/项目管理插件

crud插件

So far, we covered different aspects of OctoberCMS. This is a follow up article to discover how to use OctoberCMS for CRUD applications and take a detailed view at how to work with models, relations and controllers. Let’s get started.

到目前为止 ,我们介绍了OctoberCMS的不同方面。 这是一篇后续文章,旨在发现如何将OctoberCMS用于CRUD应用程序,并详细介绍如何使用模型,关系和控制器。 让我们开始吧。

OctoberCMS logo

要求 (Requirements)

I will assume you already know how to set up a working installation of OctoberCMS. If not, you can check out the introduction article, or read the installation section in the documentation.

我假设您已经知道如何设置OctoberCMS的有效安装。 如果没有,您可以查看介绍文章 ,或阅读文档中的安装部分。

我们正在建设 (What We’re Building)

We are going to build a project management plugin where you can add different users to teams and assign them to projects. You can check the final result on GitHub, and feel free to suggest edits or additions to the plugins.

我们将构建一个项目管理插件,您可以在其中将不同的用户添加到团队中并将他们分配给项目。 您可以在GitHub上查看最终结果,并随时建议对插件进行编辑或添加。

设置插件 (Setting up the Plugin)

Let’s start by using the create:plugin scaffolding command to create the initial plugin structure, and define the Plugin::pluginDetails method with our plugin details.

让我们从使用create:plugin scaffolding命令创建初始插件结构开始,并使用我们的插件详细信息定义Plugin::pluginDetails方法。

php artisan create:plugin rafie.sitepointDemo
// Plugin.php

public function pluginDetails()
{
    return [
        'name'        => 'Project management',
        'description' => 'Manage your teams and projects.',
        'author'      => 'RAFIE Younes',
        'icon'        => 'icon-leaf'
    ];
}

创建数据库表 (Creating Database Tables)

Every team has a name, a list of users and projects.

每个团队都有一个名称,用户和项目列表。

php artisan create:model rafie.sitepointdemo Team
// models/team.php

class Team extends Model
{
    // ...
    public $table = 'rafie_sitepointDemo_teams';
    
    public $hasMany = [
        'projects'  => '\Rafie\SitepointDemo\Projects',
        'users'      => '\Backend\Models\User'
    ];
    
    // ...
}
// updates/create_teams_table.php

class CreateTeamsTable extends Migration
{

    public function up()
    {
        Schema::create('rafie_sitepointDemo_teams', function($table)
        {
            $table->engine = 'InnoDB';
            $table->increments('id');
            $table->string('name', 100);
            $table->timestamps();
        });
    }

    public function down()
    {
        Schema::dropIfExists('rafie_sitepointDemo_teams');
    }
}

Every project belongs to a team and has a name, a description and an end date.

每个项目都属于一个团队,并具有名称,描述和结束日期。

php artisan create:model rafie.sitepointdemo Project
// models/project.php

class Project extends Model
{
    
    // ...
    
    public $table = 'rafie_sitepointDemo_projects';
    
    public $belongsTo = [
        'team' => '\Rafie\SitepointDemo\Models\Team'
    ];
    
    // ...
}
// updates/create_projects_table.php

class CreateProjectsTable extends Migration
{

    public function up()
    {
        Schema::create('rafie_sitepointDemo_projects', function($table)
        {
            $table->engine = 'InnoDB';
            $table->increments('id');
            $table->string('name', 100);
            $table->text('description');
            $table->datetime('ends_at');
            $table->integer('team_id')->unsigned();
            $table->timestamps();
        });
    }
    
    // ...
}

Because OctoberCMS already has a users table for the backend, we should add the team_id column. We will create a migration to add our team index.

因为OctoberCMS已经有一个用于后端的用户表,所以我们应该添加team_id列。 我们将创建一个迁移以添加我们的团队索引。

// updates/add_team_to_users.php

class AddTeamToUsers extends Migration
{
    public function up()
    {
        if(!Schema::hasColumn('backend_users', 'team_id'))
        {
            Schema::table('backend_users', function($table)
            {
                $table->integer('team_id')->unsigned()->index()->nullable();
            });
        }
    }

    public function down()
    {
        if(Schema::hasColumn('backend_users', 'team_id'))
        {
            Schema::table('backend_users', function($table)
            {
                $table->dropColumn('team_id');
            });
        }
    }
}

Then, we need to link to it with a new relationship definition.

然后,我们需要使用新的关系定义链接到它。

// Plugin.php

class Plugin extends PluginBase
{
    // ...
    public function boot()
    {
        User::extend(function($model){
            $model->belongsTo['team'] = ['Rafie\SitepointDemo\Models\Team'];
        });
    }
}

Our plugin version file looks like this:

我们的插件版本文件如下所示:

// updates/version.yaml

1.0.1: 
    - First version of Sitepoint demo
    - add_team_to_users.php
    - create_teams_table.php
    - create_projects_table.php

管理团队 (Managing Teams)

Inside your models folder, you can see that every model class has a configuration folder which contains two files:

在您的models文件夹中,您可以看到每个模型类都有一个配置文件夹,其中包含两个文件:

  • columns.yaml: Holds the table columns that you want to use when listing table records.

    columns.yaml :保存列出表记录时要使用的表列。

  • fields.yaml: The same thing as columns but it’s used to configure forms for creating and updating records.

    fields.yaml :与列相同,但用于配置用于创建和更新记录的表单。

To manage teams we need to create a controller to take action on certain events. We use the following scaffolding command.

为了管理团队,我们需要创建一个控制器来对某些事件采取行动。 我们使用以下脚手架命令。

php artisan create:controller rafie.sitepointDemo Teams

If you follow the naming conventions, the controller will automatically map to the model. If you check in your browser at the backend/rafie/sitepointDemo/teams/create URL, you’ll see the new record form.

如果遵循命名约定,则控制器将自动映射到模型。 如果您在backend/rafie/sitepointDemo/teams/create URL backend/rafie/sitepointDemo/teams/create浏览器,则会看到新的记录表单。

Inside config_form.yaml, you’ll see that the form and modelClass properties are mapped to our team model. The new team form only shows a disabled input for the ID. We can add other inputs using the fields.yaml file inside our model.

config_form.yaml内部,您将看到formmodelClass属性映射到我们的团队模型。 新团队表单仅显示ID的禁用输入。 我们可以使用模型中的fields.yaml文件添加其他输入。

// models/team/fields.yaml

fields:
    name:
        label: Name
        type: text
        required: true
    users:
        label: Users
        type: checkboxlist

Every team has a name and a list of users. The name is a simple text value, while the users are listed using the checkboxlist component. You can check the list of field types in the documentation.

每个团队都有一个名称和用户列表。 名称是一个简单的文本值,而用户是使用checkboxlist组件列出的。 您可以在文档中检查字段类型的列表。

You have also the ability to use form widgets in the field types. Widgets are rich components like a WYSWYG editor, Color picker, Media Finder, etc. The only part left here is to create a Team::getUsersOptions method to fill the users checkbox list.

您还可以在字段类型中使用表单窗口小部件 。 小部件是丰富的组件,例如WYSWYG编辑器,颜色选择器,媒体查找器等。这里剩下的唯一部分是创建Team::getUsersOptions方法来填充用户复选框列表。

// models/team.php

class Team extends Model
{
    // ...
    public function getUsersOptions()
    {
        return \Backend\Models\User::lists('login', 'id');
    }
}
Create team

You can see from the screenshot above that the fields marked as required have an asterisk after the input name. However, this does not mean that the validation is handled for you. You still need to add validation inside your models.

您可以从上面的屏幕截图中看到,标记为required字段的输入名称后面带有星号。 但是,这并不意味着将为您处理验证。 您仍然需要在模型中添加验证。

// models/Team.php

class Team extends Model
{
    use \October\Rain\Database\Traits\Validation;

    public $rules = [
        'name' => 'required'
    ];

    // ...
}

The fields in the configuration file are automatically mapped to the model if found. If not, we need to use the create_onSave and update_onSave methods to alter the saving strategy.

如果找到配置文件中的字段,则会自动将其映射到模型。 如果没有,我们需要使用create_onSaveupdate_onSave方法来更改保存策略。

// controllers/teams.php

class Teams extends Controller
{
    // ...
    public function create_onSave()
    {
        $inputs = post('Team');

        // save team
        $teamModel = new \Rafie\SitepointDemo\Models\Team;
        $teamModel->name = $inputs['name'];
        $teamModel->save();

        // update users team_id
        \Backend\Models\User::whereIn('id', $inputs['users'])
                            ->update(['team_id' => $teamModel->id]);

        \Flash::success("Team saved successfully");
        
        return $this->makeRedirect('update', $teamModel);
    }
}

The post method is a helper function to avoid resolving the request object from the container. After saving the team model and updating the user relation we show a success message and create a redirect response using the FormController::makeRedirect method.

post方法是一个辅助函数,可避免从容器中解析请求对象。 保存团队模型并更新用户关系后,我们将显示成功消息并使用FormController::makeRedirect方法创建重定向响应。

// controllers/teams.php

class Teams extends Controller
{
    // ...
    public function update_onSave($recordId)
    {
        $inputs = post('Team');

        // update team
        $teamModel = \Rafie\SitepointDemo\Models\Team::findOrFail($recordId);
        $teamModel->name = $inputs['name'];
        $teamModel->save();

        \Backend\Models\User::where('team_id', $teamModel->id)
                            ->update(['team_id' => 0]);

        // update users team_id
        \Backend\Models\User::whereIn('id', $inputs['users'])
                            ->update(['team_id' => $teamModel->id]);

        \Flash::success("Team updated successfully");
    }
}

The update_onSave method has one parameter containing the updated record ID. We update the team and the attached users accordingly. Another way to accomplish this is to make use of the FormController::update_onSave method. It takes care of mapping form fields to the model and saving it.

update_onSave方法具有一个包含更新的记录ID的参数。 我们会相应地更新团队和附加的用户。 完成此操作的另一种方法是使用FormController::update_onSave方法。 它负责将表单字段映射到模型并将其保存。

// controllers/teams.php

class Teams extends Controller
{
    // ...
    public function update_onSave($recordId)
    {
        $inputs = post('Team');

        \Backend\Models\User::where('team_id', $recordId)
                            ->update(['team_id' => 0]);

        // update users team_id
        \Backend\Models\User::whereIn('id', $inputs['users'])
                            ->update(['team_id' => $recordId]);

        $this->asExtension('FormController')->update_onSave($recordId, $context);
    }
}

The only part left is deleting the records. You may use the update_onDelete method to reset the team_id on the users table and then delete the team.

剩下的唯一部分是删除记录。 您可以使用update_onDelete方法重置用户表上的team_id ,然后删除该团队。

// controllers/teams.php

class Teams extends Controller
{
    // ...
    public function update_onDelete($recordId)
    {
        $teamModel = \Rafie\SitepointDemo\Models\Team::findOrFail($recordId);
        \Backend\Models\User::where('team_id', $teamModel->id)
                            ->update(['team_id' => 0]);
        $teamModel->delete();
        \Flash::success("Team deleted successfully");

        return $this->makeRedirect('delete', $teamModel);
    }
}

Or you could just use formAfterDelete to reset the team_id.

或者,您可以只使用formAfterDelete重置team_id

// controllers/teams.php

class Teams extends Controller
{
    // ...
    public function formAfterDelete($model)
    {
        \Backend\Models\User::where('team_id', $model->id)
                            ->update(['team_id' => 0]);
    }
}

If you noticed, the update form doesn’t automatically select the users attached to the team. We may have to do it manually using the formExtendFields method inside the Teams controller. The getContext method returns whether the user is creating or updating the model.

如果您注意到了,更新表单不会自动选择团队中附属的用户。 我们可能必须使用Teams控制器内的formExtendFields方法手动进行操作。 getContext方法返回用户是创建还是更新模型。

// controllers/teams.php

class Teams extends Controller
{
    // ...
    public function formExtendFields($form)
    {
        if( $form->getContext() === 'update')
        {
            $team = $form->model;
            $userField = $form->getField('users');
            $userField->value = $team->users->lists('id');
        }
    }
}

管理项目 (Managing Projects)

We follow the same steps for managing projects: we start by defining the form fields.

我们执行与管理项目相同的步骤:首先定义表单字段。

models/project/fields.yaml

fields:
    name:
        label: Name
        type: text
        required: true
    description:
        label: Description
        type: textarea
        required: true
    ends_at:
        label: Ends At
        type: datepicker
        required: true
    team_id:
        label: Team
        type: dropdown

We define the list of teams to be displayed on the team dropdown.

我们定义要在团队下拉列表中显示的团队列表。

// models/project.php

class Project extends Model
{
    // ...
    public function getTeamIdOptions()
    {
        $teams = \Rafie\SitepointDemo\Models\Team::all(['id', 'name']);
        $teamsOptions = [];

        $teams->each(function($team) use (&$teamsOptions) {
            $teamsOptions[$team->id] = $team->name;
        });

        return $teamsOptions;
    }
}
Create Project

Because all form fields are mapped to the model, we won’t have to hook into the saving process to update some relations. The create, update and delete actions are handled automatically in this case.

由于所有表单字段均已映射到模型,因此我们无需参与保存过程即可更新某些关系。 在这种情况下,将自动处理createupdatedelete动作。

清单 (Listing)

OctoberCMS makes listing records very simple and extendable. You can show and hide columns, search, sort, filter, format column values, etc. Check the documentation for the full list of options.

OctoberCMS使清单记录非常简单且可扩展。 您可以显示和隐藏列,搜索,排序,过滤,格式化列值等。请查阅文档以获取选项的完整列表。

上市团队 (Listing Teams)

The controllers/teams/config_list.yaml file contains our listing options. Every property has a comment describing its usage.

controllers/teams/config_list.yaml文件包含我们的列表选项。 每个属性都有描述其用法的注释。

// controllers/teams/config_list.yaml

# Model List Column configuration
list: $/rafie/sitepointdemo/models/team/columns.yaml

# Model Class name
modelClass: Rafie\SitepointDemo\Models\Team

# List Title
title: Manage Teams

# Link URL for each record
recordUrl: rafie/sitepointdemo/teams/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

//...

You can see that the list property points to the columns.yaml file, which defines the columns that should be displayed. The showSetup option lets the user select what columns to show in the list.

您可以看到list属性指向columns.yaml文件,该文件定义了应显示的列。 使用showSetup选项,用户可以选择要在列表中显示的列。

List Setup
// models/team/columns.yaml

columns:
    id:
        label: ID
        searchable: true
    name:
        label: Name
    users:
        label: Users
        relation: users
        select: login
        searchable: false

The id and name properties are self-explanatory. The searchable property is set to true by default, that’s why we didn’t specify it on the name property.

idname属性是不言自明的。 默认将searchable属性设置为true ,这就是为什么我们没有在name属性上指定它的原因。

OctoberCMS has a relation property which can be used to show values from related models. In this case, we select the login attribute from the users relation model. You can check the documentation for the full list of available column options.

OctoberCMS具有一个relation属性,可用于显示相关模型的值。 在这种情况下,我们从users关系模型中选择login属性。 您可以查看文档以获取可用列选项的完整列表。

Teams List

上市项目 (Listing Projects)

We follow the same steps for listing projects: we have a name, a description, an end date and a team.

我们采用相同的步骤列出项目:我们有一个名称,描述,结束日期和团队。

// models/project/columns.yaml

columns:
    id:
        label: ID
        searchable: true
    name:
        label: Name
    description:
        label: Description
        type: text
    ends_at:
        label: End At
        type: datetime
    team:
        label: Team
        relation: team
        select: name

To help format the end date properly inside our list, we specify the column type as datetime. This may throw an exception for you because you need to add the ends_at attribute in your model inside the $dates array. Check the documentation for the full list of available column types.

为了帮助在列表中正确设置结束日期的格式,我们将列类型指定为datetime 。 这可能会为您引发异常,因为您需要在$dates数组内的模型中添加ends_at属性。 查看文档以获取可用列类型的完整列表。

// models/project.php

class Project extends Model
{
    // ...
    protected $dates = ['ends_at'];
    // ...
}

As for the team column, we specify the relation type and select the name attribute from the model. The final result looks like this.

对于团队列,我们​​指定关系类型并从模型中选择名称属性。 最终结果如下所示。

Projects listing

扩展清单 (Extending Lists)

If you want to alter the list behavior, you may override the index method inside your controller and the index.htm view. What we want to do now is truncate the description column. You can check the documentation for more details about extending the list behavior.

如果要更改列表行为,则可以在控制器和index.htm视图中覆盖index方法。 我们现在要做的是截断描述列。 您可以查看文档以获取有关扩展列表行为的更多详细信息。

// controllers/projects.php

class Projects extends Controller
{
    // ...
    public function listOverrideColumnValue($record, $columnName)
    {
        if( $columnName == "description" && strlen($record->description) > 20 )
        {
            $description = substr($record->description, 0, 20);

            return "<span title='{$record->description}'>{$description}...</span>";
        }
    }
}

You may be thinking about extending the users listing to show their current team. We use the boot method inside our plugin definition file to extend other plugins.

您可能正在考虑扩展用户列表以显示其当前团队。 我们在插件定义文件中使用boot方法来扩展其他插件。

// Plugin.php

class Plugin extends PluginBase
{
    // ...
    public function boot()
    {
        // ...
        
        \Backend\Controllers\Users::extendListColumns(function ($list) {
            $list->addColumns([
                'team' => [
                    'label' => 'Team',
                    'relation' => 'team',
                    'select' => 'name'
                ]
            ]);
        });
    }
}
Listing Users

筛选器 (Filters)

Filtering lists in OctoberCMS is easy. First, you reference your filter configuration file inside your config_list.yaml file.

在OctoberCMS中过滤列表很容易。 首先,您在config_list.yaml文件中引用过滤器配置文件。

// controllers/projects/config_list.yaml

// ...
filter: config_filter.yaml
// ...

Inside your config_filter.yaml file, you define a list of scopes that you want to use.

config_filter.yaml文件中,您定义要使用的作用域列表。

// controllers/projects/config_filter.yaml

scopes:
    team:
        label: Team
        modelClass: \Rafie\SitepointDemo\Models\Team
        nameFrom: name
        conditions: team_id = :filtered

Our scope is named team and will list our available teams using the specified modelClass and nameFrom properties. The conditions will filter projects where the team_id is equal to the selected teams; you may think of it as a raw SQL where statement. The screenshot below shows the list of projects taken by the Backend team.

我们的范围命名为team ,并将使用指定的modelClassnameFrom属性列出可用的团队。 条件将过滤team_id等于所选团队的项目; 您可能会将其视为原始SQL where语句。 下面的屏幕快照显示了Backend团队采取的项目列表。

Team filtered projects

OctoberCMS has two scope types. The first one is the group type, and it’s the one we used previously. The second is the checkbox type, which is used for boolean situations. We’ll use the latter to hide the past due projects from the list.

OctoberCMS具有两种范围类型。 第一个是组类型,这是我们之前使用的类型。 第二种是复选框类型,用于布尔情况。 我们将使用后者从列表中隐藏过期的项目。

// controllers/projects/config_filter.yaml

scopes:
    // ...
    hide_past_due:
        label: Hide past due
        type: checkbox
        conditions: ends_at > now()

The only past due project in our list is the one with an id of 2.

我们列表中唯一过期的项目是ID为2

Past due filtered projects

权限 (Permissions)

We can’t talk about CRUD operations without covering permissions and how to guard your controllers. Combined with the simplicity of Laravel, OctoberCMS lets you define a list of permissions for your plugin and group them under a specific tab, then use them to guard your controllers.

如果不涉及权限以及如何保护您的控制器,我们不能谈论CRUD操作。 结合Laravel的简单性,OctumCMS使您可以定义插件的权限列表,并将其分组在特定选项卡下,然后使用它们来保护您的控制器。

// Plugin.php

class Plugin extends PluginBase
{
    // ...
    public function registerPermissions()
    {
        return [
            'rafie.sitepointDemo.manage_teams' => [
                'label' => 'Manage Teams',
                'tab' => 'SitepointDemo'
            ],
            'rafie.sitepointDemo.manage_projects' => [
                'label' => 'Manage Projects',
                'tab' => 'SitepointDemo'
            ]
        ];
    }
}

If you visit the update or create user page and select the permissions tab, you’ll see the SitepointDemo tab which holds the plugin permissions.

如果您访问更新或创建用户页面并选择权限选项卡,则将看到包含插件权限的SitepointDemo选项卡。

User Permissions

Now, inside the projects and teams controllers you add the $requiredPermission attribute.

现在,在项目和团队控制器内部,添加$requiredPermission属性。

// controllers/teams.php

class Teams extends Controller
{
    // ...
    public $requiredPermissions = ['rafie.sitepointDemo.manage_teams'];
    // ...
}
// controllers/projects.php

class Projects extends Controller
{
    // ...
    public $requiredPermissions = ['rafie.sitepointDemo.manage_projects'];
    // ...
}

If you want to restrict access to a certain action inside a controller, you may use the User::hasAccess and User::hasPermissions methods. Check the documentation for more details about permissions.

如果要限制对控制器内某个动作的访问,则可以使用User::hasAccessUser::hasPermissions方法。 查看文档以获取有关权限的更多详细信息。

// controllers/teams.php

class Teams extends Controller
{
    // ...
    public function update()
    {
        if( !$this->user->hasPermissions(['rafie.sitepointDemo.update_teams']) )
        {
            // redirect Unauthorized 401
        }
    }
}

结论 (Conclusion)

Every CMS tries to make CRUD operations easier and more straightforward for newcomers, and I think that OctoberCMS achieved that goal successfully by making every aspect of it clear and extendable.

每个CMS都会尝试使CRUD操作对于新来者来说更加容易和直接,我认为OctoberCMS通过使它的各个方面都清晰可扩展而成功实现了该目标。

You can check the final result on GitHub and if you have any questions or opinions let me know in the comments!

您可以在GitHub上查看最终结果,如果有任何疑问或意见,请在评论中让我知道!

翻译自: https://www.sitepoint.com/octobercms-crud-building-a-teamproject-management-plugin/

crud插件

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
These dependencies were not found: * @/api/second/category/industry in ./node_modules/cache-loader/dist/cjs.js??ref--12-0!./node_modules/babel-loader/lib!./node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/vue-loader/lib??vue-loader-options!./src/views/trivoltine/std_base/editStructure.vue?vue&type=script&lang=js& * @/api/second/structure/crud in ./node_modules/cache-loader/dist/cjs.js??ref--12-0!./node_modules/babel-loader/lib!./node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/vue-loader/lib??vue-loader-options!./src/views/trivoltine/std_base/seeStructure.vue?vue&type=script&lang=js& * @/components/tinymce-editor/tinymce-editor.vue in ./node_modules/cache-loader/dist/cjs.js??ref--12-0!./node_modules/babel-loader/lib!./node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/vue-loader/lib??vue-loader-options!./src/views/trivoltine/std_base/editStructure.vue?vue&type=script&lang=js& * vue-pdf in ./node_modules/cache-loader/dist/cjs.js??ref--12-0!./node_modules/babel-loader/lib!./node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/vue-loader/lib??vue-loader-options!./src/views/trivoltine/std_base/editStructure.vue?vue&type=script&lang=js& * vue-quill-editor in ./node_modules/cache-loader/dist/cjs.js??ref--12-0!./node_modules/babel-loader/lib!./node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/vue-loader/lib??vue-loader-options!./src/views/trivoltine/std_base/editStructure.vue?vue&type=script&lang=js& To install them, you can run: npm install --save @/api/second/category/industry @/api/second/structure/crud @/components/tinymce-editor/tinymce-editor.vue vue-pdf vue-quill-editor
05-23
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值