Symfony prependExtensionConfig with data from a service - symfony

I try to prepend a config array to a different bundle using the prependExtensionConfig from my bundle. All works fine until $config is hardcoded.
My goal is to load the $config values using a service (from the db for example) and than prepend it to the other bundle.
The problem is that the services are not loaded at that moment.
I guess this is a limitation is symfony2.
Any ideas? thx
class MyExtension extends Extension implements PrependExtensionInterface
{
/**
* {#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');
}
public function prepend(ContainerBuilder $container)
{
$config = // load config from a service ...
$container->prependExtensionConfig('other_bundle', $config);
}
}

First of all you need to set up your config to take values from parameters section of Service container:
Setup config.yml:
# app/config/config.yml
namespace:
subnamespace:
param1: %param1%
param2: %param2%
Then you need to fill %param1% and %param2% parameters in container with values from database. To do that you need to declare your CompilerPass and add it to the container. After whole container will be loaded (in the compile time) you will have access to all services in it.
Just get the entity manager service and query for needed parameters and register them in container.
Define Compiler pass:
# src/Acme/YourBundle/DependencyInjection/Compiler/ParametersCompilerPass.php
class ParametersCompilerPass implements CompilerPassInterface
{
public function process(ContainerBuilder $container)
{
$em = $container->get('doctrine.orm.default_entity_manager');
$param1 = $em->getRepository('Acme:Params')->find(1);
$param2 = $em->getRepository('Acme:Params')->find(2);
$container->setParameter('param1', $param1);
$container->setParameter('param2', $param2);
}
}
In the bundle definition class you need to add compiler pass to your container
# src/Acme/YourBundle/AcmeYourBundle.php
class AcmeYourBundle extends Bundle
{
public function build(ContainerBuilder $container)
{
parent::build($container);
$container->addCompilerPass(new ParametersCompilerPass(), PassConfig::TYPE_AFTER_REMOVING);
}
}
PassConfig::TYPE_AFTER_REMOVING means that this CompilerPass will be processed almost after all other compiler passes and in this moment you will already have every service injected.

Related

DI extension : There is no extension able to load the configuration for "twig"

I just created a symfony DI extension , where I'm trying to append some form theme configuration to my app/config.yml.
1) My Extension class:
class ELFinderFieldTypeExtension extends Extension implements PrependExtensionInterface
{
/**
* {#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('form_themes.yml');
}
...
2) My form_themes.yml
twig:
form_themes:
- 'ELFinderFieldTypeBundle:elfinder:elfinder_widget.html.twig'
Unfortunately, It's not the right way to load a twig config, that's way I'm getting kind of:
There is no extension able to load the configuration for "twig" (in
/var/www/html/..../DependencyInjection/../Resources/config/form_themes.yml).
Looked for namespace "twig", found none
Anyone have any idea would be voted and appreciated.
I found an easy solution, ally I need is to append my config after implementing PrependExtensionInterface Interface:
public function prepend(ContainerBuilder $container)
{
$configFile = \sprintf('%s%s', __DIR__, '/../Resources/config/form_themes.yml');
$this->prependYamlConfigFile($container, 'twig', $configFile);
}

Symfony2 conditional service declaration

I'm currently trying to find a solid solution to change the dependencies of a Symfony2 service dynamically. In detail: I have a Services which uses a HTTP-Driver to communicate with an external API.
class myAwesomeService
{
private $httpDriver;
public function __construct(
HTTDriverInterface $httpDriver
) {
$this->httpDriver = $httpDriver;
}
public function transmitData($data)
{
$this->httpDriver->dispatch($data);
}
}
While running the Behat tests on the CI, I'd like to use a httpMockDriver instead of the real driver because the external API might be down, slow or even broken and I don't want to break the build.
At the moment I'm doing something like this:
<?php
namespace MyAwesome\TestBundle\DependencyInjection;
class MyAwesomeTestExtension 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'));
$environment = //get environment
if ($environment == 'test') {
$loader->load('services_mock.yml');
} else {
$loader->load('services.yml');
}
}
}
This works for now, but will break for sure. So, is there a more elegant/solid way to change the HTTPDriver dynamically?
I finally found a solution that looks solid to me. As of Symfony 2.4 you can use the expression syntax: Using the Expression Language
So I configured my service this way.
service.yml
parameters:
httpDriver.class: HTTP\Driver\Driver
httpMockDriver.class: HTTP\Driver\MockDriver
myAwesomeService.class: My\Awesome\Service
service:
myAwesomeService:
class: "%myAwesomeService.class%"
arguments:
- "#=service('service_container').get('kernel.environment') == 'test'? service('httpMockDriver) : service('httpDriver)"
This works for me.

How can I listen to console events in symfony?

I'm trying to hook into symfonys console events with the symfony standard edition (2.3), but it just won't work.
I created a listener according to their example and follow the guides on event registration:
namespace Acme\DemoBundle\EventListener;
use Symfony\Component\Console\Event\ConsoleCommandEvent;
use Symfony\Component\Console\ConsoleEvents;
class AcmeCommandListener
{
public function onConsoleCommand(ConsoleCommandEvent $event) {
// get the output instance
$output = $event->getOutput();
// get the command to be executed
$command = $event->getCommand();
// write something about the command
$output->writeln(sprintf('Before running command <info>%s</info>', $command->getName()));
}
}
and someone on the mailing list told me to register it as event in the service container. So I did this:
services:
kernel.listener.command_dispatch:
class: Acme\DemoBundle\EventListener\AcmeCommandListener
tags:
- { name: kernel.event_listener, event: console.command }
But obviously the tagging is not correct and I can't find the correct names for that. How would I do that?
Platform\EventListener\Console\InitListener:
tags:
- { name: kernel.event_listener, event: console.command, priority: 1024 }
<?php
class CustomListener
{
public function onConsoleCommand(ConsoleCommandEvent $event): void
{
//do somehting
}
}
?>
So, I finally got it. The above code in the original post ist fully working, but I defined my services.yml within my bundle not in the application config app/config.yml. This means, the configuration was never loaded. I had to import the configuration via container extensions:
# Acme/DemoBundle/DependencyInjection/AcmeDemoExtension.php
namespace Acme\DemoBundle\DependencyInjection;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
use Symfony\Component\DependencyInjection\Loader;
class AcmeDemoExtension extends Extension
{
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');
}
}
and
# Acme/DemoBundle/DependencyInjection/Configuration.php
namespace Acme\DemoBundle\DependencyInjection;
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
use Symfony\Component\Config\Definition\ConfigurationInterface;
class Configuration implements ConfigurationInterface
{
public function getConfigTreeBuilder()
{
$treeBuilder = new TreeBuilder();
$rootNode = $treeBuilder->root('headwork_legacy');
return $treeBuilder;
}
}
Though I guess you can even leave out the $configuration = new Configuration(); part and the Configuration class.
Your tags event listener name must be a console.event_listener. It's has helped me to resolve this issue.
services:
kernel.listener.command_dispatch:
class: Acme\DemoBundle\EventListener\AcmeCommandListener
tags:
- { name: console.event_listener, event: console.command }

Access a bundle (sncRedisBundle) from within an Extension

I have successfully installed the sncRedisBundle and used the predis element of it within a controller, using:
$this->container->get('snc_redis.default');
I want to do the same within an extension:
class MyExtension extends Extension
{
/**
* {#inheritDoc}
*/
public function load(array $configs, ContainerBuilder $container)
{
$configuration = new Configuration();
$config = $this->processConfiguration($configuration, $configs);
$redis = $container->get('snc_redis.default');
}
}
But I get:
The service definition "snc_redis.default" does not exist.
Is this a scoping issue? How do I access redis from within an Extension?
Thanks!
services:
site:
class: Emlaktown\AppBundle\Site\Site
arguments: [%api_url%, "#request_stack", "#service_container"]
....
use Symfony\Component\DependencyInjection\Container;
....
public function __construct($apiUrl, RequestStack $requestStack, Container $container)
{
$this->client = new Client($apiUrl);
$this->redis = $container->get('snc_redis.cache');
$request = $requestStack->getCurrentRequest();
$this->client->setDefaultOption('Accept-Language', $request->getLocale());
}

Symfony2 access Doctrine Entity Manager in custom class

UPDATE:
In case you need to work with Entity Manager in a custom class, you could go this way:
put this code in your bundle:
use Symfony\Component\DependencyInjection\Container;
use Symfony\Component\HttpFoundation\Request;
require_once DIR . '/../../../app/bootstrap.php.cache';
require_once DIR . '/../../../app/AppKernel.php';
class ApplicationBoot {
private static $kernel;
public static function getContainer() {
if(self::$kernel instanceof \AppKernel) {
if(!self::$kernel->getContainer() instanceof Container){
self::$kernel->boot();
}
return self::$kernel->getContainer();
}
$environment = 'prod';
if (!array_key_exists('REMOTE_ADDR', $_SERVER) || in_array(#$_SERVER['REMOTE_ADDR'], array('127.0.0.1', '::1', 'localhost'))) {
$environment = 'dev';
}
self::$kernel = new \AppKernel($environment, false);
self::$kernel->boot();
return self::$kernel->getContainer();
}
public static function shutDown() {
self::$kernel->shutdown();
}}
So now you can access EntityManager:
$container = ApplicationBoot::getContainer();
$entityManager = $container->get('doctrine')->getEntityManager();
I have not seen a service file like this:
arguments:
entityManager: "#doctrine.orm.entity_manager"
Probably should be:
arguments: [#doctrine.orm.entity_manager]
UPDATE:
Based on some comments it appears that you are trying to do:
$job = new PostJob();
And expecting that entity manager will somehow be passed. And that is just not the way things work. You need to do:
$job = $this->get('postjob.service.id');
In order to have the Symfony 2 dependency injection work. Review the chapter in the manual on services. It might seem a bit over whelming at first but once you get a few services working then it becomes second nature.
To load the services.yml from your bundle, you need to provide an extension class:
// src/Vendor/YourBundle/DedendencyInjection/VendorYourBundleExtension.php
namespace Vendor\YourBundle\DependencyInjection;
use Symfony\Component\HttpKernel\DependencyInjection\Extension,
Symfony\Component\DependencyInjection\ContainerBuilder,
Symfony\Component\DependencyInjection\Loader\YamlFileLoader,
Symfony\Component\Config\FileLocator;
class VendorYourBundleExtension extends Extension
{
public function load(array $configs, ContainerBuilder $container)
{
$loader = new YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
$loader->load('services.yml');
}
}

Resources