扩展域

    在本节我们将深入探讨如何扩展 Flarum 的域。

    如果您的扩展引入了一种新的实体 (如标签),或者将新属性添加到了一个现有的实体 (例如 的 is_sticky 属性),那么你需要更新 Flarum 的数据库架构,来存储新的数据。

    可通过迁移(migrations)来实现这一工作;Flarum 中的迁移基于 Laravel 的实现。迁移文件必须放在扩展的 migrations 文件夹下,命名时应包括时间戳和对其所作修改的描述,如下所示:

    此文件应该包括一个扩展自 Flarum\Migrations\Migration 的类,并附有相同的描述。每个迁移类包括两个方法:updownup 方法在你的扩展启用时调用(如果该迁移以前没有进行过),因此你可以在数据库中添加新的表、列、索引等等,亦可执行其他安装操作。down 操作在扩展卸载时被调用,应能够回滚 up 操作所执行的行为。

    迁移可调用 $schema 变量,此为 的一个实例,用来对数据库架构进行调整:

    1. use Illuminate\Database\Schema\Blueprint;
    2. use Flarum\Migrations\Migration;
    3. class CreateTagsTable extends Migration
    4. {
    5. public function up()
    6. {
    7. $this->schema->create('tags', function (Blueprint $table) {
    8. $table->increments('id');
    9. $table->string('name', 100);
    10. $table->string('slug', 100);
    11. $table->text('description')->nullable();
    12. }
    13. public function down()
    14. {
    15. $this->schema->drop('tags');
    16. }
    17. }

    如果需要在不重新启用某个扩展的情况下运行迁移,只需简单地执行升级命令即可:

    1. php flarum upgrade

    为了使用您设置的新数据库结构,你需要创建新的模型(Model)或对现有的模型加以扩展。在 Flarum 中,每个模型都扩展自 Flarum\Core\Model,此亦为 Laravel 的 Eloquent\Model 类的扩展。在此详细了解 Eloquent 模型。

    在 Eloquent 模型中,你无需为定义属性而烦恼。你可以像调用实例的属性一样调用它,比如 $discussion->is_sticky。这一切使得事情变得很简单。

    1. use Flarum\Core\Discussions\Discussion;
    2. use Flarum\Events\ModelDates;
    3. $events->listen(ModelDates::class, function (ModelDates $event) {
    4. if ($event->model instanceof Discussion) {
    5. $event->dates[] = 'stickied_at'; // stickied_at 被作为日期属性
    6. }
    7. });

    验证

    每次保存模型时,其数据将根据使用 Laravel 验证组件以一套规则加以验证。扩展可以修改这些规则,所以您可以在模型属性上执行额外的验证。

    如果您需要对已有的模型定义一个新的关系,可以使用 ModelRelationship 事件。每个未知的方法或属性连接模型时,都将调用此事件。如果你关系的名称与方法的名称匹配,你应该使之返回一个 Eloquent Relation 对象,该对象应使用模型 hasOne, belongsTo, belongsToMany, 或者 。

    1. use Flarum\Core\Discussions\Discussion;
    2. use Flarum\Events\ModelRelationship;
    3. $events->listen(ModelRelationship::class, function (ModelRelationship $event) {
    4. if ($event->model instanceof Discussion && $event->relationship === 'category') {
    5. return $event->model->belongsTo('category');
    6. }
    7. });

    如介绍中所述,Flarum 使用命令总线模式(译者注:请参见本文档。)提供对数据进行修改的方法。为了能够使一个核心实体中的所有新属性/关系保存到数据库,你需要注意会发生什么。

    让我们用一个例子来说明这个过程。当我们对 /discussions/123 入口点进行 PATCH 请求时,将会进行下列过程:

    1. 该操作创建 Flarum\Core\Discussions\Commands\EditDiscussion 命令的新实例,并把请求输入进行封装。
    2. 该行为把命令发送至命令总线,将其传递给适当的命令处理程序: Flarum\Core\Discussions\Commands\EditDiscussionHandler
    3. EditDiscussionHandler 根据输入的命令,对讨论模型进行修改,并保存此讨论。

    在第四步时 DiscussionWillBeSaved 事件会被发送。这是在保存前,扩展用来检查输入、并对模型进行的附加修改的时机。

    1. use Flarum\Events\DiscussionWillBeSaved;
    2. $events->listen(DiscussionWillBeSaved::class, function (DiscussionWillBeSaved $event) {
    3. if (isset($event->data['attributes']['isSticky'])) {
    4. $event->discussion->is_sticky = $event->data['attributes']['isSticky'];
    5. }
    6. });

    Flarum 包括一个功能强大的权限系统。它使得扩展能自行定义操作逻辑,允许或不允许某些用户或组对某些实体执行特定行为。

    模型锁

    当你需要确定是否用户可以在模型上执行操作时,可调用该模型的 方法 (在 Flarum\Core\Support\Locked 中定义) 。当用户被允许就返回 ture,反之则为 false

    1. $allowed = $discussion->can($user, 'sticky');

    扩展可以使用 ModelAllow 事件挂接于此,返回 turefalse 来授予或拒绝用户相关权限。

    组的权限

    Flarum 具有关于组和对应授予的基本权限的映射(可在后台权限页加以修改)。此信息存储在数据库的权限表中,并可以在用户模型上使用 hasPermission() 方法访问:

    1. $granted = $user->hasPermission('discussion.sticky');

    对管理员组的用户而言,此方法总是返回 true(也就是说,管理员有权进行一切事务)。其他情况,它将返回 truefalse,具体取决于用户所在的组和对应权限映射。

    这个系统与模块锁系统相连接,从而使得在被授权的某组中的用户能做规定的行为。默认情况下,在 discussion 模块上检查用户是否能做对应行为时,会检查其所在组的 discussion.{action} 许可。对于 postsusersgroups 上亦如此。

    若要了解如何在后台中添加权限设置,请参阅 章节。

    模块锁仅在检查已取得数据库的模型时有用。为了筛选哪种模式应该最先从数据库中取出 (比如确定哪些讨论/帖子是对用户可见的),Flarum 使用一种机制称为可见范围

    可见范围只是一套能在查询数据库模型时可应用的条件语句。可以使用 whereVisibleTo() 方法应用此范围(该方法在Flarum\Core\Support\VisibleScope 中定义):

    1. // Olny get discussions which are visible to $user
    2. // 仅获取对 $user 用户可见的帖子
    3. $discussions = Discussion::whereVisibleTo($user)->get();

    扩展可以使用 ScopeModelVisibility 事件挂接于此,应用限定条件于查询生成器:

    1. use Flarum\Core\Discussions\Discussion;
    2. use Flarum\Events\ScopeModelVisibility;
    3. $events->listen(ScopeModelVisibility::class, function (ScopeModelVisibility $event) {
    4. if ($event->model instanceof Discussion) {
    5. // Only let the user see discussions which they started
    6. // 用户只能看到自己发起的讨论
    7. $event->query->where('start_user_id', $event->actor->id);
    8. }

    帖子可见范围

    我们使用一个简单的模式来筛选用户可见帖子: