antonioprimera/laravel-generator-command

一个基于Artisan Command的基础命令,用于轻松从模板创建文件

v3.4 2024-09-22 19:10 UTC

This package is auto-updated.

Last update: 2024-09-22 19:11:59 UTC


README

这是一个非常简单的包,只有一个目标:创建Artisan命令,这些命令基于模板生成文件。

它允许你一次性创建一个文件或多个文件。例如,在创建CRUD控制器时,你可能希望一次性生成控制器、模型、迁移、工厂、种子和所有视图。

Laravel提供的生成命令对于简单的文件来说足够好了,但文档不佳,缺乏一些基本选项,这些选项我在大多数项目中都需要。在为我的包编写了一些生成命令之后,我决定将其提取为一个简单的包。如果你愿意贡献,请克隆仓库并提交一个PR。

安装

通过composer导入

composer require --dev antonioprimera/laravel-generator-command

用法

步骤 1:创建命令

此包附带一个Artisan生成命令,用于在项目中生成一个生成命令(听起来有点递归,确实如此)

php artisan make:generator-command <CommandName>

这将创建一个新命令在您的app/Console/Commands文件夹中。

新的命令类继承自抽象的AntonioPrimera\Artisan\FileGeneratorCommand,并包含一个待办事项列表和一些注释的示例代码,以激发您的灵感并尽快开始。

FileGeneratorCommand扩展了默认的Laravel控制台命令,因此您需要提供一个签名,并可选地提供一个描述。签名应该有一个{name}参数,以便从控制台输入获取目标文件名。如果您想保留模板文件名,可以省略{name}参数(这类似于发布命令)。

例如:

protected $signature = 'model:create-vue-assets {name}';
protected $description = 'Generate the vue assets for the given model.';

步骤 2:在命令中实现recipe方法

在您的生成命令中实现抽象的recipe()方法。此方法应返回一个FileRecipe实例,一个预定义的Recipe实例(所有这些都继承自FileRecipe)或Recipe实例的数组。

例如:

use AntonioPrimera\Artisan\FileRecipe;
use AntonioPrimera\Artisan\FileRecipes\JsRecipe;

protected function recipe(): array
{
    //a simple file recipe (although you could use the LangRecipe class for this)
    $langRecipe = FileRecipe::create()
        ->withStub('path/to/langFile.php.stub')
        ->withTargetFolder(lang_path('en'))
        ->withFileNameTransformer(fn(string $fileName) => strtolower($fileName));
    
    //a predefined recipe for javascript files
    $jsRecipe = (new JsRecipe('path/to/stubFile.js.stub'))
            ->withFileNameTransformer('snake')
            ->withReplace(['GENERATED_ON', now()->format('d.m.Y H:i:s')]);
    
    return [
        'Lang File' => $langRecipe,
        'JS File'   => $jsRecipe,
    ];
}

注意:如果您想对命令有更多控制,您可以自己动手,重写handle()方法并直接使用FileRecipe类。

文件模板流畅接口

在之前版本中,模板是作为关联数组或简单的构造函数调用创建的,但这并不理想,因为很难记住所有可用的属性及其名称。现在,流畅接口允许更可读和灵活的模板定义。只有两个强制属性是模板和目标文件夹。所有其他属性都是可选的。

例如,要创建一个json文件的模板,可以使用以下代码

$recipe = \AntonioPrimera\Artisan\FileRecipe::create()
    ->withStub('path/to/stubFile.json.stub')
    ->withTargetFolder('path/to/target/root')
    ->withFileNameTransformer(fn(string $fileName) => \Illuminate\Support\Str::kebab($fileName));

流畅接口的所有方法都以with开头,并以驼峰命名法表示属性名称,并且都是可链式的。

withStub(string|File|Stub $stub)

这是指向模板文件的路径。如果给定的是绝对路径,则使用给定的路径,不进行任何修改。如果给定的是相对路径,则认为它是一个相对于项目根的路径。

此属性对于任何模板都是强制的。

withTargetFolder(string|Folder $target)

这是生成文件的目标根文件夹。如果这是绝对路径,则使用给定的路径。如果这是相对路径,则认为它是一个相对于项目根的路径。

此属性对于任何模板都是强制的。

注意:这并不是文件最终生成的文件夹,而是目标文件的根文件夹。最终文件夹将由这个目标文件夹(使用此方法设置)和目标文件的相对路径决定,该路径是从命令参数推断出来的。

例如,如果目标文件夹是 Http/Controllers/Site,并且命令将使用参数 SectionControllers/Guest/HeroSection 调用,则最终目标文件夹将是 app/Http/Site/SectionControllers/Guest(文件名将是 HeroSection.php)。

withRootNamespace(string|null $rootNamespace)

此属性仅适用于从具有占位符 DUMMY_NAMESPACE 的占位符生成 PHP 类。最终命名空间将由根命名空间和目标文件的相对路径决定。

默认的 rootNamespace 值为 'App',但在大多数情况下这不是一个相关的设置。也许在未来的版本中,这将从 composer.json 文件自动推断出来,但到目前为止,您应该手动设置。

例如,如果根命名空间是 App\Http\Controllers\Site,并且命令将使用参数 SectionControllers/Guest/HeroSection 调用,则最终命名空间将是 App\Http\Controllers\Site\SectionControllers\Guest

withExtension(string|null $extension)

默认情况下,扩展名是从占位符文件推断出来的。建议占位符文件在所需的目标扩展名之后具有 .stub 扩展名(例如 sampleFile.blade.php.stub)。.stub 扩展名在猜测目标扩展名时将被移除,因此您不需要在配方中提供扩展名。

例如(sampleFile.blade.php.stub)。当猜测目标扩展名时,.stub 扩展名将被移除,因此您不需要在配方中提供扩展名。

如果目标扩展名不应(或无法)从占位符文件推断出来,您可以使用此配方属性来设置它。如果提供了字符串,它将仅作为扩展名附加到目标文件。如果您想生成一个不带任何扩展名的文件,请使用空字符串。

例如 'extension' => 'blade.php';

withScope(string|null $scope)

此属性是可选的,仅对控制台输出有用。当运行命令时,它将在控制台输出中显示,以使用户了解生成文件的范围。

例如 $recipe->withScope('View Component')

withReplace(array $replace)

此可选属性应接收一个关联数组,格式为 placeholder => replace_with 项目。这将为所有占位符替换相应的值。

默认情况下,生成器命令替换 2 个占位符:DUMMY_CLASS 和 DUMMY_NAMESPACE。您可以用自己的值覆盖这两个默认项,但在大多数情况下,这些应该工作得很好。如果您生成一个具有命名空间的类或文件,您应该使用 withRootNamespace(...) 方法。

例如 $replace = ['GENERATED_BY' => 'Antonio Primera', 'GENERATED_ON' => now()->format('d.m.Y H:i:s')]

withDefaultReplacements(array $defaultReplace)

此可选属性应接收一个关联数组,格式为 placeholder => replace_with 项目。这将被用作一组默认替换,可以由使用配方的命令提供的替换覆盖。

如果您想创建可重用的配方,这些配方应该有一些默认替换,但可以被使用配方的命令覆盖,这很有用。

withFileNameTransformer(mixed $transformer)

此属性应包含一个可调用者或来自 Illuminate\Support\Str 辅助类(例如 'kebab' / 'snake' 等)的静态方法名称。可调用者将接收目标文件名作为参数,并应返回所需的文件名。

如果没有提供,则此属性为 null,并且不会以任何方式更改文件名。

例如,如果您想生成一个迁移文件(不使用预定义的迁移配方),您可以使用以下转换器

$fileNameTransformer = fn(string $fileName) => date('Y_m_d_His') . '_' . \Illuminate\Support\Str::snake($fileName);

withRelativePathTransformer(mixed $transformer)

此属性是可选的,应包含一个可调用者,它接收目标文件的相对路径作为参数,并返回所需的相对路径。如果您想根据目标文件名更改目标文件夹结构,这将很有用。

例如,如果您想生成一个刀片文件,您可能希望将目标路径的相对路径转换为短横线命名法,以便使用短横线命名的视图和路径在点符号中定位视图。

注意:相对路径是从命令参数中推断出的路径,例如,如果命令参数是 SiteComponents/Sections/HeroSection,则相对路径将是 SiteComponents/Sections,目标文件名将是 HeroSection

//this will transform the relative path to the target file into kebab case
$relativePathTransformer = fn(string $relativePath) => array_map('Str::kebab', \AntonioPrimera\FileSystem\OS::pathParts($relativePath));

/**
 * For a command argument 'SiteComponents/Sections/HeroSection', the above transformer will return 'site-components/sections'.
 * The OS::pathParts method belongs to the 'antonioprimera/filesystem' package, which is a dependency of this package.
 */

钩子

beforeFileCreation()

您可以在您的命令中覆盖此钩子,在根据配方创建配方后,但在生成文件之前运行任何代码。

afterFileCreation()

您可以在您的命令中覆盖此钩子,在所有文件成功生成后运行任何代码。钩子接收一个标志,指示命令是否在测试模式(Dry Run)下运行,所有生成文件的绝对路径列表以及用于生成文件的配方。

cleanupAfterError()

如果文件生成过程中发生错误,此钩子将运行,默认情况下将删除错误发生之前(抛出异常)由命令生成的所有文件。

您可以在您的命令中覆盖此钩子,以在文件生成过程中抛出异常时运行任何代码。

高级用法

如果我想让另一个命令参数持有目标文件名呢?

如果您出于任何原因想使用另一个命令参数来保存目标文件名,您可以覆盖命令的默认 protected string $nameArgument = 'name'; 属性(但在大多数情况下不应这样做)。

升级指南

从 2.* 到 3.0

有什么变化

  • 配方类获得了流畅接口,这使得配方定义更加灵活和易读。只需确保每个配方都有一个存根和目标文件夹属性。所有其他属性都是可选的。
//instead of providing all the recipe attributes, you can now chain the setters
$recipe = \AntonioPrimera\Artisan\FileRecipe::create()
    ->withStub('path/to/stubFile.json.stub')
    ->withTargetFolder('path/to/target/root')
    ->withFileNameTransformer(fn(string $fileName) => strtolower($fileName));
  • 现在为Laravel项目中最常见的文件提供了预构建的配方
    • BladeRecipe 用于 blade 文件
    • CommandRecipe 用于 artisan 命令
    • ConfigRecipe 用于配置文件
    • ControllerRecipe 用于控制器
    • JsRecipe 用于javascript文件
    • LangRecipe 用于语言文件
    • MigrationRecipe 用于迁移文件
    • ModelRecipe 用于 Eloquent 模型
    • ServiceProviderRecipe 用于服务提供者
    • StyleSheetRecipe 用于 css 文件(css / pcss / sass 等. -只需提供正确命名的存根文件)
    • ViewComponentRecipe 用于视图组件类
//in a generator command, you can define the recipe like this
use AntonioPrimera\Artisan\FileRecipes\MigrationRecipe;

//this will create a migration file recipe, prepending the current timestamp to the file name
protected function recipe(): MigrationRecipe
{
    return (new MigrationRecipe('stubs/migration-file.php.stub'))
        ->withReplace(['GENERATED_ON' => now()->format('d.m.Y H:i:s')]);    //add any additional replace items
}

破坏性变化

  • 所有配方设置器现在都以前缀 "with" 开头(例如,withReplace(...) 而不是 replace(...))