thettler/laravel-console-toolkit

此包提供了一些有用的控制台功能,例如参数和选项的属性语法、验证、自动询问和类型转换。

0.2.0 2023-05-07 17:23 UTC

README

Latest Version on Packagist GitHub Tests Action Status GitHub Code Style Action Status PHPStan Total Downloads PHP Version

Header Image

此包使编写可维护且易于表达的Artisan命令更加容易,具有参数/选项类型转换、验证和autoAsk。此外,它允许您使用简单的属性和属性定义您的参数/选项,以获得更好的IDE支持和静态分析。所有这些只需一个特性即可完成。

🤯 特性

所有功能

💜 支持我

访问我的博客https://bitbench.dev或关注我的社交媒体Twitter @bitbench Instagram @bitbench.dev

📦 安装

您可以通过composer安装此包

composer require thettler/laravel-console-toolkit

🔧 使用方法

🗯️ 在您使用此包之前,您应该已经了解Artisan命令。您可以在这里了解有关它们的更多信息。

基本命令

要使用工具包,您只需在您的命令中添加UsesConsoleToolkit特性。

然后将Thettler\LaravelConsoleToolkit\Attributes\ArtisanCommand添加到类中,以指定名称以及其他诸如描述、帮助等事项。

ArtisanCommand需要设置name参数。这将作为您可以通过命令行调用的命令的名称。

<?php
namespace App\Console\Commands;

use Illuminate\Console\Command;
use Thettler\LaravelConsoleToolkit\Concerns\UsesConsoleToolkit;

#[ArtisanCommand(
    name: 'basic',
)]
class BasicCommand extends Command
{
    use UsesConsoleToolkit;
    
    public function handle()
    {
    }
}

可以这样调用它

php artisan basic
传统语法

    
<?php
namespace App\Console\Commands;

use Illuminate\Console\Command;

class BasicCommand extends Command
{
    protected $signature = 'basic';

    public function handle()
    {
    }
}

描述、帮助和隐藏命令

如果您想添加描述、帮助注释或将命令标记为隐藏,您可以在ArtisanCommand属性中指定此操作

#[ArtisanCommand(
    name: 'basic',
    description: 'Some useful description.',
    help: 'Some helpful text.',
    hidden: true
)]
class BasicCommand extends Command
{
    use UsesConsoleToolkit;

    ...
}

我喜欢使用命名参数,以便更易读。

传统语法

...
class BasicCommand extends Command
{
    protected $signature = 'basic';

    protected $description = 'Some useful description.';

    protected $help = 'Some helpful text.';
    
    protected $hidden = true;
    ...
}

定义输入预期

添加参数或选项的基本工作流程始终是添加一个属性,并用属性对其进行装饰。#[Option]如果您想有一个选项,以及#[Argument]如果您想有一个参数。属性将通过命令行中的值进行填充,因此您可以在handle()方法中像使用任何普通属性一样使用它。

有关更多详细信息,请参阅以下部分。⬇️

❗ 属性仅在handle()方法内部填充。请注意这一点。

参数

要定义参数,您创建一个属性,并将Argument属性添加到它。属性将通过命令行中的值进行填充,因此您可以在handle()方法中像使用任何普通属性一样使用它。

...
use \Thettler\LaravelConsoleToolkit\Attributes\Argument;

#[ArtisanCommand(
    name: 'basic',
)]
class BasicCommand extends Command
{
    use UsesConsoleToolkit;

    #[Argument]
    protected string $myArgument;
    
    public function handle() {
        $this->line($this->myArgument);
    }
}

可以这样调用它

php artisan basic myValue
# Output:
# myValue
传统语法

class BasicCommand extends Command
{
    protected $signature = 'basic {myArgument}';
    
    public function handle() {
        $this->line($this->argument('myArgument'));
    }
}

数组参数

您还可以在参数中使用数组,只需将属性的类型提示为array即可。

#[ArtisanCommand(
    name: 'basic',
)]
class BasicCommand extends Command
{
    use UsesConsoleToolkit;

    #[Argument]
    protected array $myArray;
    
    public function handle() {
        $this->line(implode(', ', $this->myArray));
    }
}

可以这样调用它

php artisan basic Item1 Item2 Item3 
# Output
# Item1, Item2, Item3 
传统语法

class BasicCommand extends Command
{
    protected $signature = 'basic {myArgument*}';
    
    public function handle() {
        $this->line($this->argument('myArgument'));
    }
}

可选参数

当然,您也可以使用可选参数。为了实现这一点,您只需使属性为可空的。

ℹ️ 这也适用于array,但属性将不为空,而是一个空数组

#[ArtisanCommand(
    name: 'basic',
)]
class BasicCommand extends Command
{
    use UsesConsoleToolkit;

    #[Argument]
    protected ?string $myArgument;
    
    ...
}
传统语法

class BasicCommand extends Command
{
    protected $signature = 'basic {myArgument?}';
    
    ...
}

如果您的参数应该有一个默认值,您可以将一个值分配给属性,该值将用作默认值。

#[ArtisanCommand(
    name: 'basic',
)]
class BasicCommand extends Command
{
    use UsesConsoleToolkit;

    #[Argument]
    protected string $myArgument = 'default';
    
    ...
}
传统语法

class BasicCommand extends Command
{
    protected $signature = 'basic {myArgument=default}';
    
    ...
}

参数描述

您可以在Argument属性上设置参数的描述。

#[ArtisanCommand(
    name: 'basic',
)]
class BasicCommand extends Command
{
    use UsesConsoleToolkit;

    #[Argument(
        description: 'Argument Description'
    )]
    protected string $myArgument;
    
    ...
}
传统语法

...
class BasicCommand extends Command
{
    protected $signature = 'basic {myArgument: Argument Description}';
    
    ...
}

❗❗ 如果您有多个参数,类中的顺序也将是命令行上的顺序

选项

要在命令中使用选项,您需要使用 Options 属性。如果您设置了 boolean 类型的类型提示,那么如果未设置选项,则它将为 false,如果设置了选项,则它将为 true。

use \Thettler\LaravelConsoleToolkit\Attributes\Option;

#[ArtisanCommand(
    name: 'basic',
)]
class BasicCommand extends Command
{
    use UsesConsoleToolkit;

    #[Option]
    protected bool $myOption;
    
    public function handle() {
        dump($this->myOption);
    }
}

可以这样调用它

php artisan basic --myOption
# Output
# true
php artisan basic
# Output
# false
传统语法

class BasicCommand extends Command
{
    protected $signature = 'basic {--myOption}';
    
    public function handle() {
        dump($this->option('myOption'));
    }
}

值选项

如果您使用不同于 bool 的类型提示来指定属性,则可以给选项添加一个值。这将自动将其转换为具有值的选项。如果您的类型提示不是可空的,则选项将具有必需的值。这意味着选项只能与值一起使用。

❌ 不起作用 --myoption ✅ 工作正常 --myoption=myvalue

如果要让值可选,只需使类型可空或将值分配给属性即可。

#[ArtisanCommand(
    name: 'basic',
)]
class BasicCommand extends Command
{
    use UsesConsoleToolkit;

    #[Option]
    protected string $requiredValue; // if the option is used the User must specify a value  
    
    #[Option]
    protected ?string $optionalValue; // The value is optional

    #[Option]
    protected string $defaultValue = 'default'; // The option has a default value

    #[Option]
    protected array $array; // an Array Option 

    #[Option]
    protected array $defaultArray = ['default1', 'default2']; // an Array Option with default
    ...
}

可以这样调用它

php artisan basic --requiredValue=someValue --optionalValue --array=Item1 --array=Item2
传统语法

class BasicCommand extends Command
{
    // requiredValue is not possible
    // defaultArray is not possible
    protected $signature = 'basic {--optionalValue=} {--defaultValue=default} {--array=*}';
   
   ...
}

选项描述

您可以在 Option 属性上设置选项的描述。

#[ArtisanCommand(
    name: 'basic',
)]
class BasicCommand extends Command
{
    use UsesConsoleToolkit;

    #[Option(
        description: 'Option Description'
    )]
    protected bool $option;
    ...
}
传统语法

class BasicCommand extends Command
{
    protected $signature = 'basic {--option: Option Description}';
}

选项快捷键

您可以在 Option 属性上设置选项的快捷键。

⚠️ 注意:快捷键只能有一个字符长。

#[ArtisanCommand(
    name: 'basic',
)]
class BasicCommand extends Command
{
    use UsesConsoleToolkit;

    #[Option(
        shortcut: 'Q'
    )]
    protected bool $option;
    ...
}

可以这样调用它

php artisan basic -Q
传统语法

class BasicCommand extends Command
{
    protected $signature = 'basic {--Q|option}';
}

可取消选项

您可以通过向 Option 属性中添加可取消参数来使选项可取消。现在该选项接受标志(例如,--yell)或其否定(例如,--no-yell)。

#[ArtisanCommand(
    name: 'basic',
)]
class BasicCommand extends Command
{
    use UsesConsoleToolkit;

    #[Option(
        negatable: true
    )]
    protected bool $yell;
    
    public function handle(){
       dump($this->yell); // true if called with --yell
       dump($this->yell); // false if called with --no-yell
    }
}

可以这样调用它

php artisan basic --yell
php artisan basic --no-yell

枚举类型

您还可以将 ArgumentsOptions 类型化为枚举。包会自动将命令行输入转换为类型化的枚举。如果您使用 BackedEnums,则使用 case 的值;如果您有一个非 backed 枚举,则使用 case 的名称。

enum Enum
{
    case A;
    case B;
    case C;
}

enum IntEnum: int
{
    case A = 1;
    case B = 2;
    case C = 3;
}

enum StringEnum: string
{
    case A = 'String A';
    case B = 'String B';
    case C = 'String C';
}
    #[Argument]
    protected Enum $argEnum;

    #[Argument]
    protected StringEnum $argStringEnum;

    #[Argument]
    protected IntEnum $argIntEnum;

    #[Option]
    protected Enum $enum;

    #[Option]
    protected StringEnum $stringEnum;

    #[Option]
    protected IntEnum $intEnum;
php artisan enum B "String B" 2 --enum=B --stringEnum="String B" --intEnum=2

输入别名

默认情况下,用于命令行的输入名称将与属性名称相同。您可以使用 OptionArgument 属性上的 as 参数来更改此名称。这可能在您有冲突的属性名称或希望为您的命令提供更丰富的 API 时很有用。

⚠️ 如果您使用 ->option() 语法,则需要指定别名名称以获取选项。

#[ArtisanCommand(
    name: 'basic',
)]
class BasicCommand extends Command
{
    use UsesConsoleToolkit;

    #[Argument(
        as: 'alternativeArgument'
    )]
    protected string $myArgument;
 
    #[Option(
        as: 'alternativeName'
    )]
    protected bool $myOption;
    
    public function handle(){
       dump($this->myArgument);
       dump($this->myOption);
    }
}

可以这样调用它

php artisan basic something --alternativeName

特殊默认值

如果您想要使用一些具有类型转换的默认对象,您可以使用命令上的 configureDefauls() 方法来设置默认值。

#[ArtisanCommand(
    name: 'basic',
)]
class BasicCommand extends Command
{
    use UsesConsoleToolkit;

    #[Argument]
    protected BandModel $band;

    public function configureDefaults(): void {
        $this->band = BandModel::find('2');
    }
    
    public function handle(){
       dump($this->band); // The Band with id 2
    }
}

类型转换

可以在 ArgumentsOptions 上指定类型转换。您可以选择提供用于转换的类字符串或转换器的实例。这有助于通过构造函数配置转换器。

模型转换

工具包提供开箱即用的 eloquent 模型转换。因此,如果您将类型提示为 eloquent 模型,则工具包会尝试将控制台输入与模型的主键匹配,并从数据库中检索它。

    #[Argument]
    protected BandModel $band;
    
    public function handle(){
        $this->band // Well be an instance of BandModel 
    }

如果您想更改用于将输入与数据库匹配的列、加载关系或仅选择特定列,您可以使用如下所示的手动转换。

    #[Argument(
        cast: new \Thettler\LaravelConsoleToolkit\Casts\ModelCaster(
            findBy: 'name',
            select: ['id', 'name']
            with: ['songs']
        )
    )]
    protected BandModel $band;
    
    public function handle(){
        $this->band // Will be an instance of BandModel 
    }

枚举转换

枚举转换将自动将每个类型化的枚举转换为该枚举。但您也可以手动指定它。

    #[Argument(
        cast: \Thettler\LaravelConsoleToolkit\Casts\EnumCaster::class
    )]
    protected Enum $argEnum;

    #[Option(
        cast: new \Thettler\LaravelConsoleToolkit\Casts\EnumCaster(Enum::class)
    )]
    protected Enum $enum;

数组转换

如果您有一个数组并且希望将其所有值转换为特定类型,您可以使用 ArrayCaster。它期望一个转换器和特定类型。

    #[Argument(
        cast: new \Thettler\LaravelConsoleToolkit\Casts\ArrayCaster(
            caster: \Thettler\LaravelConsoleToolkit\Casts\EnumCaster::class, 
            type: StringEnum::class
        )
    )]
    protected array $enumArray;

    #[Option(
        cast: new \Thettler\LaravelConsoleToolkit\Casts\ArrayCaster(
            caster: \Thettler\LaravelConsoleToolkit\Casts\EnumCaster::class, 
            type: StringEnum::class
        )
    )]
    protected array $enumArray2;

自定义转换

您还可以定义自己的转换。为此,您需要创建一个实现 Caster 接口的类。

让我们看看一个小小的 UserCast,它允许您在命令行上简单地使用用户模型的 id,并自动从数据库中检索正确的用户。

<?php

class UserCast implements Caster
{
    /**
    * This method deals with the conversion from the default value to a value the console understand so only 
    * basic return types are allowed  
    * 
    * @param mixed $value The default value if one is present
    * @param string $type The type is a string representation of the type of the property 
    * @param \ReflectionProperty $property The property reflection itself for more control
    * @return int|float|array|string|bool|null
     */
    public function from(mixed $value, string $type, \ReflectionProperty $property): int|float|array|string|bool|null
    {
        if ($value instanceof Band){
            return $value->getKey();
        }
        
        throw new Exception(self::class . ' can only be used with type '. Band::class)
    }

    /**
     * This method deals with the conversion from console input to property value 
     * 
     * @param  mixed  $value The Value from the command line
     * @param  class-string<Band>  $type The type is a string representation of the type of the property 
     * @param  \ReflectionProperty  $property The property reflection itself for more control
     * @return mixed
     */
    public function to(mixed $value, string $type, \ReflectionProperty $property)
    {
        return $type::find($value);
    }
}

现在您可以在属性上本地使用此转换,或者在 AppServiceProvider 中注册它以进行自动转换,如下所示:

    /** Uses the UserCaster everytime the User class is typehint on an Argument or Option */
    \Thettler\LaravelConsoleToolkit\ConsoleToolkit::addCast(UserCaster::class, User::class);

    /** Uses the UserCaster everytime the User or MasterUser class is typehint on an Argument or Option */
    \Thettler\LaravelConsoleToolkit\ConsoleToolkit::addCast(UserCaster::class, [User::class, MasterUser::class]);

    /** Uses the UserCaster everytime the callable returns true */
    \Thettler\LaravelConsoleToolkit\ConsoleToolkit::addCast(
        UserCaster::class,
        fn (mixed $value, ReflectionProperty $property): bool  =>  is_subclass_of($property->getType()->getName(), User::class);
    );

验证

您还可以使用正常的 Laravel 验证规则来验证输入。

    #[Argument(
        validation: ['max:5']
    )]
    protected string $validated;

如果您需要自定义消息,则需要使用验证对象。

    #[Argument(
        validation: new \Thettler\LaravelConsoleToolkit\Transfers\Validation(
            rules: ['max:5']
            messages: [
                'max' => 'This is way to much!'
            ]   
        )
    )]
    protected string $validated;

自动询问

默认情况下,自动询问是启用的。每次调用命令时,如果输入验证失败或需要但未指定,命令会自动提示用户输入(新的)值。如果类型是枚举,则会提供所有枚举值的选项供用户选择。

如果您想禁用此行为,可以在本地进行

    #[Argument(
        autoAsk: false
    )]
    protected string $dontAsk;

或者在您的AppServiceProvider中全局进行

    \Thettler\LaravelConsoleToolkit\ConsoleToolkit::enableAutoAsk(false);

🤖 测试

composer test

📖 更新日志

请参阅更新日志获取有关最近更改的更多信息。

👼 贡献

请参阅贡献指南获取详细信息。

🔒 安全漏洞

请查阅我们的安全策略了解如何报告安全漏洞。

©️ 致谢

📚 许可证

MIT许可证(MIT)。请参阅许可证文件获取更多信息。