Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Idea: override the definition of an interface for a specific target-class #875

Open
rx-io opened this issue Jan 12, 2024 · 3 comments
Open

Comments

@rx-io
Copy link

rx-io commented Jan 12, 2024

It's very common to have multiple different logs within a single application, especially if it's a larger application. Therefore it would be nice if one could override an interface-definition just for a specific class.

Example: let's say we have two services: ServiceA and ServiceB. Both services demand a LoggerInterface via constructor but the actual logfiles have to be different (let's say "a.log" and "b.log"). Now it would be cool if I could express this requirement with a definition that looks something like that:

[
  'app.log' => \DI\factory(fn() => new FileLogger('app.log')),

  'a.log'   => \DI\factory(fn() => new FileLogger('a.log')),
  'b.log'   => \DI\factory(fn() => new FileLogger('b.log')),

  // Use "app.log" as default
  LoggerInterface::class => \DI\get('app.log'),

  // Use "a.log" for ServiceA
  ServiceA::class => \DI\autowire()
    ->inject(LoggerInterface::class, \DI\get('a.log')),

  // Use "b.log" for ServiceB
  ServiceB::class => \DI\autowire()
    ->inject(LoggerInterface::class, \DI\get('b.log')),
]

I'd really like to see such a feature in php-di.

My current solution to this is to move this very specific configuration into my service-classes, which feels very unclean to me.

EDIT: I forgot to mention that using constructorParameter also works here and helps keeping the class clean.

class ServiceA {
  public function __construct(
    #[Inject('a.log')]
    private LoggerInterface $log
  ){}
}

How would you guys solve this "problem"?

@mnapoli
Copy link
Member

mnapoli commented Jan 12, 2024

Just to mention an alternative (typing out of memory, might not be 100% exact):

[
  'app.log' => \DI\factory(fn() => new FileLogger('app.log')),

  'a.log'   => \DI\factory(fn() => new FileLogger('a.log')),
  'b.log'   => \DI\factory(fn() => new FileLogger('b.log')),

  // Use "app.log" as default
  LoggerInterface::class => \DI\get('app.log'),

  // Use "a.log" for ServiceA
  ServiceA::class => \DI\autowire()
    ->parameter('log', \DI\get('a.log')),

  // Use "b.log" for ServiceB
  ServiceB::class => \DI\autowire()
    ->parameter('log', \DI\get('b.log')),
]

WDYT?

@rx-io
Copy link
Author

rx-io commented Jan 14, 2024

Thanks for your reply. Appreciate it!

This is actually my current solution in older projects. It kinda solves the problem and keeps the configuration aspect outside of the class. What I don‘t like about it is that it relies on the very specific parameter name. But maybe I‘m just too autistic here 😬

Do you think my idea is worth considering in future versions or is it too much off the rails / too niche?

@rx-io rx-io closed this as completed Jan 14, 2024
@rx-io rx-io reopened this Jan 14, 2024
@mnapoli
Copy link
Member

mnapoli commented Jan 14, 2024

I don't think what you suggest is a bad idea. Laravel supports that for example.

I just don't have time to invest into this right now, but let's keep this issue open 👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants