I migrate symfony 3 to symfony 4.3 and my custom bundle moved to: src/Bundles/FooBundle.
Strucutre FooBundle:
- DependencyInjection
- Configuration.php
- FooExtension.php
- Event
- Model
- Resources
- config
- services.yml
- Service
- Twig
- Exception
- FooBundle.php
And files
Bundles/FooBundle/Resources/config/servies.yaml
services:
foo:
class: App\Bundles\FooBundle\Service\Foo
arguments:
- '%foo.argument1%'
- '%foo.argument2%'
- '%foo.argument3%'
foo.listener.example:
class: App\Bundles\FooBundle\Event\Listener\ExampleListener
arguments: ['#annotations.reader']
tags:
- { name: kernel.event_listener, event: kernel.controller, method: onKernelController }
foo.filter:
class: App\Bundles\FooBundle\Filter\FilterConverter
tags:
- { name: request.param_converter }
Bundles/FooBundle/DependencyInjection/Configuration.php
<?php
namespace App\Bundles\FooBundle\DependencyInjection;
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
use Symfony\Component\Config\Definition\ConfigurationInterface;
class Configuration implements ConfigurationInterface
{
/**
* {#inheritdoc}
*/
public function getConfigTreeBuilder()
{
$treeBuilder = new TreeBuilder('foo');
$treeBuilder->getRootNode()
->children()
->scalarNode('argument1')->isRequired()->end()
->scalarNode('argument2')->isRequired()->end()
->scalarNode('argument3')->isRequired()->end()
->end();
return $treeBuilder;
}
}
Bundles/FooBundle/DependencyInjection/FooExtension.php
<?php
namespace App\Bundles\FooBundle\DependencyInjection;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
use Symfony\Component\DependencyInjection\Loader;
/**
* This is the class that loads and manages your bundle configuration.
*
* #link http://symfony.com/doc/current/cookbook/bundles/extension.html
*/
class FooExtension extends Extension
{
/**
* {#inheritdoc}
*/
public function load(array $configs, ContainerBuilder $container)
{
$configuration = new Configuration();
$config = $this->processConfiguration($configuration, $configs);
$container->setParameter('foo.argument1', $config['page_field_name']);
$container->setParameter('foo.argument2', $config['per_page_field_name']);
$container->setParameter('foo.argument3', $config['sort_field_name']);
$loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
$loader->load('services.yml');
}
}
And bundle registration: config/bundles.php
App\Bundles\FooBundle\FooBundle::class => ['all' => true],
configuration package:
config/packages/foo.yam
foo:
argument1: test1
argument2: test2
argument3: test3
Done... Run app and error:
Cannot autowire service "App\Bundles\FooBundle\Service\Foo": argument "$argument1" of method "__construct()" has no type-hint, you should configure its value explicitly.
But, when I add conf in config/services.yaml:
App\Bundles\FooBundle\Service\Foo:
arguments:
- '%foo.argument1%'
- '%foo.argument2%'
- '%foo.argument3%'
That working...
Question: why is the bundle service not working?
I guess the problem is that the default configuration in symfony add autowireing to all classes under the App namespace.
Check the configuration under 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 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/{DependencyInjection,Entity,Migrations,Tests,Kernel.php}'
You can remove this lines:
App\:
resource: '../src/*'
exclude: '../src/{DependencyInjection,Entity,Migrations,Tests,Kernel.php}'
And the problem should disappear.
Related
I'm using Symfony 4.3.
I have a problem with autowiring service argument in controller method.
It seems like Security class cannot be found.
I installed Security component by composer require symfony/security
This is row from "composer show":
symfony/security v4.3.4 Symfony Security Component
Composer.json:
"symfony/security": "4.3.*",
Services.yaml:
parameters:
services:
_defaults:
autowire: true
autoconfigure: true
App\:
resource: '../src/*'
exclude: '../src/{DependencyInjection,Entity,Migrations,Tests,Kernel.php}'
App\Controller\:
resource: '../src/Controller'
tags: ['controller.service_arguments']
HomeController.php:
use App\Service\DB\DBALProvider;
class HomeController extends AbstractController
{
/**
* #Route("/", name="index")
*/
public function index(DBALProvider $DBALProvider)
{
DBALProvider.php:
use Symfony\Component\Security\Core\Security;
use Doctrine\ORM\EntityManagerInterface;
class DBALProvider
{
private $entityManager;
private $context;
private $user;
public function __construct(EntityManagerInterface $entityManager, Security $security)
{
Error that displays when I enter "/" route
Cannot resolve argument $DBALProvider of "App\Controller\HomeController::index()": Cannot autowire service "App\Service\DB\DBALProvider": argument "$security" of method "__construct()" references class "Symfony\Component\Security\Core\Security" but no such service exists.
Thanks for any suggestions.
composer require symfony/security-bundle
is the solution (thanks to Cerad).
https://symfony.com/doc/current/security.html
Well I though that composer require symfony/security install all necessary bundles.
Symfony 4.2.2
To cache all responses in one controller, I'm using an event listener for the kernel.controller event. My Event listener needs a couple of services and info:
EnityManagerInterface
the controller being cached
kernel cache folder
I have set this up like this:
namespace App\Listener;
use App\Controller\DataOutputController;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Debug\TraceableEventDispatcher;
use Symfony\Component\HttpKernel\Event\FilterControllerEvent;
class CachedOutput{
protected $cacheFolder;
protected $em;
protected $controller;
public function __construct($cacheFolder, EntityManagerInterface $em, DataOutputController $controller )
{
$this->cacheFolder = $cacheFolder;
$this->em = $em;
$this->controller = $controller;
}
public function findCachedObject(FilterControllerEvent $event, $eventName, TraceableEventDispatcher $dispatcher
){
$params = $event->getRequest()->attributes->get('_route_params');
$fileType = $this->em->getRepository('App:FileType')->find($params->get('fileType'));
$dataSet = $this->controller->getDataSet($params->get('dataSetSearch')?:'latest', $fileType->getType());
$cacheFile = $this->cacheFolder.'/output/DS'.$dataSet->getId().'-FT'.$fileType->getId().'.html';
if (file_exists($cacheFile)){
$fh = fopen($cacheFile,'r');
return new Response(fpassthru($fh));
}
}
}
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.
public: true # Allows optimizing the container by removing unused services; this also means
# fetching services directly from the container via $container->get() won't work.
# The best practice is to be explicit about your dependencies anyway.
bind:
$projectDir: '%kernel.project_dir%'
# 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/{DependencyInjection,Entity,Migrations,Tests,Kernel.php}'
# controllers are imported separately to make sure services can be injected
# as action arguments even if you don't extend any base controller class
App\Controller\:
resource: '../src/Controller'
tags: ['controller.service_arguments']
controller.return_cached_output:
class: App\Listener\CachedOutput
arguments:
$cacheFolder: "%kernel.cache_dir%"
tags:
- { name: kernel.event_listener, event: kernel.controller, method: findCachedObject }
However, I still get an error regarding the cache folder:
Cannot autowire service "App\Listener\CachedOutput": argument "$cacheFolder" of method "__construct()" has no type-hint, you should configure its value explicitly.
What am I missing?
Update:
Have tried to use alias for the service like this:
App\Listener\CachedOutput:
public: false
arguments:
$cacheFolder: "%kernel.cache_dir%"
tags:
- { name: kernel.event_listener, event: kernel.controller, method: findCachedObject }
return_cached_output:
alias: App\Listener\CachedOutput
public: true
with no success
You need to type-hint in your __construct(string $cacheFolder, ...)
Considering this following service class:
namespace AppBundle\Listener\Entity;
use AppBundle\Entity\Payment;
use AppBundle\Event\PaymentEvent;
use AppBundle\Event\PaymentEvents;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
final class PaymentEntityListener
{
/**
* #var EventDispatcherInterface
*/
private $eventDispatcher;
/**
* #required
*
* #param EventDispatcherInterface $eventDispatcher
*/
public function setEventDispatcher(EventDispatcherInterface $eventDispatcher)
{
dump($eventDispatcher);
$this->eventDispatcher = $eventDispatcher;
}
/**
* #param Payment $payment
*/
public function postPersist(Payment $payment)
{
$this->eventDispatcher->dispatch(PaymentEvents::ADD, new PaymentEvent($payment));
}
}
The method PaymentEntityListener::setEventDispatcher should be call with the EventDispatcherInterface matched service.
This method is correctly configured according to the debug command:
$ ./bin/console debug:container --show-private AppBundle\\Listener\\Entity\\PaymentEntityListener
Information for Service "AppBundle\Listener\Entity\PaymentEntityListener"
=========================================================================
---------------- -------------------------------------------------
Option Value
---------------- -------------------------------------------------
Service ID AppBundle\Listener\Entity\PaymentEntityListener
Class AppBundle\Listener\Entity\PaymentEntityListener
Tags -
Calls setEventDispatcher
Public no
Synthetic no
Lazy no
Shared yes
Abstract no
Autowired yes
Autoconfigured yes
---------------- -------------------------------------------------
But this method is never called.
Here is my services.yml:
imports:
- { resource: legacy_aliases.yml }
services:
_defaults:
autowire: true
autoconfigure: true
public: false
_instanceof:
Doctrine\ORM\Decorator\EntityManagerDecorator:
public: true
AppBundle\:
resource: '../../src/AppBundle/*'
exclude: '../../src/AppBundle/{Entity,Repository}'
AppBundle\Controller\:
resource: '../../src/AppBundle/Controller'
tags: ['controller.service_arguments']
PowerDNSBundle\:
resource: '../../src/PowerDNSBundle/*'
exclude: '../../src/PowerDNSBundle/{Entity,Repository}'
PowerDNSBundle\Doctrine\ORM\PowerDNSEntityManager:
arguments:
$wrapped: '#doctrine.orm.powerdns_entity_manager'
I don't know why the method is not called and why Symfony does not throw any exception.
FYI, the following command:
./bin/console debug:container --show-private Symfony\\Component\\EventDispatcher\\EventDispatcherInterface
Does return a service match.
Where can be the issue?
Thanks for your help.
I finally found why: This listener didn't have the correct tag:
AppBundle\Listener\Entity\:
resource: '../../src/AppBundle/Listener/Entity'
tags: ['doctrine.orm.entity_listener']
Because of that, I guess the service is not recognized by Doctrine and this one try to create the listener itself. But in this case, the method will never be called.
I have two services and I want pass my parameter from config.yml
my config.yml
parameters:
MyService.class: Acme\UserBundle\Services\sendEmail
MyService.arguments: #mailer
NewUserListener.class: Acme\UserBundle\Event\NewUserListener
NewUserListener.arguments: #MyService
my service.yml inside bundle
services:
MyService:
class: %MyService.class%
arguments: [%MyService.arguments%]
NewUserListener:
class: %NewUserListener.class%
arguments: [%NewUserListener.arguments%]
tags:
- { name: kernel.event_listener, event: new.user, method: sendEmailToUsers }
I got an error
You cannot dump a container with parameters that contain references to
other services
My Questions are:
How can I inject my arguments from config.yml?
Where can i Find the list of "global service" like #mailer ? i don't find in doc
You can't reference a service in a parameter. You should replace %MyService.arguments% with #mailer.
To find all available services, run php app/console container:debug
This a bit more complicated!
First, you have to declare your default services like that (I changed all the names in order to be compliant with the Symfony2's conventions):
# resources/config/services.yml
services:
my_own.service.default.class: Acme\UserBundle\Services\sendEmail
my_own.user_listener.default.class: Acme\UserBundle\Event\NewUserListener
services:
my_own.service.default:
class: %my_own.service.default.class%
arguments: [#mailer]
my_own.user_listener:
class: %my_own.user_listener.class%
arguments: [#my_own.service]
tags:
- { name: kernel.event_listener, event: new.user, method: sendEmailToUsers }
We will define some configuration for your bundle in order to allow to change the used services:
namespace My\OwnBundle\DependencyInjection;
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
use Symfony\Component\Config\Definition\ConfigurationInterface;
/**
* This is the class that validates and merges configuration from your app/config files
*
* To learn more see {#link http://symfony.com/doc/current/cookbook/bundles/extension.html#cookbook-bundles-extension-config-class}
*/
class Configuration implements ConfigurationInterface
{
/**
* {#inheritDoc}
*/
public function getConfigTreeBuilder()
{
$treeBuilder = new TreeBuilder();
$rootNode = $treeBuilder->root('my_own');
// Here you should define the parameters that are allowed to
// configure your bundle. See the documentation linked above for
// more information on that topic.
$rootNode
->children()
->scalarNode('service')->defaultValue('my_own.service.default')->end()
->scalarNode('user_listener')->defaultValue('my_own.user_listener.default')->end()
->end();
return $treeBuilder;
}
}
Note that, by default, we use our default services defined above in our bundle.
You now can use the following to change your services (in your app/config.yml) for instance:
# app/config.yml
my_own:
service: my_other.service
user_listener: my_other.user_listener
Of course, you can define the services my_other.service and my_other.user_listener as you want in your bundle or in another bundle.
Now we have to tell how to use this configuration to take the wanted services:
namespace My\OwnBundle\DependencyInjection;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
use Symfony\Component\DependencyInjection\Loader;
/**
* This is the class that loads and manages your bundle configuration
*
* To learn more see {#link http://symfony.com/doc/current/cookbook/bundles/extension.html}
*/
class MyOwnExtension extends Extension
{
/**
* {#inheritDoc}
*/
public function load(array $configs, ContainerBuilder $container)
{
$configuration = new Configuration();
$config = $this->processConfiguration($configuration, $configs);
$loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
$loader->load('services.yml');
$container->setAlias('my_own.service', $config['service']);
$container->setAlias('my_own.user_listener', $config['user_listener']);
}
}
Finally, in the rest of your code you have to use the aliased services my_own.service and my_own.user_listener in your code:
// In one of your controller:
$this->container->get('my_own.service');
/* or directly */ $this->get('my_own.service'); // if your controller is a child of the framework bundle class `Controller`.
I am trying to override Registration Form in FOSUserBundle but i get this error:
i have followed this tutorial in the official documentation:
Link
Could not load type "uae_user_registration"
My files are:
services.yml
# src/Uae/UserBundle/Resources/config/services.yml
services:
uae_user.registration.form.type:
class: Uae\UserBundle\Form\Type\RegistrationFormType
arguments: [%fos_user.model.user.class%]
tags:
- { name: form.type, alias: uae_user_registration }
config.yml:
app/config/config.yml
fos_user:
db_driver: orm
firewall_name: main
user_class: Uae\UserBundle\Entity\User
registration:
form:
type: uae_user_registration
RegistrationFormType:
<?php
#src/Uae/UserBundle/Form/Type/RegistrationType.php
namespace Uae\UserBundle\Form\Type;
use Symfony\Component\Form\FormBuilderInterface;
use FOS\UserBundle\Form\Type\RegistrationFormType as BaseType;
class RegistrationFormType extends BaseType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
parent::buildForm($builder, $options);
// add your custom field
$builder->add('nom');
$builder->add('prenom');
}
public function getName()
{
return 'uae_user_registration';
}
}
i solved my problem:
i just imported the new service that i created in the config file
app\config\config.yml
imports:
- { resource: #UaeUserBundle/Resources/config/services.yml }
The reason you're getting the error is because you do not have a DependencyInjection for your specific bundle. The program doesn't know where to look for your services.yml file.
You need an UaeUserExtension.php and Configuration.php inside your DependencyInjection folder under your User Bundle.
The easy solution to this is to generate the bundle via app/console generate:bundle. This way, it'll create your DependencyInjection for you automatically.
The manual solution would be to create a DependencyInjection folder inside your Uae/UserBundle. Inside DependencyInjection, create a file called Configuration.php and place the contents below:
<?php
namespace Uae\UserBundle\DependencyInjection;
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
use Symfony\Component\Config\Definition\ConfigurationInterface;
class Configuration implements ConfigurationInterface
{
/**
* {#inheritDoc}
*/
public function getConfigTreeBuilder()
{
$treeBuilder = new TreeBuilder();
$rootNode = $treeBuilder->root('uae_user');
return $treeBuilder;
}
}
And create a file called UaeUserExtension.php inside the same directory and place these contents inside:
<?php
namespace Uae\UserBundle\DependencyInjection;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
use Symfony\Component\DependencyInjection\Loader;
class EnergyUserExtension extends Extension
{
/**
* {#inheritDoc}
*/
public function load(array $configs, ContainerBuilder $container)
{
$configuration = new Configuration();
$config = $this->processConfiguration($configuration, $configs);
$loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
$loader->load('services.yml');
}
}