从 Symfony 命令调用服务失败并出现 "The container cannot be retrieved as the application instance is not yet set." 错误,为什么?
Calling service from Symfony Command fails with "The container cannot be retrieved as the application instance is not yet set." error, why?
我在 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']]
...
我需要从 Symfony 命令中访问 main.api
,我是这样做的:
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')
));
}
}
然后我正在尝试如下命令:
$ 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.
如您所见,我正在失败,我不确定我在这里遗漏了什么。下面是堆栈跟踪(如果有帮助的话):
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
我一直在阅读 here (I do not want and do not need to define this command as a service), here, here and here 但其中 none 对我有帮助。
有人可以帮我吗?我缺少什么?
根据我的第一条评论,您不能从 configure() 调用 getContainer,因为容器尚未注入,因为 configure 是直接从构造函数调用的。您将需要在执行方法中提取参数。
另一种(可能更简洁)方法是将命令定义为服务并注入实际需要的服务。 http://symfony.com/doc/current/console/commands_as_services.html
例如:
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%']
我在 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']]
...
我需要从 Symfony 命令中访问 main.api
,我是这样做的:
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')
));
}
}
然后我正在尝试如下命令:
$ 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.
如您所见,我正在失败,我不确定我在这里遗漏了什么。下面是堆栈跟踪(如果有帮助的话):
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
我一直在阅读 here (I do not want and do not need to define this command as a service), here, here and here 但其中 none 对我有帮助。
有人可以帮我吗?我缺少什么?
根据我的第一条评论,您不能从 configure() 调用 getContainer,因为容器尚未注入,因为 configure 是直接从构造函数调用的。您将需要在执行方法中提取参数。
另一种(可能更简洁)方法是将命令定义为服务并注入实际需要的服务。 http://symfony.com/doc/current/console/commands_as_services.html
例如:
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%']