Factory Parameters #362

Closed
bradynpoulsen opened this Issue Jan 4, 2016 · 6 comments

Comments

Projects
None yet
4 participants
@bradynpoulsen
Contributor

bradynpoulsen commented Jan 4, 2016

It would be really nice if I could define specific parameters in my factory.

Take the following example where I want to build an array of delegates for a handler

class SomeHandlerFactory
{
    public function create(SomeHandler $handler, array $delegates)
    {
        $handler->doSomethingCool();
        array_map([$handler, 'pushDelegate'], $delegates);
        return $handler;
    }
}

I would like to define the array of delegates to isolate accessing the container directly to my config.php

// config.php
return [
    'SomeHandler' => factory(['SomeHandlerFactory', 'create'])->param('delegates', get('handlerDelegates')),
    'handlerDelegates' => function (ContainerInterface $container) {
        $delegates = [];
        if (Config::get('someSetting')) {
            array_push($delegates, $container->get('DelegateA'));
        }
        if (Config::get('awesomeMode')) {
            array_push($delegates, $container->get('ImpeachTheOffice'));
        }
        return $delegates;
    }
];

@mnapoli mnapoli added the enhancement label Jan 5, 2016

@mnapoli

This comment has been minimized.

Show comment
Hide comment
@mnapoli

mnapoli Jan 5, 2016

Member

👍 thanks for the suggestion

Member

mnapoli commented Jan 5, 2016

👍 thanks for the suggestion

@klinki

This comment has been minimized.

Show comment
Hide comment
@klinki

klinki Feb 8, 2016

👍 This would be great feature. I need something similar. But I also need information about currently resolved class (at least name).

I have cache factory which accepts string as a key and several classes using cache.

class CacheFactory 
{
   protected $existingCaches = [];

   public function create($name)
   {
      if (!array_key_exists($name, $this->existingCaches)) {
           $this->existingCaches[$name] = new Cache();
      }

      return $this->existingCaches[$name];
   }
}
class CacheClientA 
{
    public function __construct(Cache $cache)
    {
           ......
    }
}
class CacheClientB
{
    public function __construct(Cache $cache)
    {
           ......
    }
}

It would be really great, if DI could call create method on CacheFactory and pass CacheClient class name as parameter.

klinki commented Feb 8, 2016

👍 This would be great feature. I need something similar. But I also need information about currently resolved class (at least name).

I have cache factory which accepts string as a key and several classes using cache.

class CacheFactory 
{
   protected $existingCaches = [];

   public function create($name)
   {
      if (!array_key_exists($name, $this->existingCaches)) {
           $this->existingCaches[$name] = new Cache();
      }

      return $this->existingCaches[$name];
   }
}
class CacheClientA 
{
    public function __construct(Cache $cache)
    {
           ......
    }
}
class CacheClientB
{
    public function __construct(Cache $cache)
    {
           ......
    }
}

It would be really great, if DI could call create method on CacheFactory and pass CacheClient class name as parameter.

@mnapoli

This comment has been minimized.

Show comment
Hide comment
@mnapoli

mnapoli Feb 8, 2016

Member

@klinki it's possible to retrieve the requested entry name, have a look here: http://php-di.org/doc/php-definitions.html#retrieving-the-name-of-the-requested-entry

Member

mnapoli commented Feb 8, 2016

@klinki it's possible to retrieve the requested entry name, have a look here: http://php-di.org/doc/php-definitions.html#retrieving-the-name-of-the-requested-entry

@klinki

This comment has been minimized.

Show comment
Hide comment
@klinki

klinki Feb 8, 2016

Thanks :) I missed that. It works quite well with wildcards.

Is it possible to mix it with DI\object()->constructorParameter?

Right now I have this code:

 'CacheClients\*' => function(Container $container, RequestedEntry $entry) {
     $cacheFactory = $container->get(Factory::class);
     $className= $entry->getName();
     $cache = $cacheFactory->getCache($className);
     return new $className(
           $container->get(Connection::class),
            $cache,
            $container->get(LoggerInterface::class)
     ); 
}

It is fine now, when I have just a few dependencies, but for classes with lots of dependencies, this would get quite long.

It would be really awesome, if I could use constructorParameter. Maybe something like this:

'CacheClients\*' => object()->constructorParameter('cache', function(Container $container, RequestedEntry $entry) {
    $cacheFactory = $container->get(Factory::class);
    $className= $entry->getName();
    return $cacheFactory->getCache($className);
}

klinki commented Feb 8, 2016

Thanks :) I missed that. It works quite well with wildcards.

Is it possible to mix it with DI\object()->constructorParameter?

Right now I have this code:

 'CacheClients\*' => function(Container $container, RequestedEntry $entry) {
     $cacheFactory = $container->get(Factory::class);
     $className= $entry->getName();
     $cache = $cacheFactory->getCache($className);
     return new $className(
           $container->get(Connection::class),
            $cache,
            $container->get(LoggerInterface::class)
     ); 
}

It is fine now, when I have just a few dependencies, but for classes with lots of dependencies, this would get quite long.

It would be really awesome, if I could use constructorParameter. Maybe something like this:

'CacheClients\*' => object()->constructorParameter('cache', function(Container $container, RequestedEntry $entry) {
    $cacheFactory = $container->get(Factory::class);
    $className= $entry->getName();
    return $cacheFactory->getCache($className);
}
@mnapoli

This comment has been minimized.

Show comment
Hide comment
@mnapoli

mnapoli Feb 8, 2016

Member

@klinki It's not possible right now :) I'm afraid it might make the configuration quite complex though

Member

mnapoli commented Feb 8, 2016

@klinki It's not possible right now :) I'm afraid it might make the configuration quite complex though

@predakanga

This comment has been minimized.

Show comment
Hide comment
@predakanga

predakanga Mar 12, 2016

Contributor

Just wanted to throw a +1 behind this, with a use-case.

I'm using Doctrine DBAL as a DB layer, was hoping to be able to use their factory class and a lazy proxy, as follows:

use DI\factory;
use DI\get;
use Doctrine\DBAL\Driver\Connection;
use Doctrine\DBAL\DriverManager;

return [
    Connection::class => factory([DriverManager::class, 'getConnection')->factoryParameter('params', [
        'host' => get('db.host'),
        'port' => get('db.port'),
        'dbname' => get('db.name'),
        'user' => get('db.user'),
        'password' => get('db.pass)
    ])->lazy();
];

In reality, the closest I can come is by using a closure, like so:

use DI\factory;
use Doctrine\DBAL\Driver\Connection;
use Doctrine\DBAL\DriverManager;
use Interop\Container\ContainerInterface;

return [
    Connection::class => function(ContainerInterface $c) {
        return DriverManager::getConnection([
            'host' => $c->get('db.host'),
            'port' => $c->get('db.port'),
            'dbname' => $c->get('db.name'),
            'user' => $c->get('db.user'),
            'password' => $c->get('db.pass')
        ]);
    }
];

And even there there's no way of applying the lazy tag; that would require writing a wrapper class.

Contributor

predakanga commented Mar 12, 2016

Just wanted to throw a +1 behind this, with a use-case.

I'm using Doctrine DBAL as a DB layer, was hoping to be able to use their factory class and a lazy proxy, as follows:

use DI\factory;
use DI\get;
use Doctrine\DBAL\Driver\Connection;
use Doctrine\DBAL\DriverManager;

return [
    Connection::class => factory([DriverManager::class, 'getConnection')->factoryParameter('params', [
        'host' => get('db.host'),
        'port' => get('db.port'),
        'dbname' => get('db.name'),
        'user' => get('db.user'),
        'password' => get('db.pass)
    ])->lazy();
];

In reality, the closest I can come is by using a closure, like so:

use DI\factory;
use Doctrine\DBAL\Driver\Connection;
use Doctrine\DBAL\DriverManager;
use Interop\Container\ContainerInterface;

return [
    Connection::class => function(ContainerInterface $c) {
        return DriverManager::getConnection([
            'host' => $c->get('db.host'),
            'port' => $c->get('db.port'),
            'dbname' => $c->get('db.name'),
            'user' => $c->get('db.user'),
            'password' => $c->get('db.pass')
        ]);
    }
];

And even there there's no way of applying the lazy tag; that would require writing a wrapper class.

@mnapoli mnapoli referenced this issue Mar 15, 2016

Closed

PHP-DI 6 ideas #395

@mnapoli mnapoli changed the title from Feature Request: Factory Parameters to Factory Parameters May 15, 2016

predakanga added a commit to predakanga/PHP-DI that referenced this issue Jul 9, 2016

@mnapoli mnapoli closed this in 6a8483d Jul 14, 2016

@mnapoli mnapoli added this to the 5.4 milestone Aug 23, 2016

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment