thettler/laravel-command-attribute-syntax

This package is abandoned and no longer maintained. No replacement package was suggested.

This Package allows the use of PHP Attributes to specify a command signature

2.0.0 2022-02-09 23:20 UTC

This package is auto-updated.

Last update: 2022-11-10 04:30:42 UTC


README

❌❌THIS REPO IS NO LONGER SUPPORTED PLEASE USE https://github.com/thettler/laravel-console-toolkit ❌❌

Laravel Command Attribute Syntax

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

Header Image

This package lets you use PHP Attributes to define your Arguments and Options on Artisan Commands. You do not need to touch the handle() method in existing commands in order to use this package. Supports all Laravel features and more:

  • Negatable Options
  • Required Value Options
  • Enum Types
  • Casting

💜 Support me

Visit my blog on https://bitbench.dev or follow me on Social Media Twitter @bitbench Instagram @bitbench.dev

📦 Installation

You can install the package via composer:

composer require thettler/laravel-command-attribute-syntax

🔧 Usage

🗯️ Before you use this package you should already have an understanding of Artisan Commands. You can read about them here.

A Basic Command

To use the attributes in your commands you first need to replace the default \Illuminate\Console\Command class with Thettler\LaravelCommandAttributeSyntax\Command. Then add the Thettler\LaravelCommandAttributeSyntax\Attributes\CommandAttribute to the class.

The CommandAttribute requires the name parameter to be set. This will be the name of the Command which you can use to call it from the commandline.

<?php
namespace App\Console\Commands;

use Thettler\LaravelCommandAttributeSyntax\Attributes\ArtisanCommand;
use Thettler\LaravelCommandAttributeSyntax\Command;

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

And call it like:

php artisan basic
Traditional Syntax

    
<?php
namespace App\Console\Commands;

use Illuminate\Console\Command;

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

    public function handle()
    {
        return 1;
    }
}

Descriptions, Help and Hidden Commands

If you want to add a description, a help comment or mark the command as hidden, you can specify this on the CommandAttribute like this:

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

I like to use named arguments for a more readable look.

Traditional Syntax

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

    protected $description = 'Some usefull description.';

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

Defining Input Expectations

The basic workflow to add an argument or option is always to add a property and decorate it with an Attribute. #[Option] if you want an option and #[Argument]if you want an argument. The property will be hydrated with the value from the command line, so you can use it like any normal property inside your handle() method. It's also possible to access the arguments and options via the normal laravel methods $this->argument('propertyName') or $this->option('propertyName').

More about that in the following sections. ⬇️

❗ The property will only be hydrated inside of the handle() method. Keep that in mind.

Arguments

To define Arguments you create a property and add the Argument attribute to it. The property will be hydrated with the value from the command line, so you can use it like any normal property inside your handle() method.

use \Thettler\LaravelCommandAttributeSyntax\Attributes\Argument;

#[CommandAttribute(
    name: 'basic',
)]
class BasicCommand extends Command
{
    #[Argument]
    protected string $myArgument;
    
    public function handle() {
        $this->line($this->myArgument);
        $this->line($this->argument('myArgument'));
    }
}

call it like:

php artisan basic myValue
# Output:
# myValue
# myValue
Traditional Syntax

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

Array Arguments

You can also use arrays in arguments, simply typehint the property as array.

#[CommandAttribute(
    name: 'basic',
)]
class BasicCommand extends Command
{
    #[Argument]
    protected array $myArray;
    
    public function handle() {
        $this->line(implode(', ', $this->myArray));
    }
}

Call it like:

php artisan basic Item1 Item2 Item3 
# Output
# Item1, Item2, Item3 
Traditional Syntax

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

Optional Arguments

Of course, you can use optional arguments as well. To achieve this you simply make the property nullable.

ℹ️ This works with array as well but the property won't be null but an empty array instead

#[CommandAttribute(
    name: 'basic',
)]
class BasicCommand extends Command
{
    #[Argument]
    protected ?string $myArgument;
    
    ...
}
Traditional Syntax

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

If your argument should have a default value, you can assign a value to the property which will be used as default value.

#[CommandAttribute(
    name: 'basic',
)]
class BasicCommand extends Command
{
    #[Argument]
    protected string $myArgument = 'default';
    
    ...
}
Traditional Syntax

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

Argument Description

You can set a description for arguments as parameter on the Argument Attribute.

#[CommandAttribute(
    name: 'basic',
)]
class BasicCommand extends Command
{
    #[Argument(
        description: 'Argument Description'
    )]
    protected string $myArgument;
    
    ...
}
Traditional Syntax

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

❗ ❗ If you have more than one argument the order inside the class will also be the order on the commandline

Options

To use options in your commands you use the Options Attribute. If you have set a typehint of boolean it will be false if the option was not set and true if it was set.

use \Thettler\LaravelCommandAttributeSyntax\Attributes\Option;

#[CommandAttribute(
    name: 'basic',
)]
class BasicCommand extends Command
{
    #[Option]
    protected bool $myOption;
    
    public function handle() {
        dump($this->myOption);
        dump($this->option('myOption'));
    }
}

Call it like:

php artisan basic --myOption
# Output
# true
# true
php artisan basic
# Output
# false
# false
Traditional Syntax

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

Value Options

You can add a value to an option if you type hint the property with something different as bool. This will automatically make it to an option with a value. If your typehint is not nullable the option will have a required value. This means the option can only be used with a value.

❌ Wont work --myoption ✅ works --myoption=myvalue

If you want to make the value optional simply make the type nullable or assign a value to the property

#[CommandAttribute(
    name: 'basic',
)]
class BasicCommand extends Command
{
    #[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
    ...
}

Call it like:

php artisan basic --requiredValue=someValue --optionalValue --array=Item1 --array=Item2
Traditional Syntax

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

Option Description

You can set a description for an option on the Option Attribute.

#[CommandAttribute(
    name: 'basic',
)]
class BasicCommand extends Command
{
    #[Option(
        description: 'Option Description'
    )]
    protected bool $option;
    ...
}
Traditional Syntax

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

Option Shortcuts

You can set a shortcut for an option on the Option Attribute.

⚠️ Be aware that a shortcut can only be one char long

#[CommandAttribute(
    name: 'basic',
)]
class BasicCommand extends Command
{
    #[Option(
        shortcut: 'Q'
    )]
    protected bool $option;
    ...
}

Call it like:

php artisan basic -Q
Traditional Syntax

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

Option alias

By default, the option name used on the commandline will be same as the property name. You can change this with the name parameter on the Option Attribute. This can be handy if you have conflicting property names or want a more expressive api for your commands.

⚠️ If you use the ->option() syntax you need to specify the alias name to get the option.

#[CommandAttribute(
    name: 'basic',
)]
class BasicCommand extends Command
{
    #[Option(
        name: 'alternativeName'
    )]
    protected bool $myOption;
    
    public function handle(){
       dump($this->myOption);
       dump($this->option('alternativeName'))
    }
}

Call it like:

php artisan basic --alternativeName

Negatable Options

You can make option Negatable by adding the negatable parameter to the Option Attribute. Now the option accepts either the flag (e.g. --yell) or its negation (e.g. --no-yell).

#[CommandAttribute(
    name: 'basic',
)]
class BasicCommand extends Command
{
    #[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
    }
}

Call it like:

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

Enum Types

It is also possible to type Arguments or Options as Enum. The Package will automatically cast the input from the commandline to the typed Enum. If you use BackedEnums you use the value of the case and if you have a non backed Enum you use the name of the 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

Casts

It's also possible to define your own casts. To do so you need to create a class that implements the CastInterface. The match() method checks if a type can be cast by this cast-class and returns true if it is possible and false if not.

Let's have a look at small UserCast that allows to simply use the id of a user model on the command line and automatically fetch the correct user from the database:

<?php

namespace Thettler\LaravelCommandAttributeSyntax\Casts;

use Thettler\LaravelCommandAttributeSyntax\Contracts\CastInterface;

class UserCast implements CastInterface
{
     public static function match(string $typeName, mixed $value): bool
    {
        return $typeName === User::class;
    }

    public function cast(mixed $value, string $typeName): User
    {
        return User::find($value);
    }
}

To register your cast you need to publish the config file first:

php artisan vendor:publish --tag="command-attribute-syntax-config"

and add your cast to the cast array:

return [
    'casts' => [
            \Thettler\LaravelCommandAttributeSyntax\Casts\UserCast::class
    ]
];

The package goes top to bottom through the array and uses the first cast that returns true from the match() method.

Now finally typehint our Argument (or Option).

#[CommandAttribute(name: 'userName')]
class UserNameCommand extends \Thettler\LaravelCommandAttributeSyntax\Command
{
    #[Argument]
    protected User $user;

    /**
     * Execute the console command.
     *
     * @return int
     */
    public function handle()
    {
        $this->line($this->user->name);
    }
}
php artisan userName 2
 // Some Name

🤖 Testing

composer test

📖 Changelog

Please see CHANGELOG for more information on what has changed recently.

👼 Contributing

Please see CONTRIBUTING for details.

🔒 Security Vulnerabilities

Please review our security policy on how to report security vulnerabilities.

©️ Credits

📚 License

The MIT License (MIT). Please see License File for more information.