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 likeCommissionRepository
. For example;By this way, entity manager will be automatically injected to the repository on construction when you call the
$em->getRepository('App\Entity\Commission')
method.Then you can simplify the injecting process of the repository in your factory something like:
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:
Also, starting with service manager v3 factories are compatible with the ContainerInterface. For the forward-compatibility, you may want to keep both
__invoke()
andcreateService()
methods in your factories for a smooth migration.In the end, your ZF3 compatible factory may look like:
Hope it helps.