-
Notifications
You must be signed in to change notification settings - Fork 11.7k
[9.x] Console: Alternative Attribute Syntax for Artisan Commands for better type support and more. #41131
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
|
Symfony already has an attribute for defining console commands -> symfony/symfony#40234 Can't we (re)use that? |
|
Definitely some good work on this but imho I believe the current mechanism to define the command signature is enough. This would add an awful lot for us to maintain. I'm personally also not a fan of attributes... |
|
@driesvints thank you for the feedback. Is this already a "no" for merging this? :) |
|
@thettler I don't merge PR's, Taylor does. Just adding my opinion. |
…es; Some Refactoring
|
@X-Coder264 Thanks for the Feedback. I didn't knew this Attribute already exist. I had a look but came to the conclusion that we can't simply reuse it. There are two reasons for that:
But i am open for a different opinion here. 😄 🔧 But i did changed some things:
|
|
Thanks for your pull request to Laravel! Unfortunately, I'm going to delay merging this code for now. To preserve our ability to adequately maintain the framework, we need to be very careful regarding the amount of code we include. If possible, please consider releasing your code as a package so that the community can still take advantage of your contributions! If you feel absolutely certain that this code corrects a bug in the framework, please "@" mention me in a follow-up comment with further explanation so that GitHub will send me a notification of your response. |
|
@thettler please make it a package! |
|
FWIW, Symfony 6.1 deprecates the |
|
For everybody who likes the idea i have created a Package with all of the Features i mentioned in the PR: Laravel Console ToolkitIt supports
Feel free to try it out and give me Feedback. |
🔨 What does this PR do?
This PR aims to add an alternative syntax to the artisan command signature by using attributes. It is fully backwards compatible and does not break the existing
$signaturesyntax.👀 How does it look?
#[ArtisanCommand( name: 'basic:command', description: 'Basic Command description!', help: 'Some Help.', )] class BasicCommand extends Command { #[Argument] protected string $myArgument; #[Option] protected bool $myOption; public function handle() { // ... } }➕ What are the benefits?
$signaturebecause you get more help from the IDE and you don't need to remember the custom string syntax.➖ What are the drawbacks?
$signaturesyntax especially for small commands, but of course you could still use the$signaturesyntax for those commands and the attributes for more complex ones.🔮 What can be done in future?
In order to keep this PR as small as possible i only added the base functionality. But i already have a lot of ideas what can be done with this kind of syntax.
Casting
At the moment only Enum Types are casted by default. But it is easily possible to add a caster to an argument or option to directly cast a command input to an object, model ... or something else. This could look like this or it would check if the typed class implements the Castable interface.
Show
Validation
Also the Validator could be added like this:
Show
Auto Ask
It's a good practise to ask the user for a required argument if it was not provided instead of failing the command. This could be the default behaviour or can be turned on like this:
Show
🔬 How does it work?
Basically there are two differed types of attributes. The
ArtisanCommandand theConsoleInput.ArtisanCommand Attribute
This attribute is placed at the class level and defines the
name,description,help, if the command is hidden and aliases for the command.#[ArtisanCommand( name: 'basic:command', description: 'Basic Command description!', help: 'Some Help.', hidden: true, aliases: ['alias:command'], )] class BasicCommand extends Command { // ... }Console Input
Console inputs are written als properties on the command class who get decorated with ether the
Argumentor theOptionattribute.#[Argument] protected string $myArgument; #[Option] protected bool $myOption;Arguments
If there is a non-nullable type the argument will be required. If you want an optional argument or an argument with an default value you ether make the type nullable or add an initial value to the property
#[Argument] protected ?string $myArgument; // Optional argument #[Argument] protected string $myDefaultArgument = 'Your default'; // Optional argument with a default valueIf you need an array argument simply typehint the property as array. If you want it to be optional or with a default value make it nullable or add a default.
#[Argument] protected array $myArgument; // required array argument #[Argument] protected ?array $myOptionalArgument; // optional array argument #[Argument] protected array $myDefaultArgument = ['Item A', 'Item B']; // Optional array argument with a default valueOptions
Options are pretty similar regarding the default and optional syntax. But with one catch. There are two different kinds of options, one without a value (simple boolean flags) and ones with values. The ones with Values are pretty much the same as the arguments. Nullable -> optional, With initial value -> default Value.
#[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 OptionIf you want a boolean flag option simply typehint
booland the value will be false if the option wasn't used and true if it was used:#[Option] protected bool $boolFlag;To use shortcuts on options you can simply specify the shortcut on the
Optionattribute#[Option( shortcut: 'O' )] protected bool $option;If you want to use a negatable options you can simply specify it on the
Optionattribute#[Option( negatable: true )] protected bool $yell;Descriptions
Arguments and options can have a description you can simply specify it on the attribute:
#[Argument( description: 'My fancy argument description' )] protected string $myArgument; #[Option( description: 'My fancy option description' )] protected bool $myOption;Alias
Arguments and options can be aliased to avoid conflicts with other parent properties or to make the api more readable for the user:
#[Argument( as: 'publicArgumentName' )] protected string $internalArgumentName; #[Option( as: 'publicOptionName' )] protected bool $internalOptionName;Enum support
Enums will be automatically be cast back and forth.
#[Argument] public MyEnum $enumArgument; #[Argument] public MyEnum $enumDefaultArgument = MyEnum::Something;❤️ Thank you
Thank you very much for taking the time and reading this. This is my first PR to Laravel so
i am open for feedback and discussion about the syntax or implementation and happily change the PR accordingly.