2

I have the following services definition at IntegrationBundle/Resources/config/services.yml:

services:
  event.subscriber.params.extractor:
    class: IntegrationBundle\Middleware\EventSuscriberParamsExtractor

  main.api:
    class: IntegrationBundle\API\MainAPI
    calls:
      - [setContainer, ['@service_container']]
  order.push.api:
    class: IntegrationBundle\API\OrderPushAPI
    calls:
      - [setContainer, ['@service_container']]

  process.account.api:
    class: IntegrationBundle\API\ProcessAccountData
    arguments:
      - '@event.subscriber.params.extractor'
    calls:
      - [setContainer, ['@service_container']]
  ...

I need to access main.api from within a Symfony Command and this is how I am doing it:

namespace IntegrationBundle\Command;

use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
use Symfony\Component\Console\Command\LockableTrait;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputDefinition;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;

class SchneiderAPICommand extends ContainerAwareCommand
{
    use LockableTrait;

    protected function configure()
    {
        $this
            ->setName('api:execute')
            ->setDefinition(
                new InputDefinition([
                    new InputOption('source', '', InputArgument::OPTIONAL, '', 'Wonderware'),
                    new InputOption('object', '', InputArgument::OPTIONAL, '', 'Account,AgreementHistory'),
                    new InputOption('quantity', '', InputArgument::OPTIONAL, '', $this->getContainer()->getParameter('api_items_to_process')
                    ),
                ])
            );
    }

    protected function execute(InputInterface $input, OutputInterface $output)
    {
        // Prevent multiple executions of a console command
        if (!$this->lock()) {
            $output->writeln('The command is already running in another process.');

            return 0;
        }

        $output->writeln($this->getContainer()->get('main.api')->execute(
            $input->getOption('source'),
            str_replace(',', '|', $input->getOption('object')),
            $input->getOption('quantity')
        ));
    }
}

Then I am trying the command as follow:

$ bin/console api:execute --source=Wonderware --object=Account --quantity=50 -vvv  

  [LogicException]                                                               
  The container cannot be retrieved as the application instance is not yet set.  

As you can see is failing and I am not sure what I am missing here. Below is a stack trace (if that help somehow):

Exception trace:
 () at /var/www/html/symfony/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Command/ContainerAwareCommand.php:40
 Symfony\Bundle\FrameworkBundle\Command\ContSchneiderainerAwareCommand->getContainer() at /var/www/html/symfony/src/IntegrationBundle/Command/MainAPICommand.php:38
 IntegrationBundle\Command\MainAPICommand->configure() at /var/www/html/symfony/vendor/symfony/symfony/src/Symfony/Component/Console/Command/Command.php:63
 Symfony\Component\Console\Command\Command->__construct() at n/a:n/a
 ReflectionClass->newInstance() at /var/www/html/symfony/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Bundle/Bundle.php:193
 Symfony\Component\HttpKernel\Bundle\Bundle->registerCommands() at /var/www/html/symfony/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Console/Application.php:135
 Symfony\Bundle\FrameworkBundle\Console\Application->registerCommands() at /var/www/html/symfony/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Console/Application.php:108
 Symfony\Bundle\FrameworkBundle\Console\Application->all() at /var/www/html/symfony/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Console/Application.php:72
 Symfony\Bundle\FrameworkBundle\Console\Application->doRun() at /var/www/html/symfony/vendor/symfony/symfony/src/Symfony/Component/Console/Application.php:124
 Symfony\Component\Console\Application->run() at /var/www/html/symfony/bin/console:28

I have been reading here (I do not want and do not need to define this command as a service), here, here and here but none of them help me.

Can any give me some help here? What I am missing?

ReynierPM
  • 17,594
  • 53
  • 193
  • 363
  • 1
    The problem lies with trying to access the container when constructing the quantity input option. Set a default value of -1 then set the actual value in your execute method. – Cerad May 15 '17 at 16:11
  • And I know you don't want to but you really should define the command as a service and just inject the stuff you need. And there probably is no real reason to be injecting the container in all those other classes. – Cerad May 15 '17 at 16:13
  • @Cerad if I define the command as a service, how do I use it as a command itself? my concern there is: "ok, I define the command as a service and inject the container only on the command, what then"? how should I call that command? – ReynierPM May 15 '17 at 16:20
  • @Cerad Your first comment fix my issue but now I am interested on the other approach, could you elaborate and answer and leave some examples? – ReynierPM May 15 '17 at 16:28

1 Answers1

5

As per my first comment, you cannot call getContainer from configure() as the container has not yet been injected since configure is called directly from the constructor. You will need to pull the parameter in the execute method.

An alternative (and perhaps a bit cleaner) approach is to define the command as a service and inject the services actually needed. http://symfony.com/doc/current/console/commands_as_services.html

For example:

class SchneiderAPICommand extends Command
{
    private $mainAPI;
    private $defaultQuaity;
    public function __construct(MainAPI $mainAPI, $defaultQuantity)
    {
        parent::__construct();
        $this->mainAPI = $mainAPI;
        $this->defaultQuanity = $defaultQuanity;
    }
    protected function configure() {
        ...
        new InputOption('quantity', '', InputArgument::OPTIONAL, '', $this->defaultQuantity),

# services.yml
main.api.command:
    class: MyBundle\Command\SchneiderAPICommand
    tags: [{ name: console.command }] # This makes it a command
    arguments: ['@main.api','%api_items_to_process%']
Cerad
  • 48,157
  • 8
  • 90
  • 92