Skip to content

[FrameworkBundle] Multiple services on one Command class #19305

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

Closed
wants to merge 7 commits into from

Conversation

SenseException
Copy link
Contributor

Q A
Branch? master
Bug fix? no
New feature? yes
BC breaks? no
Deprecations? no
Tests pass? yes
Fixed tickets #19001
License MIT
Doc PR

Based on my opened issue #19001, I created this PR to introduce the possibility of using multiple command-services with one command class. This should make Commands much more equal to other services in Symfony.

This way, a Command class can be used in more than just one
service without breaking registerCommands of HttpKernel
component.
This way, a Command class can be used in more than just one
service without breaking registerCommands of HttpKernel
component.
…issue_19001

Conflicts:
	src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/AddConsoleCommandPass.php
	src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/AddConsoleCommandPassTest.php
$container->addCompilerPass(new AddConsoleCommandPass());
$container->setParameter('my-command.class', 'Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\Compiler\MyCommand');

$definition1 = new Definition('%my-command.class%');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just put this in a variable, no need to use parameters to define class names

@linaori
Copy link
Contributor

linaori commented Jul 7, 2016

So now that you can register the same command service twice, how will you solve the conflicting name problem?

@ogizanagi
Copy link
Contributor

@iltar: I guess it's up to you to configure a different name according to a constructor argument or whatever.

@linaori
Copy link
Contributor

linaori commented Jul 7, 2016

This feature is closely related to another; Lazy command initialization. Ideally I'd configure the name via the tag.

@SenseException
Copy link
Contributor Author

Yes, Command allows to inject the command's name in the constructor. I think injecting it should be a best practice for a future major Symfony release, but I also like @iltar's idea with using the name in the tag.

@iltar: I'll adapt the test. Is there a link to that other feature you mentioned?

@linaori
Copy link
Contributor

linaori commented Jul 7, 2016

No, but it's come up quite a few times. The current system lets Commands define their own name, which works. However, this means the command has to be initialized. So if you have 20 commands, every single command has to be instantiated in order to get the name.

The problem arises when you start using command as a service (my preference). It means that every single command will be initialized with all their dependencies. Hence there should be some kind of support of defining and retrieving their names without initializing them.

@linaori
Copy link
Contributor

linaori commented Jul 7, 2016

Now that I think of it, Commands could be decorated. In a compiler pass you could do something that would eventually trigger this:

new LazyCommand($container, $commandServiceId, $name);
getName(): $name
run($input, $output) {
    $this->container->get($commandServiceId)->run($input, $output);
}

@@ -37,7 +37,11 @@ public function process(ContainerBuilder $container)
if (!is_subclass_of($class, 'Symfony\\Component\\Console\\Command\\Command')) {
throw new \InvalidArgumentException(sprintf('The service "%s" tagged "console.command" must be a subclass of "Symfony\\Component\\Console\\Command\\Command".', $id));
}
$container->setAlias($serviceId = 'console.command.'.strtolower(str_replace('\\', '_', $class)), $id);
$serviceId = 'console.command.'.strtolower(str_replace('\\', '_', $class));
if ($container->hasAlias($serviceId)) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be replaced by something like this to work for any number of commands.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You prefer to enumerate the alias for services of the same command class? What do we gain by adding another loop into the foreach and using numbers?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The actual proposition doesn't fix the current limitation but only reduce its impact.
For example still won't be able to have 3 commands with the same class.
If it is decided to fix this limitation it should be fully fixed.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It uses the service name in the alias. For working services, the $id has to be unique, which is given in the alias too.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh indeed sorry i thought it was a number... looks good to me then.

Copy link
Contributor

@mcfedr mcfedr Jul 26, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This little section can also be rearraged so that the check for $definition->isPublic() comes first, and avoid making $serviceId
Also, the check for existing alias could be avoided by always making $serviceId with ..._$id - there is no harm in doing this Seem that would break the automatic registration of Commands

@SenseException
Copy link
Contributor Author

It would be nice if Symfony would handle its commands like any other class in a service.

@mcfedr
Copy link
Contributor

mcfedr commented Jul 26, 2016

Are there any work arounds for the moment? I guess I just need to create subclasses for each service.

@mcfedr
Copy link
Contributor

mcfedr commented Jul 26, 2016

Also, I would call this a bug fix not a feature.

@SenseException
Copy link
Contributor Author

@mcfedr Subclasses are my current workaround. I marked the PR as feature because commands as a service were introduced in Symfony 2.4 and we're looking on legacy-code from the time before that. It just didn't grow with the rest.

@SenseException
Copy link
Contributor Author

Ping.

@nicolas-grekas nicolas-grekas added this to the 3.x milestone Dec 6, 2016
@fabpot
Copy link
Member

fabpot commented Mar 22, 2017

👍 Can you rebase to fix conflicts and get rid of the merge commit? Thanks.

@fabpot
Copy link
Member

fabpot commented Mar 22, 2017

I've done the work in #22120

@fabpot fabpot closed this Mar 22, 2017
fabpot added a commit that referenced this pull request Mar 22, 2017
…ss (SenseException)

This PR was squashed before being merged into the 3.3-dev branch (closes #22120).

Discussion
----------

[FrameworkBundle] Multiple services on one Command class

rebased version of #19305

Commits
-------

2b82fcb [FrameworkBundle] Multiple services on one Command class
@nicolas-grekas nicolas-grekas modified the milestones: 3.x, 3.3 Mar 24, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants