marcot89/laravel-bullet

Laravel应用程序的子弹开发

v1.2.1 2019-10-01 02:44 UTC

This package is auto-updated.

Last update: 2024-09-29 05:01:12 UTC


README

Laravel Bullet

Laravel Bullet

Latest Version on Packagist Total Downloads

⚡️ 为Laravel应用程序提供闪电般的CRUD和路由注册

此包让您能够快速地将API Cruds转换为Eloquent资源,您可以使用其基于约定的动态路由注册。如果您不喜欢脚手架,不喜欢对资源进行重复的CRUD操作和路由注册,那么这个包就是您和您的应用程序的理想选择。

目录

安装

您可以通过composer安装此包

composer require marcot89/laravel-bullet

推荐: 此包推荐使用来自 Spatie 团队的 Laravel Query Builder 为索引操作。

基本用法

只需在您的控制器类中扩展 ResourceController

<?php

namespace App\Http\Controllers;

use MarcoT89\Bullet\Controllers\ResourceController;

class UserController extends ResourceController
{
}

完成!现在您已经拥有了控制器类中模型 User 的所有这些CRUD操作

  • index
  • store
  • update
  • destroy
  • show
  • edit
  • forceDelete*
  • restore*

重要: 方法 forceDeleterestore 仅在资源方法使用Laravel的trait SoftDeletes 时显示。

约定: 资源模型是根据约定推断的。如果您有一个名为 PostController 的控制器,它将推断模型 Post。控制器名称的约定如下: [Model]Controller,它 不会 从复数到单数进行解析。因此,如果您定义一个控制器 PostsController,它将尝试解析模型 Posts 而不是 Post。在创建控制器时请记住这一点。

到此为止!这足以向您的控制器添加CRUD操作。但 如何动态定义路由呢? 我们接下来将要看到的。

动态路由

现在您已经创建了一个 UserController,是时候为资源控制器注册路由了,对吗?但如果这些路由可以自动注册怎么办?好的!让我们来做吧!

在您想使用的任何路由组中使用 Bullet::namespace。例如

// routes/web.php
Route::middleware('auth', function () {
    Bullet::namespace('Resources'); // the default namespace is App\Http\Controllers
});

就这样!现在 所有Resources 命名空间下创建的控制器都将自动注册其公共方法。以下是以下路由

如果您想隐藏某些特定的路由或操作,可以使用控制器保护属性 $only$except 来实现

class UserController extends ResourceController
{
    protected $only = ['index', 'show'];
    // or
    protected $except = ['destroy'];
}

这将仅生成预期的路由。

注意: 请记住,$only 属性优先于 $except 属性,并且不能同时使用。

重要: $only 属性将隐藏所有其他操作,包括动态操作。

中间件配置

因为我们使用动态路由,中间件配置是在控制器属性中设置的。以下是一些示例

// You can set only one middleware for all actions
protected $middleware = 'auth';
// or many middlewares
protected $middleware = ['auth', 'verified'];
// or customized for different actions
protected $middleware = [
    'auth',
    'auth:api' => ['except' => 'index'],
    'verified' => ['only' => ['store', 'update', 'destroy']]
];

策略类

如果遵循约定,策略类会自动使用。例如,如果你的控制器模型是User,laravel-bullet将自动尝试注册UserPolicy策略类。但如果策略类不存在,它将跳过授权。

如果你想为控制器定制策略类,你可以在控制器中设置属性$policy。如下所示

class UserController extends ResourceController
{
    protected $policy = \App\Policies\CustomUserPolicy::class;
}

如果你不想将策略类注册到控制器,即使它存在,只需将$policy属性设置为false

提示:策略类会自动注册,你不需要AuthServiceProvider中注册它。

验证和请求

建议在请求类中使用验证。请求会自动通过约定注入。如果你在控制器中有模型User,并且当前动作是store,laravel-bullet将尝试按照以下约定顺序注入请求类

App\Http\Requests\Users\StoreRequest::class
App\Http\Requests\UserStoreRequest::class

如果上述类都不存在,它将注入默认的laravel Illuminate\Http\Request类。

如果你想为特定动作自定义请求类,你可以在控制器中的受保护属性requests中定义它

use App\Http\Requests\MyCustomUserRequest;

class UserController extends ResourceController
{
    protected $requests = [
        'store' => MyCustomUserRequest::class,
    ];
}

现在,请求MyCustomUserRequest类将被注入到store动作中。

老式验证

如果你不喜欢请求类中的验证,或者更喜欢laravel的validate方法,你可以在动作钩子中使用它

class UserController extends ResourceController
{
    protected function beforeStore($request, $attributes)
    {
        return $this->validate($request, [
            'name' => 'required',
            'email' => 'required|email',
            'password' => 'required|min:6|confirmed',
        ]);
    }
}

你可以在下一个主题中找到更多关于动作钩子的信息。

操作钩子

在动作成功或失败时执行一些操作是非常常见的。例如,触发事件、记录日志、调度作业等。所有CRUD动作都有beforeafter钩子动作。

它们非常有用,例如,为用户加密密码

class UserController extends ResourceController
{
    protected function beforeStore($request, $attributes): array
    {
        $attributes['password'] = bcrypt($attributes['password']);

        return $attributes;
    }
}

或者在一个用户创建时触发一个事件

use App\Events\UserCreated;

class UserController extends ResourceController
{
    protected function afterStore($request, $user)
    {
        event(new UserCreated($user));
    }
}

以下是每个动作的声明式动作钩子列表

// Hooks for index action
protected function beforeIndex($request);
protected function afterIndex($request, $builder);

// Hooks for store action
protected function beforeStore($request, $attributes): array; // should return the attributes for the model being stored.
protected function afterStore($request, $model);

// Hooks for update action
protected function beforeUpdate($request, $attributes, $model): array;
protected function afterUpdate($request, $model);

// Hooks for destroy action
protected function beforeDestroy($request, $model);
protected function afterDestroy($request, $model);

// Hooks for show action
protected function beforeShow($request, $model);
protected function afterShow($request, $model);

// Hooks for edit action
protected function beforeEdit($request, $model);
protected function afterEdit($request, $model);

// Hooks for forceDelete action
protected function beforeForceDelete($request, $model);
protected function afterForceDelete($request, $model);

// Hooks for restore action
protected function beforeRestore($request, $model);
protected function afterRestore($request, $model);

API资源(表现层)

要设置自己的API资源,你可以在控制器类中使用受保护的resource属性。例如

use App\Http\Resources\UserResource;

class UserControlle extends ResourceController
{
    protected $resource = UserResource::class;
}

注意:此资源将在所有动作中使用。

详细操作

分页、过滤器和其他魔法

如果你使用推荐的Laravel Query Builder composer包,你应该能够很容易地使用它的所有功能。在下面的示例之前,请阅读它们的文档并熟悉它们的用法。

所有这些属性都对indexshow动作可用

protected $defaultSorts    = null;
protected $allowedFilters  = null;
protected $allowedIncludes = null;
protected $allowedSorts    = null;
protected $allowedFields   = null;
protected $allowedAppends  = null;
protected $defaultPerPage  = 15;
protected $maxPerPage      = 500;
protected $searchable      = true;

警告:这组属性仅与Laravel Query Builder包一起使用。如果没有它,将不会工作

看看它是如何工作的

$defaultSorts

// Sort your records from latest to oldest by default
protected $defaultSorts = '-created_at';

$allowedFilters

protected $allowedFilters = ['name', 'email'];

或者,你可以使用allowedFilters方法来处理更复杂的情况

protected function allowedFilters()
{
    return [
        'name',
        AllowedFilter::exact('id'),
        AllowedFilter::exact('email')->ignore(null),
        AllowedFilter::scope('with_trashed'),
        AllowedFilter::custom('permission', FilterUserPermission::class),
    ];
}

$allowedIncludes

protected $allowedIncludes = ['posts.comments'];

或者,你可以使用allowedIncludes方法来处理更复杂的情况

protected function allowedIncludes()
{
    $includes = ['posts.comments'];

    if (user()->isAdmin()) {
        $includes[] = 'created_by';
        $includes[] = 'logs';
    }

    return $includes;
}

$allowedSorts

protected $allowedSorts = ['id', 'name', 'created_at'];

或者,你可以使用allowedSorts方法来处理更复杂的情况

protected function allowedSorts()
{
    return [
        'id',
        'name',
        'created_at',
        Sort::field('street', 'actual_column_street'),
    ];
}

$allowedFields

protected $allowedFields = ['id', 'name', 'email'];

或者,你可以使用allowedFields方法来处理更复杂的情况

protected function allowedFields()
{
    return [
        'id',
        'name',
        'email',
    ];
}

$allowedAppends

protected $allowedAppends = ['is_admin', 'is_published'];

或者,你可以使用allowedAppends方法来处理更复杂的情况

protected function allowedAppends()
{
    return ['is_admin', 'is_published'];
}

$defaultPerPage

protected $defaultPerPage = 15; // defaults to 15

这可以通过per_pageperPage查询参数传递。

$maxPerPage

protected $maxPerPage = 500; // defaults to 500

但是,per_pageperPage 参数不能超过这个 $maxPerPage 限制。如果你传递 per_page=1000,并且已经定义了它,分页将限制为300。这是对查询的保护。请明智地使用它。

$searchable

protected $searchable = true; // defaults to true

默认情况下,所有索引操作都接受 search 查询参数,如果存在,它将尝试使用模型中的 scopeSearch 范围。如果不存在,它将忽略它。如果你在模型中有搜索范围,但不希望它在索引操作中可用,请将其设置为 false。

操作的自定义查询构建器

在很多情况下,你有一个针对查询的大范围。例如,如果你正在开发一个具有 teams 的多租户应用程序,有时你可能想要列出当前用户的团队中的用户。在这种情况下,你可以自定义控制器的查询。目前,每个操作都有自己的查询方法。

对于索引操作,你应该重写 getQuery 方法,如下所示

protected function getQuery()
{
    return team()->users();
}

以下是查询构建器需要覆盖的完整操作列表

protected function getQuery($request); // for index action
protected function getStoreQuery($request);
protected function getUpdateQuery($request);
protected function getDestroyQuery($request);
protected function getShowQuery($request);
protected function getEditQuery($request);
protected function getForceDeleteQuery($request);
protected function getRestoreQuery($request);

自定义操作

要完全自定义 CRUD 操作,只需按照预期声明它。由于限制原因,所有需要 $id 来查找模型的路由都使用 $id 参数而不是类型化模型对象。由于其声明,不可能注入请求类。例如,如果你想要自定义接收 $id 的更新方法,你应该这样做

public function update($id)
{
    $user = User::findOrFail($id);
    ...
}

现在你可以使用动作方法做任何事情。

高级路由

当使用子弹动态路由时,你不需要在任何一个路由文件中手动编写任何路由。使用子弹路由,控制器中的任何公共方法都成为具有已注册路由的操作。示例

class UserController extends ResourceController
{
    public function reports()
    {
        ...
    }
}

公共 方法将 自动 注册一个路由,如下所示

Route::get('users/reports', 'Resources\UserController@reports')->name('users.reports');

自定义HTTP方法

如果你想要自定义路由的 HTTP 方法,只需用该 HTTP 方法的名称作为前缀。如下所示

public function postReports()
{
    ...
}

现在,公共方法 postReports 将注册一个具有 POST HTTP 方法的路由

Route::post('users/reports', 'Resources\UserController@postReports')->name('users.reports');

提示:请注意,生成的 路由名称'users.reports' 而不是 'users.post-reports'

路由参数和依赖注入

你还可以向操作传递参数,它们将被转换为 URL 参数。

use App\Models\User;
use App\Http\Requests\MyCustomRequest;

class UserController extends ResourceController
{
    public function postReports(MyCustomRequest $request,  User $user, $report, $param1, $param2) // as many as you want.
    {
        ...
    }
}

现在这个公共方法将注册一个路由,如下所示

Route::post('users/reports/{user}/{report}/{param1}/{param2}', 'Resources\UserController@postReports')->name('users.post-reports');

注意:任何类型化的参数都将按照预期正常注入。请求参数将注入,但在路由定义中将被忽略。

从动态路由中排除控制器

有时你必须为你的控制器之一定义非常自定义的路由。为了排除你的控制器从动态路由中,你应该在 namespace 方法中使用 options 参数。如下所示

Bullet::namespace('Api/V1', ['except' => 'InternalController']);

现在这个控制器的路由将不会自动生成。你必须 手动 注册它的路由。

性能和其他提示

仅对操作使用公共方法

由于公共方法会自动注册路由,因此将其他辅助方法使用 protectedprivate 可见性是一种好习惯,以避免生成 垃圾路由

使用 route:cache 命令提高性能

由于 laravel-bullet 为每个控制器类使用了大量的反射和IO,因此对于生产环境,使用 route:cache 命令将显著提高你的请求性能。

HTML和JSON响应

index 动作在 AJAX 请求中响应 JSON,或者将尝试在 resources/views/users/index.blade.php 下返回视图。

变更日志

请参阅 CHANGELOG 了解最近更改的详细信息。

贡献

请参阅 CONTRIBUTING 了解详细信息。

安全

如果您发现任何与安全相关的问题,请通过电子邮件发送至 marcotulio.avila@gmail.com,而不是使用问题跟踪器。

鸣谢

许可

MIT许可证(MIT)。有关更多信息,请参阅许可证文件

Laravel软件包模板

本软件包是使用Laravel Package Boilerplate生成的。