antonioprimera / laravel-generator-command
一个基于Artisan Command的基础命令,用于轻松从模板创建文件
Requires
- php: ^8.2
- antonioprimera/filesystem: ^2.0
- antonioprimera/php-helpers: ^0.1.0
- illuminate/support: >=11.0
Requires (Dev)
- orchestra/testbench: ^9.0
- spatie/ray: ^1.41
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(...))