How to Give Container as Argument to Services

How to give container as argument to services

Add:

<argument type="service" id="service_container" />

And in your listener class:

use Symfony\Component\DependencyInjection\ContainerInterface;

//...

public function __construct(ContainerInterface $container, ...) {

Pass the 'container' as an argument to service

The container is registered as the service called "service_container", so you must pass "service_container" and not just "container":

<argument type="service" id="service_container" />

This is why currently Symfony doesn't understand what is the service "container" because there is no one registered with this name !

How to pass a parameter to a named services in LightInject?

You mean like this?

class Program
{
static void Main(string[] args)
{
var container = new ServiceContainer();
container.Register<string,IFoo>((factory, s) => new Foo(s), "Foo");
container.Register<string, IFoo>((factory, s) => new AnotherFoo(s), "AnotherFoo");

var foo = container.GetInstance<string, IFoo>("SomeValue", "Foo");
Debug.Assert(foo.GetType().IsAssignableFrom(typeof(Foo)));

var anotherFoo = container.GetInstance<string, IFoo>("SomeValue", "AnotherFoo");
Debug.Assert(anotherFoo.GetType().IsAssignableFrom(typeof(AnotherFoo)));
}
}

public interface IFoo { }

public class Foo : IFoo
{
public Foo(string value){}
}

public class AnotherFoo : IFoo
{
public AnotherFoo(string value) { }
}

How can i pass parameters to the constructors through the Service Container?

You can pass parameters to App::make and pass the parameters to the constructor like this:

$this->app->bind( 'App\BokaKanot\Interfaces\BillingInterface',
function( $app, array $parameters)
{
//call the constructor passing the first element of $parameters
return new KlarnaBilling( $parameters[0] );
} );

//pass the parameter to App::Make
App::make( 'App\BokaKanot\Interfaces\BillingInterface', [ $merchantId ] );

Symfony2 service container: Inject array of services parameter as an argument to another service using XML

So, you inject array of parameters no array of services. You can inject service by service via:

<services>
<service id="notifications_decorator" class="\NotificationsDecorator">
<argument type="service" id="decorator1"/>
<argument type="service" id="decorator2"/>
<argument type="service" id="decorator3"/>
</service>
</services>

Or (in my opinion better way) tag decorators services and inject them to notifications_decorator during compilation passes.

UPDATE: Working with Tagged Services

In your case you have to modify your services like this:

<services>
<service id="decorator1" class="\FirstDecorator">
<tag name="acme_decorator" />
</service>
<service id="decorator2" class="\SecondDecorator">
<tag name="acme_decorator" />
</service>
<service id="decorator3" class="\ThirdDecorator">
<tag name="acme_decorator" />
</service>
</services>

Additionaly you should remove decorators.all parameter from <parameters> section. Next, you have to add sth like addDectorator function for \NotificationsDecorator:

class NotificationsDecorator
{
private $decorators = array();

public function addDecorator($decorator)
{
$this->decorators[] = $decorator;
}
// more code
}

It would be great if you write some interface for decorator's and add this as type of $decorator for addDecorator function.

Next, you have to write own compiler pass and ask them about tagged services and add this services to another one (simillarly to doc):

use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\Reference;

class DecoratorCompilerPass implements CompilerPassInterface
{
public function process(ContainerBuilder $container)
{
if (!$container->hasDefinition('notifications_decorator')) {
return;
}

$definition = $container->getDefinition('notifications_decorator');
$taggedServices = $container->findTaggedServiceIds('acme_decorator');

foreach ($taggedServices as $id => $attributes) {
$definition->addMethodCall(
'addDecorator',
array(new Reference($id))
);
}
}
}

Finally, you should add your DecoratorCompilerPass to Compiler in your bundle class like:

class AcmeDemoBundle extends Bundle
{
public function build(ContainerBuilder $container)
{
parent::build($container);

$container->addCompilerPass(new DecoratorCompilerPass());
}
}

Good luck!

Symfony service container injecting string not object

Parameters are just strings, so you you trying to inject the string of your class name, not the actual class.

You need to define AppBundle\InjectObject as a service, then inject that.

Example:

parameters:
app.my_class.class: AppBundle\MyClass
app.object_to_inject.class: AppBundle\InjectObject

services:
app.my_object:
class: "%app.object_to_inject.class%"
app.my_service:
class: %app.my_class.class%
arguments: [@app.another_service, @app.my_object]

Passing dynamic arguments to service factory in Symfony

You could probably accomplish this easily with a custom compiler pass.

First tag all the old repository classes by loading the directory where they exist:

OldApp\Repository\:
resource: '../src/OldApp/Repository/*'
autowire: false
autoconfigure: false
tags: ['oldapp_repository']

(I think that you may need to also exclude src/OldApp from the default automatic service loading. E.g.:

App\:
resource: '../src/*'
exclude: '../src/{OldApp/Repository,DependencyInjection,Entity,Tests,Kernel.php}'

... but I'm not 100% sure, test this one).

Then create a compiler pass to go through the tags and define a factory for each one:

class OldAppRepositoryCompilerPass implements CompilerPassInterface
{

public function process(ContainerBuilder $container): void
{
$taggedServices = $container->findTaggedServiceIds('oldapp_repository');

foreach ($taggedServices as $serviceId => $tags) {

$definition = $container->getDefinition($serviceId);
$definition
->setFactory([new Reference('oldapp.service_factory'), 'factory'])
->addArgument($serviceId);
}

}
}

And in your Application Kernel build() method add the compiler pass:

// src/Kernel.php
namespace App;

use Symfony\Component\HttpKernel\Kernel as BaseKernel;
// ...

class Kernel extends BaseKernel
{
// ...

protected function build(ContainerBuilder $container): void
{
$container->addCompilerPass(new OldAppRepositoryCompilerPass());
}
}

Can't test this right at this minute, but this should get you going in the right direction. For additional details check the docs:

  • Working with service tags

You can check this example repo where the above is implemented and working. On this repo the OldApp namespace is outside of App and src, so no need to exclude it from automatic service loading.

Injecting parameter based service into other service

This question has a similar answer here

I think the best way to use this kind of definition is to use service aliasing.

This may look like this

Acme\FooBundle\DependencyInjection\AcmeFooExtension

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');

$alias = $config['mailer']['driver'];
$container->setAlias('my_scope.mailer_driver', $alias);
}

This will alias the service you've defined in my_scope.mailer.driver with my_scope.mailer_driver, which you can use as any other service

services.yml

services:
my_scope.mailer_driver:
alias: my_scope.mailer_driver_smtp # Fallback

my_scope.mailer_driver_smtp:
class: My\Scope\Driver\Smtp

my_scope.mailer_driver_mock:
class: My\Scope\Driver\Mock

my_scope.mailer:
class: My\Scope\Mailer
arguments:
- @my_scope.mailer_driver

With such a design, the service will change whenever you change the my_scope.mailer_driver parameter in your config.yml.

Note that the extension will throw an exception if the service doesn't exist.

There is a way to register service which is given as implicit type and has constructor with more then one parameter of primitive type

Either use parameter by name: Parameters.Of.Name(x, ...).Name(y, ...)

Or the full blown Made.Of which accepts primitive constants (or any value via Arg.Index):

Made.Of(() => new Blah(param1, param2, Arg.Of<RemainingParamTypeToInject>()))

Update: the full example

using System;
using DryIoc;

public class Program
{
public static void Main()
{
var container = new Container();

// variant 1
container.RegisterInstance("1", serviceKey: "x");
container.RegisterInstance("2", serviceKey: "y");
container.Register<A>(made: Parameters.Of.Name("x", serviceKey: "x").Name("y", serviceKey: "y"));

// variant 2
//container.Register<A>(made: Parameters.Of.Name("x", _ => "1").Name("y", _ => "2"));

var a = container.Resolve<A>();

Console.WriteLine(a.X + ", " + a.Y);
}

public class A
{
public string X, Y;
public A(string x, string y)
{
X = x;
Y = y;
}
}
}


Related Topics



Leave a reply



Submit