Symfony not loading runtime Twig extension - php

I followed the example on Symfony 3.4 docs in order to load a Twig extension at runtime but it doesn't load: what I'am doing wrong?
IN: src/PlotlyBundle/Twig/AppRuntime.php
<?php
namespace PlotlyBundle\Twig;
class AppRuntime
{
public function __construct()
{
}
public function biDraw()
{
return 'awesome text here';
}
}
IN: src/PlotlyBundle/Resources/config/services.yml
services:
plotly.twig_runtime:
class: PlotlyBundle\Twig\AppRuntime
public: true
tags:
- { name: twig.runtime }
IN: src/PlotlyBundle/Twig/AppExtension.php
<?php
namespace PlotlyBundle\Twig;
use PlotlyBundle\Twig\AppRuntime;
class AppExtension extends \Twig_Extension
{
public function getFunctions()
{
return [
new \Twig_SimpleFunction(
'bi_draw',
array(AppRuntime::class, 'biDraw')
),
];
}
}
IN: src/AppBundle/Controller/DashboardController.php
$twig = $this->get('plotly.twig_runtime');
return $this->render(
'dashboard/index.html.twig'
);
IN: app/Resources/views/dashboard/index.html.twig
{{ bi_draw() }}

Thanks to #Federkun comments I fixed it by autowiring the Twig extension:
IN: src/PlotlyBundle/Resources/config/services.yml
services:
# default configuration for services in *this* file
_defaults:
# automatically injects dependencies in your services
autowire: true
# automatically registers your services as commands, event subscribers, etc.
autoconfigure: true
# this means you cannot fetch services directly from the container via $container->get()
# if you need to do this, you can override this setting on individual services
public: false
# this creates a service per class whose id is the fully-qualified class name
PlotlyBundle\Twig\:
resource: '../../../../src/PlotlyBundle/Twig/*'
tags:
- { name: twig.runtime }
The example on the Symfony docs (Creating Lazy-Loaded Twig Extensions) need an update to mention that auto-wiring MUST be enabled (as explained in the autoconfigure Option) in order for the example to work.
I submitted a PR to the Symfony docs.

Related

Command not found

I want to migrate a symfony2.8 api to symfony5.4. I transferred all my Symfony commands, changed containerAwareCommand to \Symfony\Component\Console\Command\Command and changed the command configuration to match the configuration of the new version .
However, my commands are still not visible in the bin/console list and I cannot use them without declaring them in the services.yml. I have more than thirty commands to transfer and adding a paragraph in the service.yml seems to me tedious and not necessary. Did I forget something?
Second question, how can I access the service container directly in the commands? On Symfony 2.8 I was accessing it with $this->getContainer()->get('toto') but it doesn't seem to work anymore on version 5.
services.yml:
tags:
- { name: 'console.command', command: 'word:manager:expiration_email' }
arguments:
- '#service_container'
- '#logger'
Command:
protected function configure()
{
$this
->setName('word:manager:expiration_email')
->setDescription('Alert email : Send an email x days before the expiration.')
->addOption(
'days',
'd',
InputOption::VALUE_REQUIRED,
'How many days before the expiration',
1
);
}
/**
* {#inheritdoc}
*/
protected function execute(InputInterface $input, OutputInterface $output): int
{
$days = intval($input->getOption('days'));
if ($days > 0) {
try {
$this->getManager()->sendExpirationEmail($days);
} catch (\Exception $e) {
$this->container->get('logger')->error(sprintf(
'aemCommand: an error occurred in file "%s" (L%d): "%s"',
$e->getFile(),
$e->getLine(),
$e->getMessage()
));
}
}
return 0;
}
/**
* #return aem
*/
private function getManager()
{
return $this->container->get('word.manager.alert_employee');
}
services.yml:
imports:
- { resource: "#WORDCoreBundle/Resources/config/services.yml" }
- { resource: "#WORDAlertBundle/Resources/config/services.yml" }
- { resource: "#WORDEmployeeBundle/Resources/config/services.yml" }
- { resource: "#WORDTimeManagementBundle/Resources/config/services.yml" }
- { resource: "#WORDUserBundle/Resources/config/services.yml" }
- { resource: "#WORDFileBundle/Resources/config/services.yml" }
- { resource: "#WORDLocalisationBundle/Resources/config/services.yml" }
- { resource: "#WORDPlatformBundle/Resources/config/services.yml" }
- { resource: "#WORDCompanyBundle/Resources/config/services.yml" }
- { resource: "#WORDContractBundle/Resources/config/services.yml" }
- { resource: "#WORDFolderBundle/Resources/config/services.yml" }
- { resource: "#WORDTrainingBundle/Resources/config/services.yml" }
- { resource: "#WORDExpenseReportBundle/Resources/config/services.yml" }
- { resource: "#WORDBookStoreBundle/Resources/config/services.yml" }
- { resource: "#WORDPaymentBundle/Resources/config/services.yml" }
- { resource: "#WORDInvoiceBundle/Resources/config/services.yml" }
- { resource: "#WORDAgendaBundle/Resources/config/services.yml" }
- { resource: "#WORDAlertBundle/Resources/config/services.yml" }
- { resource: "#WORDAnnualReviewBundle/Resources/config/services.yml" }
- { resource: "#WORDProReviewBundle/Resources/config/services.yml" }
- { resource: "#WORDWidgetBundle/Resources/config/services.yml" }
- { resource: "#WORDCommentBundle/Resources/config/services.yml"}
- { resource: "#WORDFrontBundle/Resources/config/services.yml" }
- { resource: "#WORDNotificationBundle/Resources/config/services.yml" }
services:
App\:
resource: '../src/'
exclude:
- '../src/DependencyInjection/'
- '../src/Entity/'
- '../src/Kernel.php'
_defaults:
autowire: true
autoconfigure: true
WORD\CoreBundle\Controller\ApiController:
calls:
- method: setContainer
arguments: ['#service_container']
WORD\PlatformBundle\Controller\PlatformRESTController:
calls:
- method: setContainer
arguments: ['#service_container']
WORD\UserBundle\Controller\UserRESTController:
calls:
- method: setContainer
arguments: ['#service_container']
WORD\AlertBundle\Controller\AlertRESTController:
calls:
- method: setContainer
arguments: ['#service_container']
WORD\EmployeeBundle\Controller\EmployeeRESTController:
calls:
- method: setContainer
arguments: ['#service_container']
WORD\CompanyBundle\Controller\CompanyRESTController:
calls:
- method: setContainer
arguments: ['#service_container']
WORD\CompanyBundle\Controller\ContactRESTController:
calls:
- method: setContainer
arguments: ['#service_container']
WORD\CompanyBundle\Controller\ServiceRESTController:
calls:
- method: setContainer
arguments: ['#service_container']
WORD\CompanyBundle\Controller\ChartRESTController:
calls:
- method: setContainer
arguments: ['#service_container']
WORD\BookStoreBundle\Controller\InfoRESTController:
calls:
- method: setContainer
arguments: ['#service_container']
WORD.user.manager.user:
class: 'WORD\UserBundle\Service\UserManager'
public: false
lazy: true
arguments:
- '#fos_user.util.password_updater'
- '#fos_user.util.canonical_fields_updater'
- '#fos_user.object_manager'
- 'WORD\UserBundle\Entity\User'
WORD\AgendaBundle\DataFixtures\AgendaFixtures:
tags: [doctrine.fixture.orm]
WORD\UserBundle\DataFixtures\UserFixtures:
tags: [doctrine.fixture.orm]
arguments:
$fos_manager: '#fos_user.user_manager'
WORD\LocalisationBundle\DataFixtures\CountryFixtures:
tags: [doctrine.fixture.orm]
App\DataFixtures\Processor\UserProcessor:
tags: [ { name: fidry_alice_data_fixtures.processor } ]
WORD\AlertBundle\Command\AlertExpirationEmailCommand:
tags:
- { name: 'console.command', command: 'WORD:alert:expiration_email' }
arguments:
- '#service_container'
- '#logger'
Composer.json
"autoload": {
"psr-4": {
"App\\": "src/",
"WORD\\": "WORD"
}
},
Namespace PSR-4 mapping
In order for composer to properly map out your files to their associated namespaces, the namespace and path must be declared in the autoload.psr-4 and/or autoload.classmap composer.json configuration.
composer.json autoload mappings
"autoload": {
"psr-4": {
"App\\": "src/",
"W2D\\": "W2D/",
"WORD\\": "WORD/"
},
"classmap": {
"src/",
"W2D/",
"WORD/"
}
},
Convert Commands to be Lazy Loaded
First to reduce configuration and instantiation overhead, make all of your commands lazy loaded by declaring the name using protected static $defaultName = '.....'.
Repeat the changes for each command in the namespace.
Command changes
// /WORD/AlertBundle/Command/AlertExpirationEmailCommand.php
namespace WORD\AlertBundle\Command;
class AlertExpirationEmailCommand extends Command
{
protected static $defaultName = 'WORD:alert:expiration_email';
protected function configure()
{
$this
/* ->setName('WORD:alert:expiration_email') */
// ...
}
}
Fix Service Loading
All services in Symfony 3.4+ are public: false by default (including the Logger) making them inaccessible by using container->get('service'). Additionally if a service is not explicitly injected in a service configuration or by autowire, the services are removed from the Container to reduce overhead. Because of this, you should never inject the entire container into a service - since the service you may be looking for may have inadvertently been removed.
Due to the way services are handled, _defaults should be the first line of any services declaration. It is also important to note, that _defaults will not be inherited/cascaded by any other file or configuration imports.
Since App\ is the default namespace being autowired, you will need to replace it with yours to ensure that the appropriate namespace(s) are being loaded as services for autowire.
Application services autowire
# /config/services.yaml
services:
# default configuration for services in *this* file
_defaults:
autowire: true
autoconfigure: true
# makes classes in App/ available to be used as services
# this creates a service per class whose id is the fully-qualified class name
App\:
resource: '../src/'
exclude:
- '../src/DependencyInjection/'
- '../src/Entity/'
- '../src/Kernel.php'
# makes classes in WORD/ available to be used as services
# this creates a service per class whose id is the fully-qualified class name
WORD\: # <--- root namespace to autowire eg: WORD\Command\MyCommand
resource: '../WORD/' # location of the namespace
exclude:
- '../WORD/DependencyInjection/'
- '../WORD/Entity/'
- '../WORD/*Bundle/' # Never autowire Bundles as they should be self-contained
# declare manually wired and overridden services below this line
# ...
Fix Bundle Configurations to be self-contained
With the introduction of autowiring and changes to order of operations in Symfony Flex (4.0+), loading Bundle services in the main application config/services.yaml will often cause conflicts in loading. Because of this it is best to use an Extension for each bundle to load the services configurations, until you migrate away from the Bundle file hierarchy in favor of using a flat application configuration.
Again repeat the changes for each registered Bundle.
Ultimately it is best-practice to explicitly define all services
within the Bundles and NOT to use autowire: true. However, considering that this is a migration from
2.8 to 5.x, you will want to refactor away from using Bundles.
Bundle services.yaml and autowire
# /WORD/AlertBundle/Resources/config/services.yml
services:
# default configuration for services in *this* file
_defaults:
autowire: true # Automatically injects dependencies in your services.
autoconfigure: true # Automatically registers your services as commands, event subscribers, etc.
# makes classes in WORD/AlertBundle/ available to be used as services
# this creates a service per class whose id is the fully-qualified class name
WORD\AlertBundle\:
resource: '../../*'
exclude:
- '../../DependencyInjection/'
- '../../Entity/'
# Bundle specific services below this line
# ...
Bundle Extension
/* /WORD/AlertBundle/DependencyInjection/WORDAlertExtension.php */
namespace WORD\AlertBundle\DependencyInjection;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Extension\Extension;
use Symfony\Component\DependencyInjection\Loader\YamlFileLoader;
class WORDAlertExtension extends Extension
{
public function load(array $configs, ContainerBuilder $container)
{
$loader = new YamlFileLoader(
$container,
new FileLocator(__DIR__ . '/../Resources/config')
);
$loader->load('services.yml');
}
}
Manual Bundle Extension Loading
When not using the Bundle file hierarchy naming conventions, you may need to manually instantiate the Bundle's extension in the Bundle::getContainerExtension() method.
/* /WORD/AlertBundle/WordAlertBundle.php */
namespace WORD\AlertBundle;
// ...
use WORD\AlertBundle\DependencyInjection\AlertExtension;
class WordAlertBundle extends Bundle
{
public function getContainerExtension()
{
return new AlertExtension();
}
}
Container Access and Autowire
As mentioned above, as of Symfony 3.4+ you should never inject the entire container directly into a service.
Instead, the desired services should be explicitly type-hinted in the __construct() method of the class, which will be injected with the autowire: true services configuration.
# EXAMPLE ONLY
#
# /WORD/AlertBundle/Resources/config/services.yml
services:
# default configuration for services in *this* file
_defaults:
autowire: true # Automatically injects dependencies in your services.
autoconfigure: true
# example autowire and autoconfig
WORD\AlertBundle\:
resource: '../../*'
# EXAMPLE - Service Override Declaration
WORD\AlertBundle\Service\EmployeeManager:
arguments:
- '#fos_user.util.password_updater'
- '#fos_user.util.canonical_fields_updater'
- '#fos_user.object_manager'
- 'WORD\UserBundle\Entity\User'
# EXAMPLE - declaration of alias
word.manager.alert_employee:
alias: WORD\AlertBundle\Service\EmployeeManager
public: true # to prevent deletion of service when not used
# EXAMPLE OVERRIDE - manually declare all files under Command as a command service
WORD\AlertBundle\Command\:
resource: '../../Command/*'
tags:
- { name: 'console.command' } # force as a command
// /WORD/AlertBundle/Command/AlertExpirationEmailCommand.php
namespace WORD\AlertBundle\Command;
// ...
use Psr\Log\LoggerInterface;
use WORD\AlertBundle\Service\EmployeeManager;
class AlertExpirationEmailCommand extends Command
{
protected static $defaultName = 'WORD:alert:expiration_email';
private LoggerInterface $logger;
private EmployeeManager $manager;
public function __construct(LoggerInterface $logger, EmployeeManager $manager)
{
$this->logger = $logger;
$this->manager = $manager;
}
/**
* {#inheritdoc}
*/
protected function execute(InputInterface $input, OutputInterface $output): int
{
$days = intval($input->getOption('days'));
if ($days > 0) {
try {
$this->getManager()->sendExpirationEmail($days);
} catch (\Exception $e) {
$this->logger->error(sprintf(
'aemCommand: an error occurred in file "%s" (L%d): "%s"',
$e->getFile(),
$e->getLine(),
$e->getMessage()
));
}
}
return 0;
}
/**
* #return EmployeeManager
*/
private function getManager(): EmployeeManager
{
return $this->manager;
}
}
Moving Forward
Go through each of the service classes that uses the container directly and refactor them to use the explicit service type-hints or manual wiring in their respective services.yml configurations.
You should aim to migrate away from the 2.x - 3.4 Bundle file hierarchy and naming conventions and port to a flat configuration such as moving WORD\AlertBundle to WORD\Alert\... or WORD\Command\Alert\.... This way you have a single cohesive application, as opposed to several mini-applications as Bundles, significantly reducing code complexity, overhead, page loads, and cache warm-up times.
Alternatively, if each Bundle provides functionality shared across several of your applications, move each Bundle to their own repositories, following the Bundle best-practices, so they are segregated from the main application. Which will require another directory refactoring.

Move configuration from service.yaml to another file in Symfony5

My config/service.yaml contains several services that have huge configuration. Now, I want to move the configuration of that services to a separate file.
I tried to do like this:
At the end of service.yaml:
imports:
- { resource: 'packages/custom_service.yaml' }
config/packages/custom_service.yaml:
services:
App\Service\CustomService:
arguments:
$forms:
- location: 'room1'
id: 43543
- location: 'room2'
id: 6476546
- location: 'room3'
id: 121231
...
src/Service/CustomService.php:
/**
* #var array
*/
protected $forms;
public function __construct(array $forms)
{
$this->forms = $forms;
}
But when I try to autowire in some Controller, I am getting this error:
Cannot resolve argument $customService of "App\Controller\CustomController::customAction()": Cannot autowire service "App\Service\CustomService": argument "$forms" of method "__construct()" is type-hinted "array", you should configure its value explicitly.
But if I remove type hint, Then i get this error:
Cannot resolve argument $customService of "App\Controller\CustomController::customAction()": Cannot autowire service "App\Service\CustomService": argument "$forms" of method "__construct()" has no type-hint, you should configure its value explicitly.
The other two answers are partially correct but they don't quite cover everything.
To start with, anytime you decide you want multiple autowired service files you have to be careful that a given service is only autowired by one and only one file. Otherwise Symfony will attempt to define the service multiple times and usually fail. There are different approaches to accomplishing this but the most straight forward one is to explicitly exclude any custom services your config/services.yaml file:
# config/services.yaml
parameters:
imports:
- { resource: 'services/custom_service.yaml' }
services:
# default configuration for services in *this* file
_defaults:
autowire: true # Automatically injects dependencies in your services.
autoconfigure: true # Automatically registers your services as commands, event subscribers, etc.
# makes classes in src/ available to be used as services
# this creates a service per class whose id is the fully-qualified class name
App\:
resource: '../src/'
exclude:
- '../src/Service/CustomService.php' # ADD THIS
- '../src/DependencyInjection/'
- # the rest of the default excludes
So that takes care of skipping CustomService.php in the main config file.
Secondly, notice in the above file I loaded the custom_service.yaml file from config/services instead of config/packages. The packages is really reserved for bundle specific configuration. Every file in there is loaded by default. Putting service files in there may or may not work but it definitely changes the loading order of things and could cause problems. So make a directory just for custom services and use it.
You also need to repeat the _defaults section in each service file since _defaults are considered to be file specific. Bit of a pain perhaps but that is the way it is:
# config/services/custom_service.yaml
services:
_defaults:
autowire: true
autoconfigure: true
App\Service\CustomService:
arguments:
$forms:
-
location: 'room1'
id: 43543
-
location: 'room2'
id: 6476546
-
location: 'room3'
id: 121231
Also note that your yaml array syntax was messed up. Possibly just a copy/paste issue.
And that should do it.
Autowiring has file scope.
You need to set it in config/packages/custom_service.yaml as well.
Modify that file as follows
services:
_defaults:
autowire: true
App\Service\CustomService:
[...]
You should also add autoconfigure and/or visibility (public keyword) if needed.
Unfortunately, I cannot add a comment so I have to use an answer.
I have to make the following assumption, I believe you have the following snippet inside your services.yaml:
App\:
resource: '../src/'
exclude:
- '../src/DependencyInjection/'
- '../src/Entity/'
- '../src/Kernel.php'
- '../src/Tests/'
which already includes your desired class App\Service\CustomService and so Symfony will try to autowire it before looking inside the imports which leads to the error because you have that class defined inside the import which will only be evaluated if the root services.yaml does not already find the class.
The following example will work. However, you will lose auto-discovery of classes inside the App namespace and it is not recommended to do this.
services.yaml:
parameters:
imports:
- { resource: 'custom_services.yaml' }
services:
# default configuration for services in *this* file
_defaults:
autowire: true
autoconfigure: true
App\Controller\:
resource: '../src/Controller/'
tags: ['controller.service_arguments']
custom_services.yaml:
services:
_defaults:
autowire: true
autoconfigure: true
App\Service\SomeService:
arguments:
$forms:
- location: 'room1'
id: 43543
- location: 'room2'
id: 6476546
- location: 'room3'
id: 121231
src/Service/SomeService.php:
<?php
declare(strict_types=1);
namespace App\Service;
class SomeService
{
protected array $forms;
public function __construct(array $forms)
{
$this->forms = $forms;
}
public function getForms()
{
return $this->forms;
}
}
src/Controller/HomeController:
<?php
declare(strict_types=1);
namespace App\Controller;
use App\Service\SomeService;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\Routing\Annotation\Route;
class HomeController
{
#[Route('/', 'home')]
public function __invoke(SomeService $someService)
{
return new JsonResponse($someService->getForms());
}
}
What you can do instead is import the file in the Kernel instead in the yaml file which will overwrite the service definition.
So remove the imports part inside your services.yaml and make sure that you import the file inside the src/Kernel.php:
protected function configureContainer(ContainerConfigurator $container): void
{
$container->import('../config/{packages}/*.yaml');
$container->import('../config/{packages}/'.$this->environment.'/*.yaml');
if (is_file(\dirname(__DIR__).'/config/services.yaml')) {
$container->import('../config/services.yaml');
// Import your custom service files after services.yaml to overwrite already existing definitions
$container->import('../config/custom_services.yaml');
$container->import('../config/{services}_'.$this->environment.'.yaml');
} elseif (is_file($path = \dirname(__DIR__).'/config/services.php')) {
(require $path)($container->withPath($path), $this);
}
}

Adding services to a Controller through "container.service_subscriber" not working as expected

I am trying to use the container.service_subscriber tag on my Controller to make some services available without injecting them through the constructor. In our project we don't want to use the autowiring and also can't use the autoconfigure option.
The structure of the Controller is as follow:
I have a base BaseController which extends from the AbstractFOSRestController of FOSRestBundle which has some common used methods for all my Controllers. That service will be used as parent for my other Controllers.
The service definition looks like this:
WM\ApiBundle\Controller\BaseController:
class: WM\ApiBundle\Controller\BaseController
abstract: true
arguments:
- "#service1"
- "#service2"
- ...
WM\ApiBundle\Controller\UserController:
parent: WM\ApiBundle\Controller\BaseController
public: true
#autowire: true
class: WM\ApiBundle\Controller\UserController
tags:
- { name: 'container.service_subscriber'}
- { name: 'container.service_subscriber', key: 'servicexyz', id: 'servicexyz' }
The class looks like this:
/**
* User controller.
*/
class UserController extends AbstractCRUDController implements ClassResourceInterface
{
public static function getSubscribedServices()
{
return array_merge(parent::getSubscribedServices(), [
'servicexyz' => ServiceXYZ::class,
]);
}
.......
}
The problem I have is, if I set autowire: false, it always automatically sets the full container and with this the appropriate deprecation message (as I am not setting it myself):
User Deprecated: Auto-injection of the container for "WM\ApiBundle\Controller\UserController" is deprecated since Symfony 4.2. Configure it as a service instead.
When setting autowire: true Symfony does respect the container.service_subscriber tag and only sets the partial container (ServiceLocator), which also would solve the deprecation message. I would have expected that autowiring should not make any differences in this case because I am explicitly telling the service which other services it should have.
Am I using the tags wrong or do I have a general problem in understanding how to subscribe a service to a Controller?
The basic issue is that the builtin service subscriber functionality will only inject the service locator into the constructor. A conventional controller which extends AbstractController uses autoconfigure to basically override this and uses setContainer instead of the constructor.
# ApiBundle/Resources/config/services.yaml
services:
_defaults:
autowire: false
autoconfigure: false
Api\Controller\UserController:
public: true
tags: ['container.service_subscriber']
class UserController extends AbstractController
{
protected $container;
public function __construct(ContainerInterface $container)
{
$this->container = $container;
}
public static function getSubscribedServices()
{
return array_merge(parent::getSubscribedServices(), [
// ...
'logger' => LoggerInterface::class,
]);
}
public function index()
{
$url = $this->generateUrl('user'); // Works as expected
// $signer = $this->get('uri_signer'); // Fails as expected
$logger = $this->get('logger'); // Works as expected
return new Response('API Index Controller ' . get_class($this->container));
}
}
Results in:
API Index Controller Symfony\Component\DependencyInjection\Argument\ServiceLocator
Indicating that a service locator (as opposed to the global container is being injected).
You can also configure your service to use the setContainer method and eliminate the need for a constructor. Either approach will work.
Api\Controller\UserController:
public: true
tags: ['container.service_subscriber']
calls: [['setContainer', ['#Psr\Container\ContainerInterface']]]
Solution to the problem is to extend the service definition of the Controller with a call to setContainer to inject the '#Psr\Container\ContainerInterface' service:
WM\ApiBundle\Controller\BaseController:
class: WM\ApiBundle\Controller\BaseController
abstract: true
arguments:
- "#service1"
- "#service2"
- ...
calls:
- ['setContainer', ['#Psr\Container\ContainerInterface']]
WM\ApiBundle\Controller\UserController:
parent: WM\ApiBundle\Controller\BaseController
public: true
class: WM\ApiBundle\Controller\UserController
tags:
- { name: 'container.service_subscriber'}
- { name: 'container.service_subscriber', key: 'servicexyz', id: 'servicexyz' }
This will give me a ServiceLocator as container containing only the regiestered services instead of the full container without using the autowire option.
Sidenote: Setting the #service_container would inject the full container.
For completeness, there was already an issue on the symfony project where this was discussed.

Symfony 3.4.3 - service container: Error requires that you provide a value for the

i spent a lot of time but i did not solve it
I would like to pass LoggerInterface in the action of my controller
here is my service.yml
# Learn more about services, parameters and containers at
# https://symfony.com/doc/current/service_container.html
parameters:
#parameter_name: value
services:
# default configuration for services in *this* file
_defaults:
# automatically injects dependencies in your services
autowire: true
# automatically registers your services as commands, event subscribers, etc.
autoconfigure: true
# this means you cannot fetch services directly from the container via $container->get()
# if you need to do this, you can override this setting on individual services
public: false
# makes classes in src/AppBundle available to be used as services
# this creates a service per class whose id is the fully-qualified class name
AppBundle\:
resource: '../../src/AppBundle/*'
# you can exclude directories or files
# but if a service is unused, it's removed anyway
exclude: '../../src/AppBundle/{Entity,Repository,Tests}'
# controllers are imported separately to make sure they're public
# and have a tag that allows actions to type-hint services
AppBundle\Controller\:
resource: '../../src/AppBundle/Controller'
public: true
tags: ['controller.service_arguments']
# AppBundle\Controller\ArticlesController:
appbundle.form.type.articles:
class: AppBundle\Controller\ArticlesController
autowire: false
tags: ['controller.service_arguments']
here is my controller
<?php
//AppBundle\Controller\ArticlesController.php
namespace AppBundle\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use AppBundle\Dto\ArticlesRequest;
use AppBundle\Form\ArticlesType;
use Psr\Log\LoggerInterface;
class ArticlesController extends Controller
{
public function ListAction()
{
return $this->render("#App/Articles/list.html.twig");
}
public function CreateAction(Request $request, LoggerInterface $logger)
{
$createArticleRequest = new ArticlesRequest();
$form = $this->createForm(ArticlesType::class, $createArticleRequest);
return $this->render("#App/Articles/create.html.twig", array('form' => $form->createView()));
//return $this->render("#App/Articles/create.html.twig");
}
public function EditAction()
{
return $this->render("#App/Articles/edit.html.twig");
}
public function DeleteAction()
{
return $this->render("#App/Articles/delete.html.twig");
}
}
This is the error message:
Controller "AppBundle\Controller\ArticlesController::CreateAction()" requires that you provide a value for the "$logger" argument. Either the argument is nullable and no null value has been provided, no default value has been provided or because there is a non optional argument after this one.
In your services.yml under appbundle.form.type.articles, you need to pass autowire to true. Otherwise symfony cannot inject the dependencies in your service.
If you add a null default value as suggested above, you will not be able to log anything since you cannot log into a null. And if you do not want to log anything, simply remove the LoggerInterface from your parameters.
i resolve whit this code
instead of
public function CreateAction(Request $request, EntityManagerInterface $logger)
this
public function CreateAction(Request $request, EntityManagerInterface $logger = null)
add only = null

Symfony service declaration in services.yml

I am writing a service in Symfony 3 according to latest docs.
I wrote a class:
<?php
namespace julew\AdminBundle\Service;
class FileService {
public function create() {
die('I am here!');
}
}
My app/config/services.yml looks like:
services:
# default configuration for services in *this* file
_defaults:
# automatically injects dependencies in your services
autowire: true
# automatically registers your services as commands, event subscribers, etc.
autoconfigure: true
# this means you cannot fetch services directly from the container via $container->get()
# if you need to do this, you can override this setting on individual services
public: false
julew\AdminBundle\:
resource: '../../src/julew/AdminBundle/*'
exclude: '../../src/julew/AdminBundle/{Entity,Repository}'
But only what i get is this error:
Class Symfony\Bundle\FrameworkBundle\Test\WebTestCase not found in C:\xampp\htdocs\roch\app/config\services.yml (which is being imported from "C:\xampp\htdocs\roch\app/config\config.yml").
What am i doing wrong?
EDIT 1
The problem was, that automatically generated bundles had tests in it - had to add them to exclude paths.
But now other problem occured. Service is not injected via controler type-hint. Error that i get:
Controller "julew\RochNotesBundle\Controller\ParserController::indexAction()" requires that you provide a value for the "$fileService" argument. Either the argument is nullable and no null value has been provided, no default value has been provided or because there is a non optional argument after this one.
The controller method looks like:
public function indexAction(FileService $fileService) {
.
.
.
}
And at the top i added:
use julew\AdminBundle\Service\FileService;
If you want to inject various services into a controller, it can be a little easier to do so in the constructor, and avoid the use of the controller.service_arguments tag (which has to be explicitly added for the controller definition in the services.yml).

Categories