If you have the entity manager within the controller you introduce a coupling of your "domain logic" (your database queries) where they become inseparable form the application logic (the controller should only read the request and return the correct response). This coupling makes it considerably harder to reuse and maintain code.
One solution would be to create "services" that are injected into the controller. Services encapsulate the business logic (such as database queries) and provide a well defined API to the controller. The nice thing is that should the business logic change at any point (it always will); you would only need to change the implementation of said services and the controller will continue to function.
ZF2 is very flexible and there are many ways to accomplish the same task. Personally I do the following:
Services
Services are not mean to encapsulate just one entity; they should encapsulate all entities that are required to perform that specific task. This completely depends on what the service is trying to do. More complex services could require other services for instance.
In my implementation I have an abstract class called AbstractEntityService
this class is extended for all services require persistence (anything that needs the database).
The class is quite long for here, however the key bits are bellow.
abstract class AbstractEntityService
extends Service\AbstractService
implements EntityMapperAwareInterface, FormProviderInterface
{
public function __construct(
EntityMapper $entityMapper,
FormElementManager $formElementManager)
{
$this->entityMapper = $entityMapper;
$this->formElementManager = $formElementManager;
$this->getEventManager()->addIdentifiers(array(__CLASS__));
parent::__construct();
}
public function getForm($name)
{
return $this->formElementManager->get($name);
}
public function create(Entity\EntityInterface $entity)
{
$this->triggerEvent(static::EVENT_CREATE, array('entity' => $entity));
$entity = $this->entityMapper->insert($entity);
$this->triggerEvent(static::EVENT_CREATE_POST, array('entity' => $entity));
return $entity;
}
Notice the EntityMapper
and FormElementManager
are injected - No ObjectManager
or ServiceLocator
here.
EntityMapper
Is simply a thin layer around the object manager; this allows us to swap out the EntityManager
for the DocumentManager
if we do from MySQL
to MongoDB
for example.
interface MapperInterface
{
public function getObjectManager();
public function setObjectManager(ObjectManager $objectManager);
public function getRepository($className);
public function insert(EntityInterface $entity);
public function save(EntityInterface $entity);
public function delete(EntityInterface $entity);
public function flush();
}
So an example of a concrete service would be:
class ListService extends AbstractEntityService
{
public function __construct(
EntityMapper $entityMapper,
FormElementManager $formElementManager,
ListRepository $listRepository
){
parent::__construct($entityMapper, $formElementManager);
$this->listRepository = $listRepository;
}
protected function init(EventManagerInterface $eventManager){
parent::init($eventManager);
$eventManager->attach(static::EVENT_CREATE, array($this, 'createList'));
}
public function createList(EventInterface $event)
{
$list = $event->getParam('entity');
if (! $list instanceof Entity\ValueList) return;
$name = $list->getName();
if (empty($name)) {
$name = $this->formatName($list->getTitle());
$list->setName($name);
}
}
ListController
The controller then simply uses the service (in the above example it is a "ListService")
class ListController extends AbstractActionController {
public function __construct(
ListService $listService
){
$this->listService = $listService;
}
public function createAction(){
// validate request/form data...
$form = $this->listService->getListCreateForm();
$list = $this->listService->create($form->getData());
// return view...
}
Wow; bit longer than planned but hope it helps.