Orchid CRUD for Laravel

建议编辑

介绍

Orchid 为编写应用程序提供了独特的体验。但有时,当需要执行简单的 CRUD 操作时,可能显得有些过于复杂。因此,我们创建了一个新包,旨在帮助开发者快速为 Eloquent 模型创建用户界面,支持创建、读取、更新和删除等功能。

您可以使用一个文件描述整个过程。当您需要更多选项时,可以轻松切换到使用平台。所有字段、过滤器和特性都是兼容的。

安装

本手册假设您已经拥有 LaravelOrchid 的副本

您可以使用 Composer 安装该包。在命令行中运行:

$ composer require orchid/crud

这将更新 composer.json 并将包安装到 vendor/ 目录中。

定义资源

资源存储在应用程序的 app/Orchid/Resources 目录中。您可以使用 orchid:resource Artisan 命令生成一个新资源:

php artisan orchid:resource PostResource

资源的最基本属性是其 model 属性。这个属性告诉生成器资源对应哪个 Eloquent 模型:

use App\Models\Post;

/**
 * 资源对应的模型。
 *
 * @var string
 */
public static $model = Post::class;

这些类是完全静态的。由于其声明性质,它们没有任何状态。它们只告诉要做什么,不持有任何数据。因此,如果您添加自定义方法,请确保它们是静态的。

新创建的资源不包含任何内容。别担心,我们很快会为我们的资源添加更多字段。

注册资源

app/Orchid/Resources 目录中的所有资源默认会自动注册。您不需要手动注册它们。但如果需要,例如在创建附加包时,最佳方式是:

use App\Orchid\Resources\UserResource;
use Illuminate\Support\ServiceProvider;
use Orchid\Crud\Arbitrator;

class CrudServiceProvider extends ServiceProvider
{
    /**
     * 启动任何应用程序服务。
     *
     * @return void
     */
    public function boot(Arbitrator $arbitrator)
    {
        $arbitrator->resources([
            UserResource::class,
        ]);
    }
}

模型扩展

Orchid 平台的许多功能依赖于模型自定义。您可以根据目标添加或移除特性。但我们假设您已为模型设置了这些特性:

use Illuminate\Database\Eloquent\Model;
use Orchid\Attachment\Attachable;
use Orchid\Filters\Filterable;
use Orchid\Screen\AsSource;

class Post extends Model
{
    use AsSource, Filterable, Attachable;
}

定义字段

每个资源都包含一个 fields 方法。该方法返回一个字段数组,通常扩展自 Orchid\Screen\Field 类。要向资源添加字段,我们可以将其添加到资源的 fields 方法中。通常,字段可以使用其静态 make 方法创建。该方法接受几个参数;然而,您通常只需传递字段的名称。

use Orchid\Screen\Fields\Input;

/**
 * 获取资源显示的字段。
 *
 * @return array
 */
public function fields(): array
{
    return [
        Input::make('title')
            ->title('Title')
            ->placeholder('Enter title here'),
    ];
}

在生成 CRUD 的包中,您可以使用 Orchid 平台的字段。查看文档站点上的所有可用字段

定义列

每个资源都包含一个 сolumns 方法。要向资源添加列,我们可以将其添加到资源的 column 方法中。通常,列可以使用其静态 make 方法创建。

use Orchid\Screen\TD;

/**
 * 获取资源显示的列。
 *
 * @return TD[]
 */
public function columns(): array
{
    return [
        TD::make('id'),
        TD::make('title'),
    ];
}

CRUD 生成包完全基于表层。您可以在文档页面上阅读更多关于此的信息

定义图例

每个资源都包含一个 legend 方法。它决定了模型在查看时的外观。要添加到资源中,我们可以将其添加到资源的 legend 方法中。通常,列可以使用其静态 make 方法创建。

use Orchid\Screen\Sight;

/**
 * 获取资源显示的视图。
 *
 * @return Sight[]
 */
public function legend(): array
{
    return [
        Sight::make('id'),
        Sight::make('title'),
    ];
}

CRUD 生成包完全基于图例层。您可以在文档页面上阅读更多关于此的信息

定义规则

每个资源都包含一个 rules 方法。在提交创建或更新表单时,可以验证数据,这在 rules 方法中描述:

/**
 * 获取适用于保存/更新的验证规则。
 *
 * @return array
 */
public function rules(Model $model): array
{
    return [
        'slug' => [
            'required',
            Rule::unique(self::$model, 'slug')->ignore($model),
        ],
    ];
}

您可以在 Laravel 的验证页面上了解更多信息。

定义过滤器

每个资源都包含一个 filters 方法。该方法期望您返回一个类名列表,这些类名应被渲染,并在必要时替换为查看的模型。

/**
 * 获取资源可用的过滤器。
 *
 * @return array
 */
public function filters(): array
{
    return [];
}

要创建新过滤器,有一个命令:

php artisan orchid:filter QueryFilter

这将在 app/Http/Filters 文件夹中创建一个类过滤器。要在您自己的资源中使用过滤器,您需要:

public function filters(): array
{
    return [
        QueryFilter::class
    ];
}

我们已经提供了一些准备好的过滤器:

  • Orchid\Crud\Filters\DefaultSorted – 设置默认列排序
  • Orchid\Crud\Filters\WithTrashed – 显示已删除的记录
public function filters(): array
{
    return [
        new DefaultSorted('id', 'desc'),
    ];
}

您可以在 Orchid 的过滤页面上了解更多信息。

导航

如果您不希望资源出现在导航中,可以重写资源类的 displayInNavigation 方法:

/**
 * 获取资源是否应显示在导航中
 *
 * @return bool
 */
public static function displayInNavigation(): bool
{
    return false;
}

预加载

假设您经常需要在字段中访问资源的关系。在这种情况下,最好将关系添加到资源的 with 属性中。此属性指示在检索资源时始终预加载列出的关系。

 /**
 * 获取在执行索引查询时应预加载的关系。
 *
 * @return array
 */
public function with(): array
{
    return ['user'];
}

分页

要定义每页应显示多少结果,请设置 perPage 方法:

/**
 * 获取每页返回的模型数量
 *
 * @return int
 */
public static function perPage(): int
{
    return 30;
}

资源事件

每个资源都有两个处理方法,onSaveonDelete。每当事件执行时,您可以更改或补充逻辑:

use Orchid\Crud\ResourceRequest;
use Illuminate\Database\Eloquent\Model;

/**
 * 创建和更新模型的操作
 *
 * @param ResourceRequest $request
 * @param Model           $model
 */
public function onSave(ResourceRequest $request, Model $model)
{
    $model->forceFill($request->all())->save();
}

/**
 * 删除模型的操作
 *
 * @param Model $model
 *
 * @throws Exception
 */
public function onDelete(Model $model)
{
    $model->delete();
}

权限资源

每个资源都包含一个 permission 方法,该方法应返回用户访问此资源所需的字符串键。默认情况下,所有资源对每个用户都是可用的。

/**
 * 获取资源的权限键。
 *
 * @return string|null
 */
public static function permission(): ?string
{
    return null;
}

对于每个注册的资源,如果方法返回非空值,则会创建一个新权限。

/**
 * 获取资源的权限键。
 *
 * @return string|null
 */
public static function permission(): ?string
{
    return 'private-post-resource';
}

有必要赋予用户管理它的权限。点击左侧栏中的个人资料,进入系统页面,然后进入用户页面,您可以为他们颁发授权或分配角色。之后,它们将显示在左侧菜单中。

操作

可以使用 orchid:action Artisan 命令生成操作:

php artisan orchid:action CustomAction

默认情况下,所有操作都放置在 app/Orchid/Actions 目录中。操作必须由两个方法组成。button 方法定义名称、图标、对话框等。而 handler 方法直接处理模型的操作。

namespace App\Orchid\Actions;

use Illuminate\Support\Collection;
use Orchid\Crud\Action;
use Orchid\Screen\Actions\Button;
use Orchid\Support\Facades\Toast;

class CustomAction extends Action
{
    /**
     * 操作的按钮。
     *
     * @return Button
     */
    public function button(): Button
    {
        return Button::make('Run Custom Action')->icon('bs.fire');
    }

    /**
     * 对给定模型执行操作。
     *
     * @param \Illuminate\Support\Collection $models
     */
    public function handle(Collection $models)
    {
        $models->each(function () {
            // action
        });

        Toast::message('It worked!');
    }
}

handle 方法中,您可以执行完成操作所需的任何任务。

handle 方法始终接收一个模型的 Collection,即使操作仅针对单个模型执行。

一旦定义了操作,您就可以将其附加到资源中。每个资源都包含一个 actions 方法。要将操作附加到资源,您应将其添加到该方法返回的操作数组中:

/**
 * 获取资源可用的操作。
 *
 * @return array
 */
public function actions(): array
{
    return [
        CustomAction::class,
    ];
}

策略

要限制哪些用户可以查看、创建、更新或删除资源,可以利用 Laravel 的授权策略。策略是简单的 PHP 类,用于组织特定模型或资源的授权逻辑。例如,如果您的应用程序是一个博客,您可能有一个 Post 模型和一个对应的 PostPolicy

通常,这些策略将在应用程序的 AuthServiceProvider 中注册。如果 CRUD 检测到为模型注册了策略,它将自动在执行各自的操作之前检查该策略的相关授权方法,例如:

  • viewAny
  • view
  • create
  • update
  • delete
  • restore
  • forceDelete

无需额外配置!因此,例如,要确定哪些用户可以更新 Post 模型,您需要在模型对应的策略类中定义一个 update 方法:

namespace App\Policies;

use App\Models\User;
use App\Models\Post;
use Illuminate\Auth\Access\HandlesAuthorization;

class PostPolicy
{
    use HandlesAuthorization;

    /**
     * 确定用户是否可以更新帖子。
     *
     * @param  User  $user
     * @param  Post  $post
     * @return mixed
     */
    public function update(User $user, Post $post)
    {
        return true;
    }
}

如果策略存在但缺少特定的操作方法,则用户将不被允许执行该操作。因此,如果您定义了策略,请不要忘记定义所有相关的授权方法。

如果您不希望策略影响 CRUD 生成用户,您可能希望在给定策略中授权所有操作。为此,请在策略上定义一个 before 方法。在任何其他策略方法之前,before 方法将被执行,允许您在实际调用预期的策略方法之前授权操作。

namespace App\Policies;

use App\Models\User;
use Illuminate\Auth\Access\HandlesAuthorization;

class PostPolicy
{
    use HandlesAuthorization;

    /**
     * 执行预授权检查。
     *
     * @param  User  $user
     * @param  string  $ability
     * @return void|bool
     */
    public function before(User $user, $ability)
    {
        if ($user->hasAccess('private-post-resource')) {
            return true;
        }
    }
}

防止冲突(流量警察)

如果此选项处于活动状态,将检查模型的最后更改时间,如果在编辑表单打开后进行了更新,将抛出验证错误。默认情况下,流量警察功能是禁用的。

/**
 * 指示是否应检查在查看和更新资源之间的修改。
 *
 * @return  bool
*/
public static function trafficCop(): bool
{
    return false;
}

描述

要在资源的每个页面上显示额外的描述,请使用 description

/**
 * 获取屏幕的描述。
 *
 * @return null|string
 */
public static function description(): ?string
{
    return null;
}

面包屑

在资源的每个页面上都有面包屑。您可以使用以下方法监控消息:

/**
 * 获取列表面包屑的文本。
 *
 * @return string
 */
public static function listBreadcrumbsMessage(): string
{
    return static::label();
}

/**
 * 获取创建面包屑的文本。
 *
 * @return string
 */
public static function createBreadcrumbsMessage(): string
{
    return __('New :resource', ['resource' => static::singularLabel()]);
}

/**
 * 获取编辑面包屑的文本。
 *
 * @return string
 */
public static function editBreadcrumbsMessage(): string
{
    return __('Edit :resource', ['resource' => static::singularLabel()]);
}

本地化

可以通过重写资源类上的 labelsingularLabel 方法来本地化资源名称:

/**
 * 获取资源的可显示标签。
 *
 * @return string
 */
public static function label()
{
    return __('Posts');
}

/**
 * 获取资源的可显示单数标签。
 *
 * @return string
 */
public static function singularLabel()
{
    return __('Post');
}

操作按钮和通知也可以翻译,例如:

/**
 * 获取创建资源按钮的文本。
 *
 * @return string|null
 */
public static function createButtonLabel(): string
{
    return __('Create :resource', [
        'resource' => static::singularLabel()
    ]);
}

/**
 * 获取创建资源通知的文本。
 *
 * @return string
 */
public static function createToastMessage(): string
{
    return __('The :resource was created!', [
        'resource' => static::singularLabel()
    ]);
}

您可以在 Laravel 本地化页面上了解更多信息。

我们的朋友