How Service Container create object declared in services.yml? - dependency-injection

Consider this code:
public function showActiveJobsAction($slug)
{
$em = $this->getDoctrine()->getEntityManager();
$category = $em->getRepository('JobeetBundle:Category')->findBySlug($slug);
if (! $category) {
throw $this->createNotFoundException('Unable to find Category entity.');
}
$jobService = $this->container->get('job_service');
$category = $jobService->populateCategoryByItsActiveJobs($category);
return $this->render('JobeetBundle:Category:jobs.html.twig', array(
'category' => $category,
));
}
job_service need JobeetBundle:Category repository to work. The repository is passed to service constructor. It's all defined in services.yml
So in this case I end up with two instance of JobeetBundle:Category repository class?
If yes how can I change my design to do it better?
Probably it's better to create code just like:
$jobService->getCatetoryWithActiveJobsByItsSlug($slug)
but I still wonder if container looks for object existance before create it?

When you get a service from the container, by default, you get always the same instance. It is also the same instance when this service is injected into another one.
So you don't have two worry, you get only one instance of the service job_service.
Here is an extract from the Symfony2 book, chapter Service Container:
As an added bonus, the Mailer service is only created once and the same instance is returned each time you ask for the service. This is almost always the behavior you'll need (it's more flexible and powerful).
Hope that helps!

In general, you wont get duplicate repositories (or services) in Symfony2 so no worries there.
This:
$jobService->getCatetoryWithActiveJobsByItsSlug($slug)
Is a better approach simply because it hides the entity manager/repository stuff from your controller. You could (in theory at least) swap out the entire Doctrine2 engine with something else and still have your controller code work.

Related

Is it possible to pass arguments to Symony2 container constructor

When creating an service container in Symfony2 you mostly pass "static" arguments (like other classes etc.) to its constructor.
However I'd like to create a factory and therefore I need to be able to pass an dynamic argument to the service constructor.
The examples I found ( e.g. http://symfony.com/doc/current/cookbook/service_container/factories.html) are all ending up using static arguments as argument.
But what do I have to do, if I want my factory to decide which object to return based on (for example) user input?
I have some problems understanding why service factory should not work on your case. Do you need to return different service class unrelated to each other?
What I see from the factory example is that you could do something like this:
class NewsletterFactory
{
public function __constructor(...)
{
// Receive arguments needed to create the service below
}
public function get()
{
// Say the variable $userInput exists and is derived from constructor
if ($userInput === 'string')
return new NewsletterManager($dynamicArgument1);
if ($userInput === 'integer')
return new AnotherNewsletterManager($dynamicArgument2);
return new DefaultNewsletterManager();
}
}
Now, if this doesn't fit your needs. You can also create a service say CustomFactory that returns what you need. What is returned is not directly a service, so you can do whatever you want. But this would prevent you from requesting the objects created by CustomFactory from the dependency container.
Similar to that is the FormFactory. It is the factory used to instantiate form type. But the FormFactory is more powerfull since it is coupled with a dependency injection tag and a compiler pass, which register each types into the dependency injection system so they can be retrieved on their own. I don't exactly all the internals of the Form component but I think it could solve your problem if other methods do not.
Regards,
Matt

What benefits does a DI container provide over a factory?

There are many discussions around here about this, but none seem to really answer the question.
I'm currently considering using the Symfony 2 service container. The more I look at it though, the more it seems like I could just do the same thing with a service factory. Consider the following example:
services.yml
services:
my_mailer:
class: Acme\Mailer
arguments: [sendmail]
newsletter_manager:
class: Acme\Newsletter\NewsletterManager
arguments: [#my_mailer]
Now to get a newsletter manager, I do something like:
// this has been configured with the above yml file
$serviceContainer = new ServiceContainer();
$newsletterManager = $serviceContainer->get("newsletter_manager");
However, consider the following factory style code:
class ServiceContainer
{
public function getMyMailer()
{
return new Acme\Mailer("sendmail");
}
public function getNewsletterManager()
{
return new Acme\Newsletter\NewsletterManager($this->getMyMailer());
}
}
And then using it:
$serviceContainer = new ServiceContainer();
$newsletterManager = $serviceContainer->getNewsletterManager();
Is there something I'm missing here? Because I don't really see the advantage to using a DI container if a factory can do all of that for me.
I have heard using factories causes your code to "depend on the factory". I either don't understand this argument or the person arguing against factories is confused. Only the top level class (the composition root) would have a reference to the factory, just like it would be the only one with the reference to the DI container.
Is there something I'm missing here? Because I don't really see the
advantage to using a DI container if a factory can do all of that for
me.
Yes - you are using your DI container using the Service Locator (anti-)pattern instead of injecting your dependencies into your classes. Your class should not have to reference your DI container at all.
Instead there should be just a single aggregate root where you create the container and resolve all dependencies. Once you have resolved your top level class instance all of its dependencies (and the dependencies of the dependencies and so on) are then resolved using the IoC container using the Hollywood Principle - when class instances are created all of its dependencies are passed in, the instance never asks for a dependency itself.

Register named instance in ObjectFactory

I try to register named instances in structure map. But my trials are not successful.
What is the correct way?
Currently I'm doing it like this:
IService bus = CreateInstanceOfServiceBus();
ObjectFactory.Configure(cfg => cfg.For<IServiceBus>()
.AddInstances(x =>x.IsThis(bus)
.Named("foobar"))
.Singleton());
But when I try to get the named instance, nothing is found:
ObjectFactory.TryGetInstance<IServiceBus>("foobar");
Thanks in advance.
Best regards.
Joachim
You can simply register as follows
IServiceBus bus = CreateInstanceOfServiceBus();
ObjectFactory.Initialize(x => x.ForSingletonOf<IServiceBus>().Use(bus).Named("bus"));
Now you can resolve the instance either like this
ObjectFactory.GetNamedInstance<IServiceBus>("bus");
or
ObjectFactory.TryGetInstance<IServiceBus>("bus");
You are using the Configure method which simply adds another configuration to the container. Consider if you will change to Initialize, which initializes a new configuration.

Unity Container ResolutionFailedException when the mapping is correct in the config file

I was using a ServiceLocator which i was DIing with Unity
public ServiceLocator(IUserStore userStore, IProdcutsStore productsStore, ...etc) {}
public IUserStore UserStore
{
get { return userStore; }
}
This all worked fine, but I wanted lazy instantiation of the repositories as they have quite sparse use.
So my ServiceLocator now looks like
public ServiceLocator(IUnityContainer container) {}
public IUserStore UserStore
{
get { return (IUserStore)container.Resolve(typeof(IUserStore)); }
}
// ...etc
I'm now getting a really unhelpful ResolutionFailedException error
Resolution of the dependency failed,
type =
"DomainModel.DataServices.Interface.IUserStore",
name = "". Exception message is: The
current build operation (build key
Build
Key[DomainModel.DataServices.Interface.IUserStore,
null]) failed: The current type,
DomainModel.DataServices.Interface.IUserStore,
is an interface and cannot be
constructed. Are you missing a type
mapping? (Strategy type
BuildPlanStrategy, index 3)
Telling me my interface type cannot be instantiated because it is an interface is pretty pointless. I know it's an interface, that's why the container is supposed to be resolving it for me!
Anyway, the point to note here is that I know the type mapping in the config is fine, as when I was injecting the type interface directly instead of trying to lazy load, it resolved it with no problems.
What am I missing that means something somewhere has to change in order to lazy load this way?
Update: I am guessing what's happening here, is that when I DI the container into the ServiceLocator, the "main" container is instantiating a new container each time which is then not configured properly. I think maybe I need some way to specify that I was to pass this in as the container, rather than resolve it with a new instantiation.
You're going in a somewhat wrong direction... At first you've had a testable class that declared its dependencies in the constructor and you turned it into non-so-testable, asking for "something" inside a container... No good =(
You should either implement some factory interface for your expensive object and require it in the constructor, or (if you can) switch to Unity 2.0 and use the Automatic Factories:
public ServiceLocator(Func<IUserStore> userStoreBuilder)
//...
public IUserStore UserStore
{
get { return userStoreBuilder(); }
}
If you want to only create the instance of that object once, you can add cahcing to that property, or with .NET 4.0 you can try asking Lazy in the constructor.
P.S. Oh, yes. And answering your particualr question =) If you still want to inject an instance of your container somewhere else, you need to first register it inside itself =)
container.RegisterInstance<IUnityContainer>(container);
Fix (see comments) DO NOT register a Unity container inside itself, this will cause StackOverflowException in container.Dispose(), the correct instance will be injected as a dependency without the registration.

How to avoid having injector.createInstance() all over the place when using guice?

There's something I just don't get about guice: According to what I've read so far, I'm supposed to use the Injector only in my bootstrapping class (in a standalone application this would typically be in the main() method), like in the example below (taken from the guice documentation):
public static void main(String[] args) {
/*
* Guice.createInjector() takes your Modules, and returns a new Injector
* instance. Most applications will call this method exactly once, in their
* main() method.
*/
Injector injector = Guice.createInjector(new BillingModule());
/*
* Now that we've got the injector, we can build objects.
*/
RealBillingService billingService = injector.getInstance(RealBillingService.class);
...
}
But what if not all Objects I ever need can be created during startup? Maybe I want to respond to some user interaction when the application is running? Don't I have to keep my injector around somewhere (e.g. as a static variable) and then call injector.getInstance(SomeInterface.class) when I need to create a new object?
Of course spreading calls to Injector.getInstance() all over the place seems not to be desirable.
What am I getting wrong here?
Yes, you basically only should use the Injector to create get the instance for the root-object. The rest of the application shouldn't touch the Guice-Container. As you've noticed, you still need to create some objects when required. There are different approaches for doing that, each suitable for different needs.
Inject a Provider
Provider is a interface from Guice. It allows you to request a new instance of a object. That object will be created using Guice. For example.
class MyService{
private Provider<Transaction> transactionProvider;
public MainGui(Provider<Transaction> transactionProvider){
this.transactionProvider = transactionProvider;
}
public void actionStarted(){
Transaction transaction = transactionProvider.get();
}
Build a Factory
Often you need some kind of factory. This factory uses some injected services and some parameters and creates a new object for you. Then you use this factory for new instances. Then you inject that factory and use it. There also help for this with the AssistedInject-extension
I think with these two possibilities you rarely need to use the Guice-Injector itself. However sometimes is still appropriate to use the injector itself. Then you can inject the Injector to a component.
To extend on the answer Gamlor posted, you need to also differentiate between the object types you are using.
For services, injection is the correct solution, however, don't try to always make data objects (which are generally the leafs in your object graph) injectable. There may be situations where that is the correct solution, but injecting a Provider<List> is probably not a good idea. A colleague of mine ended up do that, it made the code base very confusing after a while. We just finished cleaning it all out and the Guice modules are much more specific now.
In the abstract, I think the general idea is that if responding to user events is part of the capabilities of your application, then, well...
BillingService billingService = injector.getInstance(BillingService.class);
billingService.respondToUserEvent( event );
I guess that might be a little abstract, but the basic idea is that you get from Guice your top-level application class. Judging from your question, I guess that maybe BillingService isn't your top-level class?

Resources