How do I inject the service manager into a Doctrine repository to allow me to retrieve the Doctrine Entity Manager?
I using the ZF2-Commons DoctrineORMModule and are trying to implement the repository example listed in the Doctrine Tutorial (bottom of tutorial in link below):
http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/tutorials/getting-started.html
However, I keep getting a message "Fatal error: Call to a member function get() on a non-object in C:\zendProject\zf2 ... ", which suggests that I do not have a working instance of the service locator.
My Doctrine repository looks like this:
namespace Calendar\Repository;
use Doctrine\ORM\EntityRepository,
Calendar\Entity\Appointment,
Calendar\Entity\Diary;
use Zend\ServiceManager\ServiceLocatorAwareInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
class ApptRepository extends EntityRepository implements ServiceLocatorAwareInterface
{
protected $services;
public function setServiceLocator(ServiceLocatorInterface $serviceLocator)
{
$this->services = $serviceLocator;
}
public function getServiceLocator()
{
return $this->services;
}
public function getUserApptsByDate()
{
$dql = "SELECT a FROM Appointment a";
$em = $this->getServiceLocator()->get('Doctrine\ORM\EntityManager');
$query = $em()->createQuery($dql);
return $query->getResult();
}
}
I then want to call this in my controller using the following pattern:
$diary = $em->getRepository('Calendar\Entity\Appointment')->getUserApptsByDate();
EDIT: The attached link suggests that I may need to convert the class to a service,
https://stackoverflow.com/a/13508799/1325365
However, if this is the best route, how would I then make my Doctrine Entity aware of the service. At the moment I include an annotation in the doc block pointing to the class.
@ORM\Entity (repositoryClass="Calendar\Repository\ApptRepository")
As long as you extend the Doctrine\ORM\EntityRepository, you have immediate access to the entity manager by calling
EntityRepository::getEntityManager()
or the$_em
attribute. The inheritence from the Doctrine\ORM\EntityRepository class allow you to do so.Your method should now look like this:
I always keep in mind that access to my data should go from the web front (Zend MVC, Service Manager) to the persistence layer (Doctrine). My persistence (entities, repositories...) layer should not refer to the web front or neither know that it exists. If my system is doing the inverse at some level, then probably I'm doing something wrong.
Happy end of year
The way i approach things is this:
First i register a Service for each entity. This is done inside Module.php
Next thing would be to create the factory class src\My\Factory\EntitynameServiceFactory.php. This is the part where you inject the EntityManager into your Entity-Services (not into the entity itself, the entity doesn't need this dependency at all)
This class looks something like this:
Next thing in line is to create the src\My\Service\EntitynameService.php. And this is actually the part where you create all the getter functions and stuff. Personally i extend these Services from a global DoctrineEntityService i will first give you the code for the EntitynameService now. All this does is to actually get the correct repository!
This part until here should be quite easy to understand (i hope), but that's not all too interesting yet. The magic is happening at the global DoctrineEntityService. And this is the code for that!
So what does this do? This DoctrineEntityService pretty much is all what you globally need (to my current experience). It has the
fincAll()
,find($id)
and thefindByQuery($closure)
Your next question (hopefully) would only be "How to use this from my controller now?". It's as simple as to call your Service, that you have set up in the first step! Assume this code in your Controllers
The function
findByQuery()
would expect an closure. The$queryBuilder
(or however you want to name that variable, you can choose) will be an instance of\Doctrine\DBAL\Query\QueryBuilder
. This will always be tied toONE Repository
though! Thereforeentity.somekey
theentity.
will be whatever repository you are currently working with.If you need access to the
EntityManager
you'd either only instantiate only theDoctrineEntityService
or call the$entityService->getEntityManager()
and continue from there.I don't know if this approach is overly complex or something. When setting up a new Entity/EntityRepository, all you need to do is to add a new Factory and a new Service. Both of those are pretty much copy paste with two line change of code in each class.
I hope this has answered your question and given you some insight of how work with ZF2 can be organized.