This question can be viewed through a prism of ZF2 + Doctrine + MVC programming practices, or it can be viewed through just an OOP perspective.
My concern is about Separation of Concerns, and on removing dependencies.
I am using code in my controllers that goes something like this:
class MyController
{
private $em; //entityManager
function __construct()
{
$this->em = DoctrineConnector::getEntityManager();
}
function indexAction()
{
//Input
$inputParameter = filter_input(...);
//request for Data
$queryBuilder = $this->em->createQuery(...)
->setParameter('param', $inputParameter);
$query = $queryBuilder->getQuery();
//$services is the user-defined data type requested
$services = $query->getResult();
//use data to produce a view model
$view = new ViewModel();
$view->setVariables(array('services' => $services));
return $view;
}
}
I am not entirely comfortable with the above and wanted a second opinion. For one, my EntityManager
is part of the class, so my class is cognizant of the entity manager construct, when I think it should not be a part of the controller. Do I perhaps use a Factory
or Builder
design pattern to help me create MyController
class?
If I do, I can move my em
(entityManager) construct into the Factory pattern and create and populate my MyController
inside the Factory. Then, the MyController
can have a private variable $services
instead.
i.e.
class MyController
{
private $services;
function setServices($services)
{
$this->services = $services;
}
function indexAction()
{
//use data to produce a view model
$view = new ViewModel();
$view->setVariables(array('services' => $this->services));
return $view;
}
}
class MyFactoryMethod
{
function createMyController()
{
//Input
$inputParameter = filter_input(INPUT_GET...);
//request for Data
$queryBuilder = $this->em->createQuery(...)
->setParameter('param', $inputParameter);
$query = $queryBuilder->getQuery();
//$services is the user-defined data type requested
$services = $query->getResult();
//create and return MyController instance
$controller = new MyController();
$controller->setServices($services);
return $controller;
}
}
I typically tried to do this PHP's mysql
extension to remove dependency on data out of my various objects. I am using Doctrine2 now which is an ORM, and wondering if I should keep doing the same thing (namely preferring 2nd example rather than the first...
Question:
I can write code both ways. It works essentially the same. My question is -- is the code, as it is written in my 2nd example preferred more than the code as it is written in my first?
Notes / Clarifications:
In my case variable $services
is a domain-specific variable (not ZF2's ServiceLocator
). i.e. think of MyController
as a controller for business-specific "services".
I am not harnessing full power of ZF2 with configs, routers, events, and everything. I am using ZF2 modules on an existing legacy codebase on as-needed basis.
There are two different approaches to this problem that are provided by ZF2.
Use the
ServiceLocator
to retrieve theEntityManager
via a Factory.In
Module.php
, add an anonymous function or Factory.In your Controller
Create an
Initializer
andAwareInterface
to inject theEntityManger
into your controllers.The
AwareInterface
can be added to any class which is initialized by theServiceManager
.The
Initializer
is run when services are initialized by theServiceManager
. A check is performed to so if$instance
is aEntityManagerAwareInterface
.Next add the
Initializer
toModule.php
The advantage of going the
Initializer
route is there is a one time setup. Any class that implements theEntityManagerAwareInterface
will have theEntityManager
injected when the class is initialized.When your controller has hard dependencies I would suggest to use the common ZF2 solution by creating the controller and injecting the dependency in a factory instance and registering the controller under the
'factories'
key in your'controllers'
config array.In your
module.config.php
In your controller I would set hard dependency in the
__construct
method. Like this you prevent the controller from ever being instantiated without your dependencies (it will throw an exception).Never inject something like
$services
(if this is aServiceLocator
) from which you will pull the actual dependencies since it is not clear what the class actually needs. It will be harder to understand for other developers and it is also hard to test since you cannot set mocks for your individual dependencies so easily.Your Controller class:
Your Factory: