In Zend Framework 2,
I have a controller class UserController
UserController is dependent on UserService
UserService is dependent on UserChangedListener
UserChangedListener is dependent on SomeOtherClass
SomeOtherClass is dependent on UserService
So here my UserController and SomeOtherClass are dependent on UserService.
I am getting error :
Circular dependency for LazyServiceLoader was found for instance
UserService
The above error (i.e. Circular dependency for LazyServiceLoader) occurred when I injected SomeOtherClass in UserChangedListener
And I have
"zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3",
UserControllerFactory.php
class UserControllerFactory implements FactoryInterface
{
public function __invoke(ContainerInterface $container, $requestedName, array $options = null)
{
$container = $container->getServiceLocator();
return new UserController(
$container->get(UserService::class)
);
}
public function createService(ServiceLocatorInterface $serviceLocator)
{
return $this($serviceLocator, UserController::class);
}
}
UserServiceFactory.php
class UserServiceFactory implements FactoryInterface
{
public function __invoke(ContainerInterface $container, $requestedName, array $options = null)
{
$service = new UserService(
$container->get(UserRepository::class)
);
$eventManager = $service->getEventManager();
$eventManager->attach($container->get(UserChangedListener::class));
return $service;
}
public function createService(ServiceLocatorInterface $serviceLocator)
{
return $this($serviceLocator, UserService::class);
}
}
UserChangedListenerFactory.php
class UserChangedListenerFactory implements FactoryInterface
{
public function __invoke(ContainerInterface $container, $requestedName, array $options = null)
{
$container = $container->getServiceLocator();
return new UserChangedListener(
$container->get(SomeOtherClass::class)
);
}
public function createService(ServiceLocatorInterface $serviceLocator)
{
return $this($serviceLocator, UserChangedListener::class);
}
}
SomeOtherClassFactory.php
class SomeOtherClassFactory implements FactoryInterface
{
public function createService(ServiceLocatorInterface $serviceLocator)
{
$rootLocator = $serviceLocator->getServiceLocator();
return new SomeOtherClass(
$rootLocator->get(UserService::class)
);
}
}
Looks like you have a legitimate circular dependency with UserService. The error message is telling you that the UserService cannot be created without the UserService.
This is really a problem introduced by the good practice of using dependency injection via the __construct method. By doing so, ZF2 will eager load a very large object graph into memory, when you have lots of related 'services' that have complex nested relationships you are bound to have circular dependencies.
ZF2 does offer Lazy Services as a solution to delay the instantiation of certain objects that as the developer you will need to decide on which ones (I would suggest UserChangedListener).
Alternatively to update your code, you could move the registration of the listener code outside of the UserServiceFactory and into the Module::onBootstrap() method.
namespace User;
class Module
{
public function onBootstrap(EventInterface $event)
{
$serviceManager = $event->getApplication()->getServiceManager();
// Create the user service first
$userService = $serviceManager->get(UserService::class);
$listener = $serviceManager->get(UserChangedListener::class);
// The create and attach the listener after user service has been created.
$userService->getEventManager()->attach($listener);
}
}
This would be required if you are using the 'aggregate' listeners. For simple event listeners you can also use the SharedEventManager which would prevent the overhead of loading the UserService in the above example.
UserService is dependent on SomeOtherClass through
UserChangedListener
SomeOtherClass is dependent on UserService
So basically for creation UserService you need first create SomeOtherClass instance, but for create it you need UserService instance to be created already.
I'm not sure about your architecture, but according to class names it looks bit wrong that you attach UserChangedListener in UserService.
Probably UserService should only fire events, and shouldn't know anything about listeners for this events. But again - that just idea, and for good answer you need to explain this dependencies bit more.
Related
There is an interface DCE, which is implemented by a class DCEImpl which has a dependency, say, string S, which it gets via its constructor.
The universe of S is limited, say S can only take values {'A','B','C'}.
There is an already existing Guice module that accepts the value of S in its constructor, and then binds the interface DCE to the correctly initialized version of DCEImpl.
public class DCEModule extends AbstractModule {
private final String s;
public DCEModule(String s){
this.s = s;
}
protected void configure() {
bind(DCE.class).toInstance(new DCEImpl(s));
}
}
Now I have a class C which needs a List<DCE> with all the 3 implementations (actually a lot more than 3, using 3 for example purpose).
I want to inject this list via Guice in C. To do that, I created a new module DCEPModule, which will provide a List<DCE> in this way:
#Provides
List<DCE> getDCE() {
for(String s: S){
Module m = new DCEModule(s);
install(m);
Injector injector = Guice.createInjector(m);
listDomains.add(injector.getInstance(DCE.class));
}
}
My problem is that I don't want to call a new injector in this module, because DCEPModule will be installed by a different module.
public class NewModule extends AbstractModule {
protected void configure() {
install(DCEPModule);
}
}
I want a way to get the List<DCE> without explicitly creating a new injector in DCEPModule.
You can achieve this by using a Multibinder (javadoc, wiki).
Here’s an example:
public class SnacksModule extends AbstractModule {
protected void configure(){
Multibinder<Snack> multibinder = Multibinder.newSetBinder(binder(), Snack.class);
multibinder.addBinding().toInstance(new Twix());
multibinder.addBinding().toProvider(SnickersProvider.class);
multibinder.addBinding().to(Skittles.class);
}
}
Now, the multibinder will provide a Set<Snack>. If you absolutely need a List instead of a Set, then you can add a method to your module like this:
#Provides
public List<Snack> getSnackList(Set<Snack> snackSet) {
return new ArrayList(snackSet);
}
You can add implementations to the same Multibinding in more than one module. When you call Multibinder.newSetBinder(binder, type) it doesn’t necessarily create a new Multibinding. If a Multibinding already exists for for that type, then you will get the existing Multibinding.
I have a view helper that acts as a factory by returning an entity-specific renderer.
I would like the factory to implement the FactoryInterface and MutableCreationOptionsInterface, so i can return different renderers depending on the type of object passed to it, eg:
$serviceLocator->get('entityRenderer', ['entity' => $user]); // returns UserRenderer
$serviceLocator->get('entityRenderer', ['entity' => $admin]); // returns AdminRenderer
$serviceLocator->get('entityRenderer'); // returns DefaultRenderer
However, there is no access to the servicelocator from within a view, and the factory view helper i have created is called using it's __invoke method. This means the type check is occuring here and returning the specific renderer without using the service manager, which is not desirable. eg
class EntityRendererFactory extends AbstractHelper{
public function __invoke(Entity $entity){
if($entity instanceof User){
$renderer = new UserRenderer($entity);
$renderer->setView($this->view);
}
if($entity instanceof Admin){
$renderer = new AdminRenderer($entity);
$renderer->setView($this->view);
}
if($renderer){
return $renderer;
}
}
}
Note how this "factory" is having to extend AbstractHelper (view) simply just to pass on the instance of the current view.
My "ideal" would be something like this (proof of concept, not working code):
use Zend\ServiceManager\FactoryInterface;
use Zend\ServiceManager\MutableCreationOptionsInterface;
class EntityRendererFactory implements FactoryInterface, MutableCreationOptionsInterface{
protected $options = [];
protected $renderers = [];
public function createService(ServiceLocatorInterface $serviceLocator){
$this->addRenderer($serviceLocator->get('ViewHelperManager')->get('UserRenderer'), User::class);
$this->addRenderer($serviceLocator->get('ViewHelperManager')->get('AdminRenderer'), Admin::class);
$this->addRenderer($serviceLocator->get('ViewHelperManager')->get('DefaultRenderer'), 'default');
if(!array_key_exists('entity', $this->options)){
return $this->getRenderer('default');
}
$entity = $this->options['entity'];
foreach($this->getRenderers() as $renderer){
if($renderer->canRender($entity)){
return $renderer;
}
}
//Alternatively, more specific hard-coding interface type check
if($entity instanceof User){
return $serviceLocator->get('ViewHelperManager')->get('UserRenderer');
}
//etc.
}
public function setCreationOptions(array $options){
$this->options = $options;
}
}
...but with the above demonstration, i would be unsure how to call it from within the view (as view helpers are typically called from their __invoke method and not from the service manager)?
(With an eye to migrating to ZF3, i do not want to use the ServiceLocatorAwareInterface).
You can declare your viewhelperin the factories section of module.config.php as :
return [
...
'view_helpers' => [
'factories' => [
'entityRenderer' => EntityRendererFactory::class
]
],
...
]
then use the following model :
class EntityRendererFactory extends AbstractHelper implement FactoryInterface
{
private $sm;
public function createService(ServiceLocatorInterface $serviceLocator){
$this->sm = $serviceLocator;
return $this;
}
public function _invoke() {
// your code
}
}
Personally, I start by creating a specific service manager containing only the necessary classes and it is this one that I record in the class.
$this->sm = $servicelocator->getServiceLocator()->get('mySpecificSM');
Incidentally, this model does not work under ZF3 where you need a factory class that builds the viewhelper class. A change not too complicated however.
I am writing a new ZF2 app. I have noticed that ServiceLocator usage pattern of calling services "from anywhere" has been deprecated from ZF3. I want to write code in mind for ZF3.
I was able to set up my Controller to call all dependencies at constructor time. But that means loading i.e. Doctrine object upfront before I need it.
Question
How do I set it up so that it is only loaded when I need it immediately? (lazy-loaded). I understand that ZF3 moves loading to Controller construction, which makes it not apparent as to how to load something Just-In-Time.
Old Code
class CommissionRepository
{
protected $em;
function getRepository()
{
//Initialize Doctrine ONLY when getRepository is called
//it is not always called, and Doctrine is not always set up
if (! $this->em)
$this->em = $this->serviceLocator->get('doctrine');
return $this->em;
}
}
Current Code after Refactor of ServiceLocator pattern
class CommissionRepository
{
protected $em;
function getRepository()
{
return $this->em;
}
function setRepository($em)
{
$this->em = $em;
}
function useRepository($id)
{
return $this->em->find($id);
}
}
class CommissionControllerFactory implements FactoryInterface
{
public function createService(ServiceLocatorInterface $serviceLocator)
{
$parentLocator = $controllerManager->getServiceLocator();
// set up repository
$repository = new CommissionRepository();
$repository->setRepository($parentLocator->get('doctrine'));
// set up controller
$controller = new CommissionController($repository);
$controller->setRepository();
return $controller;
}
}
class CommissionController extends AbstractActionController
{
protected $repository;
public function setRepository(CommissionRepository $repository)
{
$this->repository = $repository;
}
public function indexAction()
{
//$this->repository already contains Doctrine but it should not
//I want it to be initialized upon use. How?
//Recall that it has been set up during Repository construction time
//and I cannot call it from "anywhere" any more in ZF3
//is there a lazy loading solution to this?
$this->repository->useRepository();
}
If you don't have any valid/strong reason to instantiate a custom entity repository, you should prefer extending of Doctrine\ORM\EntityRepository in your repositories like CommissionRepository. For example;
use Doctrine\ORM\EntityRepository;
class CommissionRepository extends EntityRepository
{
// No need to think about $em here. It will be automatically
// injected by doctrine when you call getRepository().
//
function fetchCommissionById($id)
{
// You can easily get the object manager directly (_em) or
// using getEntityManager() accessor method in a repository
return $this->_em->find($id);
}
}
By this way, entity manager will be automatically injected to the repository on construction when you call the $em->getRepository('App\Entity\Commission') method.
I assume that you already have a Commission entity in your app's Entity namespace:
<?php
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity(repositoryClass="App\Repo\CommissionRepository")
* #ORM\Table
*/
class Commission
{
}
Then you can simplify the injecting process of the repository in your factory something like:
// ZF2 Way
class CommissionControllerFactory implements FactoryInterface
{
public function createService(ServiceLocatorInterface $services)
{
$em = $services->getServiceLocator()->get('doctrine');
$repository = $em->getRepository('App\Entity\Commission');
return new CommissionController($repository);
}
}
UPDATE - With the release of Service Manager V3, FactoryInterface has been moved to Zend\ServiceManager\Factory namespace (1), factories are literally invokables (2) and works with any container-interop compatible DIC (3) Updated factory would be like below:
// ZF3 Way
use Zend\ServiceManager\Factory\FactoryInterface;
use Interop\Container\ContainerInterface;
use Doctrine\ORM\EntityManager;
class CommissionControllerFactory implements FactoryInterface
{
public function __invoke(ContainerInterface $dic, $name, array $options = null) {
$em = $dic->get(EntityManager::class);
$repository = $em->getRepository('App\Entity\Commission');
return new CommissionController($repository);
}
}
For the question; as of marcosh's said, Lazy Services are way to go to create services when need it immediately. ZF3 will use the zend-servicemanager 3.0 component when released. (Currently zend-expressive uses it) As of servicemanager v3 you can create some proxied services by defining lazy_services and delegators in your service configuration:
'factories' => [],
'invokables' => [],
'delegators' => [
FooService::class => [
FooServiceDelegatorFactory::class,
],
],
'lazy_services' => [
// map of service names and their relative class names - this
// is required since the service manager cannot know the
// class name of defined services up front
'class_map' => [
// 'foo' => 'MyApplication\Foo',
],
// directory where proxy classes will be written - default to system_get_tmp_dir()
'proxies_target_dir' => null,
// namespace of the generated proxies, default to "ProxyManagerGeneratedProxy"
'proxies_namespace' => null,
// whether the generated proxy classes should be written to disk or generated on-the-fly
'write_proxy_files' => false,
];
Also, starting with service manager v3 factories are compatible with the ContainerInterface. For the forward-compatibility, you may want to keep both __invoke() and createService() methods in your factories for a smooth migration.
In the end, your ZF3 compatible factory may look like:
class CommissionControllerFactory implements FactoryInterface
{
public function __invoke(ContainerInterface $container, $name, array $options = null)
{
$em = $container->get('doctrine');
$repository = $em->getRepository('App\Entity\Commission');
return new CommissionController($repository);
}
public function createService(ServiceLocatorInterface $container, $name = null, $requestedName = null)
{
return $this($container, $requestedName, []);
}
}
Hope it helps.
I am using Guice for assisted injection. Here is a standard scenario:
Class TargetType {
#Inject
TargetType(#Assisted Param1 assistedParam, InjectedType injectedType)
{
/* ... */
}
}
Class InjectedType {
#Inject
injectedType(OtherInjectedType otherInjectedType)
{
/* ... */
}
}
Now I could use Guice factory to call TargetTypeFactory.create(assistedArg /* instanceof Param1 */) and happily obtain my instance of TargetType with instance of InjectedType injected by Guice.
My problem is: what if I want for the InjectedType to have reference to the instance of the TargetType being created? In other words, I want:
Class TargetType {
#Inject
TargetType(#Assisted Param1 assistedParam, InjectedType injectedType)
{
}
}
Class InjectedType {
#Inject
injectedType(/* How to get this? -> */ TargetType constructedTargetType, OtherInjectedType otherInjectedType)
{
}
}
My current workaround is quite ugly: I create a TargetType manually without the InjectedType, then I use InjectedTypeFactory to get the InjectedType instance and call the setInjectedType(InjectedType) method on the TargetType instance. Ungh!
Interesting problem. I don't think that there's a totally clean solution to this, but maybe this solution based on child injectors will work for you:
First, create a custom guice binding annotation for your own use:
#Retention(RetentionPolicy.RUNTIME)
#Target({ElementType.FIELD, ElementType.PARAMETER})
#BindingAnnotation
public #interface JamrozikParam {}
Then, change TargetType to look like this:
class TargetType {
#Inject
TargetType(#JamrozikParam Param1 assistedParam, InjectedType injectedType)
{
}
}
Leave InjectedType as it was.
Now, this is how you get an instance of TargetType:
Injector topInjector = Guice.createInjector(/* Your modules here */);
// Note that the modules used there can't mention TargetType or InjectedType
// Now many lines later
Param1 myP1val;
myP1val = // ... however that was computed
Injector childInjector = topInjector.childInjector(new Param1Module(myP1val));
TargetType t1 = childInjector.getInstance(TargetType.class);
Where Param1Module is a class that looks like:
public class Param1Module extends AbstractModule {
private final Param1 p1val;
public Param1Module(Param1 p1val) {this.p1val = p1val;}
protected void configure() {
bind(Param1.class).annotatedWith(JamrozikParam.class).toInstance(p1val);
bind(TargetType.class).in(Singleton.class);
bind(InjectedType.class).in(Singleton.class);
}
}
Note that in order for this to work, Guice is going to be creating a proxy to work around the circular dependency. I suggest that you make its job of proxy creation easier and make InjectedType implement an interface, have TargetType inject that interface instead of injecting InjectedType directly, and then bind that interface to InjectedType. Otherwise, guice will have to do horrible stuff with bytecode rewriting on the fly and that's just asking for trouble.
I strongly urge you to look at your whole problem and refactor things so that you don't need to do this. Maybe your two objects don't need to know about each other in their constructors? For example, maybe you could pass them references to each other as method parameters by going through some supervisor object:
class TargetType {
#Inject
TargetType(#Assisted Param1 assistedParam)
{
}
void ttMethod1(InjectedType injected, String other) { }
// other methods
}
class InjectedType {
#Inject
injectedType(OtherInjectedType otherInjectedType)
{
}
void injMethod1(TargetType constructedTarget, String other) { }
// other methods
}
class TargetTypeSupervisor {
private TargetType target;
private InjectedType injected;
#Inject TargetTypeSupervisor(#Assisted Param1 assistedParam, InjectedType injected) {
this.injected = injected;
this.target = new Target(assistedParam);
}
void ttMethod1(String other) { target.ttMethod1(injected, other); }
void injMethod1(String other) { injected.injMethod1(target, other); }
// other methods as needed
}
I'm trying to use Service Manager on my entity class but I don't know the best way to do that.
It's easy on a controller because we can call service manager with : $this->getServiceLocator();
But, in my entity class, even if I implements ServiceLocatorAwareInterface i can retieve ServiceManager because my entity class isn't call with the service manager :
So what is the best way :
1 - Pass serviceManager in my entity class from my controller
2 - Using ServiceManager to build my entity class
3 - ... ?
To best understand my problem, that's my code which doesn't work :
My entity class:
class Demande extends ArraySerializable implements InputFilterAwareInterface {
/../
public function getUserTable() {
if (! $this->userTable) {
$sm = $this->getServiceLocator();//<== doesn't work !
$this->userTable = $sm->get ( 'Application\Model\UserTable' );
}
return $this->userTable;
}
I wouldn't inject the ServiceManager into your model (although you can). I would rather get the ServiceManager to build your Model for you, and inject anything you need directly into the model.
Service Config:
'factories' => array(
'SomethingHere' => function($sm) {
$model= new \My\Model\Something();
return $model;
},
'\My\Model\Demande' => function($sm) {
$model= new \My\Model\Demande();
/**
* Here you use the SM to inject any dependencies you need
* into your model / service what ever..
*/
$model->setSomething($sm->get('SomethingHere'));
return $model;
},
/**
* Alternatively you can provide a class implementing
* Zend\ServiceManager\FactoryInterface
* which will provide an instance for you instad of using closures
*/
'\My\Model\DemandeDefault' => '\My\Model\DemandeFactory',
Place any of your dependencies inside the Service Manager Config, and then use that to inject any dependencies into your models, services etc for you.
An example factory class if you want to use the factory method rather than closures:
DemandeFactory.php
use Zend\ServiceManager\FactoryInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
class DemandeFactory implements FactoryInterface
{
/**
* Create a new Instance
*
* #param ServiceLocatorInterface $serviceLocator
* #return Demande
*/
public function createService(ServiceLocatorInterface $serviceLocator)
{
$config = $serviceLocator->get('Config'); // if you need the config..
// inject dependencies via contrustor
$model = new \My\Model\Demande($serviceLocator->get('SomethingHere'));
// or using setter if you wish.
//$model->setSomething($serviceLocator->get('SomethingHere'));
return $model;
}
}
An example Model you are trying to instantiate via the Service Manager.
Demande.php
class Demande
{
protected $_something;
/**
* You can optionally inject your dependancies via your constructor
*/
public function __construct($something)
{
$this->setSomething($something);
}
/**
* Inject your dependencies via Setters
*/
public function setSomething($something)
{
$this->_something = $something;
}
// Something will be injected for you by the Service Manager
// so there's no need to inject the SM itself.
}
In your Controller:
public function getDemande()
{
if (! $this->_demande) {
$sm = $this->getServiceLocator();
$this->_demande = $sm->get ('\My\Model\Demande');
}
return $this->_demande;
}
You could inject the SergiceManager/ServiceLocator into your models but then your models will depend on the ServiceLocator.