-
-
Notifications
You must be signed in to change notification settings - Fork 9.8k
[Console] Add support for interactive invokable commands with #[Interact] and #[Ask] attributes
#61748
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
b6193b6 to
5871af6
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Very nice feature. I had the idea of adding a Question $question property to the #[Argument] attribute, but #[Ask] provides a better separation of concerns.
6b62f9e to
c70568c
Compare
c70568c to
a91ba83
Compare
cfd0c52 to
fd86d3f
Compare
|
Updates about
It's pending to play with autocompleter values (not a blocker) |
|
Example of question value validation using property set hook: class UserDto
{
#[Argument, Ask('Enter the user email')]
public string $email {
set (?string $email) {
if (null === $email || '' === $email) {
throw new InvalidArgumentException('Email cannot be empty.');
}
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
throw new InvalidArgumentException('Email is not valid.');
}
$this->email = strtolower($email);
}
}
// ...
}I particularly like this approach because it guarantees that your DTO will never contain an invalid value. |
fd86d3f to
0f9fed7
Compare
|
Minor update regarding future
|
|
I believe this is ready for final review, with no remaining tasks on my side. |
0f9fed7 to
6f5e1f7
Compare
chalasr
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
With minor comments. Nice one!
696bd3d to
9113944
Compare
|
Rebased and PR description updated after #61890 |
alexandre-daubois
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice!
how does this interact with the validation of required arguments done when validating the input (after the |
the point is, they are being set back in the input :) (see the new |
#[Interactive] and #[InteractiveQuestion] attributes#[Interact] and #[Ask] attributes
|
As discussed internally, I renamed the new attribute names:
(failures are unrelated btw) |
…ract]` and `#[Ask]` attributes
cb30e6e to
6e837c4
Compare
|
Thank you @yceruto. |
…nges for interactive invokable commands (yceruto) This PR was merged into the 7.4 branch. Discussion ---------- [Console] Update CHANGELOG to reflect attribute name changes for interactive invokable commands | Q | A | ------------- | --- | Branch? | 7.4 | Bug fix? | no | New feature? | no | Deprecations? | no | Issues | - | License | MIT Missed this on #61748 Commits ------- c976917 [Console] Update CHANGELOG to reflect attribute name changes for interactive invokable commands
… (yceruto) This PR was merged into the 7.4 branch. Discussion ---------- [Console] Fine-tuning the interactive `#[Ask]` attribute | Q | A | ------------- | --- | Branch? | 7.4 | Bug fix? | no | New feature? | no | Deprecations? | no | Issues | - | License | MIT Complements #61748, concerning interactive array and boolean arguments. **Definition:** ```php public function __invoke( #[Argument, Ask('Must be unique?')] bool $unique, #[Argument, Ask('Enter the tag (leave blank to finish)')] array $tags, ): int ``` **Input:** ```sh $ bin/console app:add-tags Must be unique? (yes/no) [no]: > yes Enter the tag (leave blank to finish): > tag1 Enter the tag (leave blank to finish): > tag2 Enter the tag (leave blank to finish): > ``` As expected, you'll get a `true` value in `$unique`, and `["tag1", "tag2"]` in `$tags`. Commits ------- 29ec76c [Console] Fine-tuning the interactive Ask attribute
… (yceruto) This PR was merged into the 7.4 branch. Discussion ---------- [Console] Fine-tuning the interactive `#[Ask]` attribute | Q | A | ------------- | --- | Branch? | 7.4 | Bug fix? | no | New feature? | no | Deprecations? | no | Issues | - | License | MIT Complements symfony/symfony#61748, concerning interactive array and boolean arguments. **Definition:** ```php public function __invoke( #[Argument, Ask('Must be unique?')] bool $unique, #[Argument, Ask('Enter the tag (leave blank to finish)')] array $tags, ): int ``` **Input:** ```sh $ bin/console app:add-tags Must be unique? (yes/no) [no]: > yes Enter the tag (leave blank to finish): > tag1 Enter the tag (leave blank to finish): > tag2 Enter the tag (leave blank to finish): > ``` As expected, you'll get a `true` value in `$unique`, and `["tag1", "tag2"]` in `$tags`. Commits ------- 29ec76c80a0 [Console] Fine-tuning the interactive Ask attribute
Hi @yceruto do you still plan working on a |
|
Yes, I already have a draft of the |
The
#[Interact]attribute lets you hook into the command interactive phase without extendingCommand, and unlocks even more flexibility!Characteristics
#[Interact]attribute will be called during interactive modeLogicExceptionis thrown--no-interaction)__invoke()Before:
After (long version):
This PR also adds the
#[Ask]attribute for the most basic use cases. It lets you declare interactive prompts directly on parameters. Symfony will automatically ask the user for missing values during the interactive phase, without needing to implement a custom "interact" method yourself:After (short version):
DTO‑friendly interaction
In more complex commands, the DTO approach (see PR #61478) lets you work directly with the DTO instance and its properties. You've got three ways to do this, so let's start with the simplest and move toward the most flexible:
1) Attribute-driven interactivity
You can also use the
#[Ask]attribute on DTO properties that have the#[Argument]attribute and no default value; if such a property is unset when running the command (e.g. when the linked argument isn't passed), the component automatically triggers a prompt using your defined question:Example run:
This makes the most common interactive cases completely declarative.
2) DTO-driven interactivity
For scenarios that go beyond simple prompts, you can handle interactivity inside the DTO itself. As long as it only concerns the DTO's own properties (and doesn't require external services), you can mark a method with
#[Interact]. Symfony will call it during the interactive phase, giving you access to helpers to implement custom logic:Yes!
#[Ask]and#[Interact]complement each other and are executed in sequence during the interactive phase.3) Service‑aware prompts
For cases where prompts depend on external services or need a broader context, you can declare the
#[Interact]method on the command class itself, giving you full control over the interactive phase:In earlier approaches, you had to set arguments manually with
$input->setArgument(). With DTOs, you can now work directly on typed properties, which makes the code more expressive and less error-prone.All three ways can coexist, and the execution order is:
#[Ask]on__invokeparameters#[Ask]on DTO properties#[Interact]on the DTO class#[Interact]on the command classMore related features will be unveiled later.
Cheers!