ratiborro/laravel-scopes

通过数据库列在现有文件中为模型生成本地作用域

v1.0.6 2021-03-08 21:25 UTC

This package is auto-updated.

Last update: 2024-09-12 00:00:40 UTC


README

用于通过数据库列最大程度地简化创建模型本地作用域的包。

可以自动根据模型表的现有列生成作用域,可选地排除按名称指定的列(适用于所有或特定模型),可自定义生成作用域名称的数据库数据类型,以及可自定义每个生成作用域的逻辑(如果默认行为不适用)。

安装

安装包

composer require ratiborro/laravel-scopes --dev

用法

使用一个简单的命令为模型创建作用域

php artisan make:scopes ModelName

命令会找到模型文件(位于配置中的基本模型目录内),根据数据库列生成所有未来作用域的名称(考虑重命名和排除列,以及作用域名称生成逻辑),使用模板填充它们,然后将它们导入模型(如果存在现有作用域,则直接在其后导入,否则在模型类末尾导入)。

对于每个列可以创建无限数量的作用域(如果列上有多个作用域,则存在一些命名限制)。

您可以随意更改创建的作用域,而不用担心丢失您的更改,因为包不会影响已创建的作用域。

配置

发布配置

php artisan vendor:publish --tag=laravel-scopes-config
  • 设置模型存储的根目录
  • 自定义作用域名称的生成逻辑
  • 列:排除不必要的列,更改名称,指定最不寻常类型列的默认类型
  • 模板(stubs):设置模板保存路径和类型转换映射(例如,对于bigint类型,使用与int相同的模板生成作用域)

发布Laravel不同对象的模板

php artisan stub:publish

发布创建的作用域模板(用于进一步编辑或创建新模板)

php artisan vendor:publish --tag=laravel-scopes-stubs

示例

例如,有一个模型User,包含列idnameemailis_adminlovely_wordteam_idverified_atcreated_atupdated_at

如果您满意默认行为或已经根据需要进行配置,除了执行make:scopes命令外,无需采取任何行动。但我们是来寻找捷径的吗? :)

首先描述一下我们的需求

  1. 假设我们不希望为列idcreated_atupdated_at生成作用域。

  2. 对于以*_id结尾的外键列,我们希望这样的命名风格,以便清楚地表明这是与其他实体之间的关系:例如,对于列team_id,我们希望有作用域scopeOfTeam

  3. 对于布尔列,我们希望以下命名风格,没有is(如果存在):is_admin => scopeAdmin

  4. 具体对于列name,我们希望看到作用域scopeNamed,因为这样我们更喜欢。

  5. lovely_word名称保留在数据库中是历史原因(以及其他类似名称),因此对于所有以lovely_开头的列,我们希望将作用域中的单词改为favorite (lovely_word => scopeFavoriteWord)

  6. 对于datetime列,我们希望有两个作用域:一个用于精确的日期和时间搜索,另一个仅用于日期搜索。

  7. 对于其他列,我们接受camelCase命名风格:scopeColumnName

现在,为了实现所有这些需求,我们需要执行以下操作

  • 简单地通过在配置中列出这些列来关闭idcreated_atupdated_at列的作用域生成

    'columns' => [
      ...
      'exclude_columns' => ['id', 'created_at', 'updated_at', 'deleted_at'],
      ...
    ],
    
  • 2号和3号案例已经作为默认行为嵌入(作者特权),如果需要,可以更改命名风格。

  • 我们在配置中的字典里指定name

    'columns' => [
      ...
      'dictionary' => [
          'name' => 'named'
      ]
      ...
    ],
    
  • 为了编写自定义列命名逻辑的lovely_word,我们将创建一个名为CustomScopeNameGenerator的类,该类将扩展Ratiborro\LaravelScopes\Helpers\ScopeNameGenerator类,用于生成作用域名称。由于列类型是字符串,我们将重写getStringScopeName方法,并在其中添加类似以下逻辑:

      if (Str::startsWith($name, 'lovely_')) {
          return 'favorite_' . Str::after($name, 'lovely_');
      }
    

    同时,别忘了在配置中指定我们的类

      'scopes' => [
          'nameGeneratorClass' => \App\YourPath\ScopeNameGenerator::class
      ],
    
  • 为了为datetime列创建2个作用域,我们需要

    • 创建一个名为datetime.stub的模板。默认情况下没有这样的模板,因为对于datedatetime列的作用域是使用一个模板生成的,并且在配置中指定了从datetime映射到date的映射。您可以简单地复制date.stub模板并将其保存为datetime.stub,然后从配置中删除'datetime' => 'date',映射,以便根据列类型名称使用模板。
    • 修改模板中的逻辑,例如如下所示

            public function scope{{$name}}($q, $value)
            {
                return $q->where('{{$column}}', $value);
            }
            
            public function scope{{$name}}Date($q, $value)
            {
                return $q->whereDate('{{$column}}', $value);
            }
      

      其中$name是作用域名称,由作用域名称生成器创建,$column是原始列名

      故意没有指定值类型,以便可以发送日期的字符串,例如,也可以发送Carbon对象。

      这将生成名为scopeVerifiedAtscopeVerifiedAtDate的作用域。命名限制只是名称的共同部分,它可以是什么都可以,一切只是取决于您的想象力。

  • 对于其他列,我们不进行任何特殊操作,这将为我们创建“标准”作用域,无需任何技巧

最终,我们将获得以下名称的作用域:scopeNamedscopeEmailscopeAdminscopeFavoriteWordscopeOfTeamscopeVerifiedAtscopeVerifiedAtDate,在这些作用域中描述了所需的逻辑(从模板中)。

故障排除

  1. 错误模型不存在:请确保模型的命名空间与配置中指定的相匹配。

    例如,如果配置中指定了命名空间 App\Models,则您可以通过其名称调用模型:如果模型位于直接在Models命名空间中,则使用php artisan make:scopes User,或者如果模型位于子目录中并且具有不同的命名空间,则使用php artisan make:scopes SubdirectoryName/User

    如果模型位于其他目录中,只需更改配置models.base_namespace

  2. 错误无法获取模型表列:请确保为该模型已存在表(已执行创建表的迁移)。如果错误仍然存在,请通过以下联系方式联系。

联系方式

作者:Ratibor Korobin