10000 [Console] Command as service by gnugat · Pull Request #3621 · symfony/symfony-docs · GitHub
[go: up one dir, main page]

Skip to content

[Console] Command as service #3621

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

Merged
merged 9 commits into from
Mar 24, 2014
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Created a new article
  • Loading branch information
Loïc Chardonnet committed Mar 1, 2014
commit e13795114c37b5085bf7c57fb79cce6a6cb57571
118 changes: 118 additions & 0 deletions components/console/commands_as_services.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
.. index::
single: Console; Commands as Services

How to define Commands as Services
Copy link
Member

Choose a reason for hiding this comment

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

How to Define Commands as Services (standard is to capitialize all words except from closed-class wo 8000 rds)

==================================

.. versionadded:: 2.4
Support for registering commands in the service container was added in
Copy link
Member

Choose a reason for hiding this comment

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

[...] was introduced

version 2.4.

By default, Symfony will take a look in the ``Command`` directory of your
Copy link
Member

Choose a reason for hiding this comment

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

... of each bundle ...

bundles and automatically register your commands. For the ones implementing
Copy link
Member

Choose a reason for hiding this comment

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

We have to be careful because we're talking about the ContainerAwareCommand, not the actual ContainerAwareInterface.

If a command extends the ContainerAwareCommand, Symfony...

the ``ContainerAwareCommand`` interface, Symfony will even inject the container.

While making life easier, this default implementation has some drawbacks in some
Copy link
Member

Choose a reason for hiding this comment

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

I would suggest adding this to the previous paragraph

situations:
Copy link
Member

Choose a reason for hiding this comment

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

... this has some limitations ...

Copy link
Member

Choose a reason for hiding this comment

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

And I rephrased each bullet point below so that they all sound like limitations (not things that you will be able to do after registering as as service), since we mention limitations/drawbacks here.


* what if you want your command to be defined elsewhere than in the ``Command``
Copy link
Member

Choose a reason for hiding this comment

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

I prefer to start list items with an uppercase letter

Copy link
Member

Choose a reason for hiding this comment

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

[...] to be located somewhere different from the Command folder [...]

(folder or directory?)

Copy link
Author

Choose a reason for hiding this comment

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

Aren't folder and directory synonyms?

Copy link
Member

Choose a reason for hiding this comment

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

I think so. That question was more a note to myself to make that consistent all over the docs

folder?
* what if you want to register conditionally your command, depending on the
Copy link
Member

Choose a reason for hiding this comment

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

[...] want to conditionally register your command [...]

current environment or on the availability of some dependencies?
* what if you need to access dependencies before the ``setContainer`` is called
Copy link
Member

Choose a reason for hiding this comment

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

setContainer() (configre()` below)

(for example in the ``configure`` method)?
* what if you want to reuse a command many times, but with different
Copy link
Contributor

Choose a reason for hiding this comment

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

better is to remove all the what ifs and just list use cases in which we want to do adhoc things

dependencies or parameters?

To solve those problems, you can register your command as a service by simply
Copy link
Member

Choose a reason for hiding this comment

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

... solve these ...

... as a service and tag it with console.command:

defining it with the ``console.command`` tag:
Copy link
Contributor

Choose a reason for hiding this comment

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

In these cases we tag ...

Copy link
Member

Choose a reason for hiding this comment

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

-1 never use the first person


.. configuration-block::

.. code-block:: yaml

# app/config/config.yml
services:
acme_hello.command.my_command:
class: Acme\HelloBundle\Command\MyCommand
tags:
- { name: console.command }

.. code-block:: xml

<!-- app/config/config.xml -->
<?xml version="1.0" encoding="UTF-8" ?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">

<service id="acme_hello.command.my_command"
Copy link
Member

Choose a reason for hiding this comment

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

missing <services> element (which is the wrapper for all <service> elements)

class="Acme\HelloBundle\Command\MyCommand">
<tag name="console.command" />
</service>
</container>

.. code-block:: php

// app/config/config.php

Copy link
Member

Choose a reason for hiding this comment

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

F438

remove empty line

$container
->register('acme_hello.command.my_command', 'Acme\HelloBundle\Command\MyCommand')
->addTag('console.command')
;

Here are some use cases.
Copy link
Member

Choose a reason for hiding this comment

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

seems like a bit misplaced. The usecases where listed below. I would suggest removing this completely


Use dependencies and parameters in configure
Copy link
Member

Choose a reason for hiding this comment

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

Using Dependencies and Parameters in the configure Method

--------------------------------------------

For example, imagine you want to provide a default value for the ``name``
Copy link
Member

Choose a reason for hiding this comment

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

I would start with "Imagine you want to provide a default [...]"

argument. You could:

* hard code a string and pass it as the 4th argument of ``addArgument``;
Copy link
Member

Choose a reason for hiding this comment

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

hardcode is one word I believe
addArgument()

Copy link
Author

Choose a reason for hiding this comment

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

It looks like many spelling are used: http://en.wikipedia.org/wiki/Hard_coding

* allow the user to set the default value in the configuration;
* retrieve the default value from a service (a repository for example).
Copy link
Member

Choose a reason for hiding this comment

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

[...] ​(e.g. a Repository)


With a ``ContainerAwareCommand`` you wouldn't be able to retrieve the
Copy link
Member

Choose a reason for hiding this comment

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

By extending ContainerAwareCommand, only the first is possible, because you can't
access the container inside the configure() method. Instead, inject any parameter or
service you need into the constructor. For example, suppose you have some NameRepository service that you'll use to get your default value:

configuration parameter, because the ``configure`` method is called in the
command's constructor. The only solution is to inject them through its
Copy link
Member

Choose a reason for hiding this comment

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

we always have a discussion about object's constructor. I would go for just "constructor": "[...] method is called in the constructor."

Copy link
Member

Choose a reason for hiding this comment

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

and its would be the then

constructor:
Copy link
Member

Choose a reason for hiding this comment

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

use double colon (which is the shortcut for .. code-block:: php)


<?php
Copy link
Member

Choose a reason for hiding this comment

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

remove php start tag

// src/Acme/DemoBundle/Command/GreetCommand.php
namespace Acme\DemoBundle\Command;

use Acme\DemoBundle\Entity\NameRepository;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;

class GreetCommand extends Command
{
protected $nameRepository;

public function __construct(NameRepository $nameRepository)
{
$this->nameRepository = $nameRepository;
}

protected function configure()
{
$defaultName = $this->nameRepository->findLastOne();

$this
->setName('demo:greet')
->setDescription('Greet someone')
->addArgument('name', InputArgument::OPTIONAL, 'Who do you want to greet?', $defaultName)
;
}

protected function execute(InputInterface $input, OutputInterface $output)
{
$name = $input->getArgument('name');

$output->writeln($name);
}
}
1 change: 1 addition & 0 deletions components/console/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ Console

introduction
usage
commands_as_services
single_command_tool
events
helpers/index
74 changes: 0 additions & 74 deletions cookbook/console/console_command.rst
Original file line number Diff line number Diff line change
Expand Up @@ -62,80 +62,6 @@ This command will now automatically be available to run:

$ app/console demo:greet Fabien

.. _cookbook-console-dic:

Register Commands in the Service Container
------------------------------------------

.. versionadded:: 2.4
Support for registering commands in the service container was added in
version 2.4.

By default, Symfony will take a look in the ``Command`` directory of your
bundles and automatically register your commands. For the ones implementing
the ``ContainerAwareCommand`` interface, Symfony will even inject the container.

If you want to, you can instead register them as services in the container using
the ``console.command`` tag:

.. configuration-block::

.. code-block:: yaml

# app/config/config.yml
services:
acme_hello.command.my_command:
class: Acme\HelloBundle\Command\MyCommand
tags:
- { name: console.command }

.. code-block:: xml

<!-- app/config/config.xml -->
<?xml version="1.0" encoding="UTF-8" ?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">

<service id="acme_hello.command.my_command"
class="Acme\HelloBundle\Command\MyCommand">
<tag name="console.command" />
</service>
</container>

.. code-block:: php

// app/config/config.php

$container
->register('acme_hello.command.my_command', 'Acme\HelloBundle\Command\MyCommand')
->addTag('console.command')
;

.. tip::

Commands as services can be useful in few situations:

* if you need your commands to be defined somewhere else than in the
``Command`` folder;
* if you need to register the command conditionally (depending on the
environment or presence of some dependencies);
* ou want to reuse the command with a different service or different
configuration, without having to extend the command - you could just
define a second service with the same class;
* if you need to access services or configuration parameters in the
``configure`` method.

For example, imagine you want to provide a default value for the ``name``
option. You could hard code a string and pass it as the 4th argument of
``addArgument``, or you could allow the user to set the default value in the
configuration.

With a ``ContainerAwareCommand`` you wouldn't be able to retrieve the
configuration parameter, because the ``configure`` method is called in the
command's constructor. The only solution is to inject them through its
constructor.

Getting Services from the Service Container
-------------------------------------------

Expand Down
0