Zend Framework 2 + Doctrine: get Entity Manager in Model - doctrine-orm

Edit 1: it seems like I didn't explain myself very well. Class Foo is not an entity. Just a general purpose model that I would like to have an access to the entity manager.
Edit 2: I don't think there is an answer to my question. Basically, I wanted a class that can have access to the EntityManager without this class being called by the service manager, simply due to the fact that it may be called by a class who is also not called by the service manager. In other words, I was trying to achieve what Zend_Registry used to achieve in ZF1. I'll have to find another way of doing what I am trying to do.
I am trying to access Doctrine's entity manager in a model, in a similar way as it done in a controller:
$this->getServiceLocator()->get('Doctrine\ORM\EntityManager');
The ZF2 manual (http://framework.zend.com/manual/2.0/en/modules/zend.service-manager.quick-start.html) says:
By default, the Zend Framework MVC registers an initializer that will inject the ServiceManager instance, which is an implementation of Zend\ServiceManager\ServiceLocatorInterface, into any class implementing Zend\ServiceManager\ServiceLocatorAwareInterface.
So I created a the following class:
<?php
namespace MyModule\Model;
use Zend\ServiceManager\ServiceLocatorAwareInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
class Foo implements ServiceLocatorAwareInterface
{
protected $services;
public function setServiceLocator(ServiceLocatorInterface $serviceLocator)
{
$this->services = $serviceLocator;
}
public function getServiceLocator()
{
return $this->services;
}
public function test()
{
$em = $this->getServiceLocator()->get('Doctrine\ORM\EntityManager');
}
}
Then, from another class I call this class as such:
$foo = new \MyModule\Model\Foo();
$foo->test()
which throws the following error:
PHP Fatal error: Call to a member function get() on a non-object
So, I guess I am missing something somewhere, but what? Where? How? Perhaps there is an easier to access the entity manager?
Thanks!

From your question, I see that you have mainly two misunderstandings, one about your design strategy (injecting an EntityManager on your model) and one about how things work with the service manager (ServiceLocatorAwareInterface). In my answer I'll try to focus on the second one.
Initializers are php closures that are called over each instance accessed from the Service Manager before this one returns it to you.
Here is an example of an Initializer :
// Line 146 - 150 of Zend\Mvc\Service\ServiceManagerConfig class + comments
$serviceManager->addInitializer(function ($instance) use ($serviceManager) {
if ($instance instanceof ServiceManagerAwareInterface) {
$instance->setServiceManager($serviceManager);
}
});
As you can see each time Service Manager is asked to return an instance/object that implements the ServiceManagerAwareInterface interface, it will setup/inject the Service Manager instance to it.
By the way in your previous code you omitted to implement correctly the interface as you didn't define the setServiceManager method. However, this is not your only problem.
First, if you want the Service Manager to inject itself in your Model, you need to call/construct your model instance from it (during this process it will call the initializers) through a factory for example if your class has complex dependencies.
[EDIT]
Example:
In your MyModule
namespace MyModule;
use Zend\ModuleManager\Feature\ServiceProviderInterface;
use MyModule\Model\Foo;
class Module implements ServiceProviderInterface{
//Previous code
public function getServiceConfig()
{
return array(
'instances' => array(
'myModelClass' => new Foo(),
),
);
}
Now, when you need a Foo instance you should call the Service Manager:
$serviceManager->get('myModelClass');
Don't forget defining setServiceManager method, otherwise your'e not correctly implementing the ServiceManagerAwareInterface!

I think, the only thing you’re missing, is to add your model class to the list of invokables and retreive it through the service manager.
So basically add this to your module.conf.php:
return array(
'service_manager' => array(
'invokables' => array(
'MyModule\Model\Foo' => 'MyModule\Model\Foo',
),
),
);
And instantiate your model object like this (if in a controller):
$foo = $this->getServiceLocator()->get('MyModule\Model\Foo');
$foo->test();

Related

How to write a test for bundle which it depends on some entity

I've been working on a grid bundle for Symfony. The bundle receives a Symfony Entity and based on that, it renders a gridview.
something like this:
class IndexController extends AbstractController
{
public function __construct(GridBuilder $grid, BookGrid $userGrid)
{
$this->grid = $grid;
$this->userGrid = $userGrid;
}
/**
* #Route("/")
*/
public function index()
{
return $this->render('index.html.twig', [
'grid' => $this->grid->build($this->userGrid),
]);
}
}
BookGrid is a class extended from BaseGridConfigurator which it has to implement getEntity method:
class BookGrid extends BaseGridConfigurator
{
public function getEntity()
{
return Book::class;
}
}
The GridBuilder uses the EntityRepository (in this case BookRepository) to get the entity's metadata such as fields, Repository and QueryBuilder.
If I want to write unit test for the bundle, I need an entity class to pass it to GridBuilder. I think there are two approaches to solve this problem.
Create a mock Entity and Repository
Create a real Entity and Repository class inside my test directory
My question is which approach is correct? and is there any other way to test a bundle that it depends on an entity?
Thank you
Assuming getEntity (which perhaps should be renamed to getEntityClass) is used by GridBuilder to obtain the desired entity repository from the entity manager internally, wouldn't it be easier to have BaseGridConfigurator provide access to the entity repository directly? E.g. getEntityRepository(): EntityRepository instead of getEntity(): string. I can imagine this would significantly reduce the amount of mocking you would have to do if all you need is the entity repository.
In any case, the Symfony documentation on the subject of unit testing entity repositories advice against unit testing entity repository dependent implementations in general.
But if you have to, I would focus on a design where your implementation needs as few contact points with the entity repository as possible in order to minimize the amount of mocking that the test requires. But would still opt for mocking over stubbing regardless.

Symfony Doctrine entity manager and repository

I understand the benefit or repository pattern but I just can't understand in Symfony3 Doctrine there are Doctrine\ORM\EntityManager and \Doctrine\ORM\EntityRepository
What are the difference between the two?
Is repository should be injected to controller or entity manager?
Edit
The correct question should be: What's the proper way to access a repository from a controller?
Should a repository be injected to a controller as a service?
Should a repository be injected to another service as a service?
Should entity manager contain any query at all?
Edit
The correct question should be: should a service contain a query at all? Which #MateuszSip already explained, it could be done by injecting Entity Manager
Should a custom function like getAvailableManagers be put in
repository or services? (Where manager is a repository and there
are some logic in determining available manager)
How about a more generic function like findAllManager, should it be in repository or entity manager?
Currently I'm using Symfony3. Thank you very much
Cheers,
Edit
Talking to #MateuszSip (thanks mate), I decided to make my question clearer with an example below. Please note that below code are not representing real problem
controller
Class ManagementController
{
public function assignManager($projectType)
{
// Grabbing a service
$s = $this->get('mycompany_management_management_service')
$managers = $s->findAvailableManagers();
$managers = $s->checkCapability($managers, $projectType);
return $managers
}
}
repository
class ManagerRepository extends \Doctrine\ORM\EntityRepository
{
public function findAvailableManagers()
{
...
return $managers
}
public function checkCapability($managers, $type)
{
...
return $capableManagers
}
}
services
class ManagementService
{
... I am not sure what should be here.
}
EntityManager is used to manage doctrine-related objects, so:
you can persist an entity object (it's now managed by doctrine, and ready to save)
you can remove an entity object (so it'll be deleted later)
you can flush, and it'll trigger pending operations
you can get a repository (to get objects you'll need) or use a generic api to get an object by a primary key
etc.
It's a class that manages a state of objects and their relation to the database.
Repository is a pattern that standarizes an access to the entites.
If your app is complex, you should inject a separate service(s) to your controller. So there's a UserSaver service (as an example) that use entityManager to create/update a user and UserFinder (or something well-named) using UserRepository which's responsible of fetching user by defined criterias.
You can create a query using entity manager, but em itself cannot contain queries.
In my opinion, define a method inside a service, and a corresponding method in your UserRepository. At this moment, all of what you want should be fetched by a database, but it can change later.
In repository. Methods like: findByRole(role=manager), findIsActive, findOneBySecurityNumber relies to a repository.

Laravel Tests pass to model to View

I'm mocking my repository correctly, but in cases like show() it either returns null so the view ends up crashing the test because of calling property on null object.
I'm guessing I'm supposed to mock the eloquent model returned but I find 2 issues:
What's the point of implementing repository pattern if I'm gonna end up mocking eloquent model anyway
How do you mock them correctly? The code below gives me an error.
$this->mockRepository->shouldReceive('find')
->once()
->with(1)
->andReturn(Mockery::mock('MyNamespace\MyModel)
// The view may call $book->title, so I'm guessing I have to mock
// that call and it's returned value, but this doesn't work as it says
// 'Undefined property: Mockery\CompositeExpectation::$title'
->shouldReceive('getAttribute')
->andReturn('')
);
Edit:
I'm trying to test the controller's actions as in:
$this->call('GET', 'books/1'); // will call Controller#show(1)
The thing is, at the end of the controller, it returns a view:
$book = Repo::find(1);
return view('books.show', compact('book'));
So, the the test case also runs view method and if no $book is mocked, it is null and crashes
So you're trying to unit test your controller to make sure that the right methods are called with the expected arguments. The controller-method fetches a model from the repo and passes it to the view. So we have to make sure that
the find()-method is called on the repo
the repo returns a model
the returned model is passed to the view
But first things first:
What's the point of implementing repository pattern if I'm gonna end up mocking eloquent model anyway?
It has many purposes besides (testable) consisten data access rules through different sources, (testable) centralized cache strategies, etc. In this case, you're not testing the repository and you actually don't even care what's returned, you're just interested that certain methods are called. So in combination with the concept of dependency injection you now have a powerful tool: You can just switch the actual instance of the repo with the mock.
So let's say your controller looks like this:
class BookController extends Controller {
protected $repo;
public function __construct(MyNamespace\BookRepository $repo)
{
$this->repo = $repo;
}
public function show()
{
$book = $this->repo->find(1);
return View::make('books.show', compact('book'));
}
}
So now, within your test you just mock the repo and bind it to the container:
public function testShowBook()
{
// no need to mock this, just make sure you pass something
// to the view that is (or acts like) a book
$book = new MyNamespace\Book;
$bookRepoMock = Mockery::mock('MyNamespace\BookRepository');
// make sure the repo is queried with 1
// and you want it to return the book instanciated above
$bookRepoMock->shouldReceive('find')
->once()
->with(1)
->andReturn($book);
// bind your mock to the container, so whenever an instance of
// MyNamespace\BookRepository is needed (like in your controller),
// the mock will be loaded.
$this->app->instance('MyNamespace\BookRepository', $bookRepoMock);
// now trigger the controller method
$response = $this->call('GET', 'books/1');
$this->assertEquals(200, $response->getStatusCode());
// check if the controller passed what was returned from the repo
// to the view
$this->assertViewHas('book', $book);
}
//EDIT in response to the comment:
Now, in the first line of your testShowBook() you instantiate a new Book, which I am assuming is a subclass of Eloquent\Model. Wouldn't that invalidate the whole deal of inversion of control[...]? since if you change ORM, you'd still have to change Book so that it wouldn't be class of Model
Well... yes and no. Yes, I've instantiated the model-class in the test directly, but model in this context doesn't necessarily mean instance of Eloquent\Model but more like the model in model-view-controller. Eloquent is only the ORM and has a class named Model that you inherit from, but the model-class as itself is just an entity of the business logic. It could extend Eloquent, it could extend Doctrine, or it could extend nothing at all.
In the end it's just a class that holds the data that you pull e.g. from a database, from an architecture point of view it is not aware of any ORM, it just contains data. A Book might have an author attribute, maybe even a getAuthor() method, but it doesn't really make sense for a book to have a save() or find() method. But it does if you're using Eloquent. And it's ok, because it's convenient, and in small project there's nothing wrong with accessing it directly. But it's the repository's (or the controller's) job to deal with a specific ORM, not the model's. The actual model is sort of the outcome of an ORM-interaction.
So yes, it might be a little confusing that the model seems so tightly bound to the ORM in Laravel, but, again, it's very convenient and perfectly fine for most projects. In fact, you won't even notice it unless you're using it directly in your application code (e.g. Book::where(...)->get();) and then decide to switch from Eloquent to something like Doctrine - this would obviously break your application. But if this is all encapsulated behind a repository, the rest of your application won't even notice when you switch between databases or even ORMs.
So, you're working with repositories, so only the eloquent-implementation of the repository should actually be aware that Book also extends Eloquent\Model and that it can call a save() method on it. The point is that it doesn't (=shouldn't) matter if Book extends Model or not, it should still be instantiable anywhere in your application, because within your business logic it's just a Book, i.e. a Plain Old PHP Object with some attributes and methods describing a book and not the strategies how to find or persist the object. That's what repositories are for.
But yes, the absolute clean way is to have a BookInterface and then bind it to a specific implementation. So it could all look like this:
Interfaces:
interface BookInterface
{
/**
* Get the ISBN.
*
* #return string
*/
public function getISBN();
}
interface BookRepositoryInterface()
{
/**
* Find a book by the given Id.
*
* #return null|BookInterface
*/
public function find($id);
}
Concrete implementations:
class Book extends Model implements BookInterface
{
public function getISBN()
{
return $this->isbn;
}
}
class EloquentBookRepository implements BookRepositoryInterface
{
protected $book;
public function __construct(Model $book)
{
$this->book = $book;
}
public function find($id)
{
return $this->book->find($id);
}
}
And then bind the interfaces to the desired implementations:
App::bind('BookInterface', function()
{
return new Book;
});
App::bind('BookRepositoryInterface', function()
{
return new EloquentBookRepository(new Book);
});
It doesn't matter if Book extends Model or anything else, as long as it implements the BookInterface, it is a Book. That's why I bravely instantiated a new Book in the test. Because it doesn't matter if you change the ORM, it only matters if you have several implementations of the BookInterface, but that's not very likely (sensible?), I guess. But just to play it safe, now that it's bound to the IoC-Container, you can instantiate it like this in the test:
$book = $this->app->make('BookInterface');
which will return an instance of whatever implementation of Book you're currently using.
So, for better testability
Code to interfaces rather than concrete classes
Use Laravel's IoC-Container to bind interfaces to concrete implementations (including mocks)
Use dependency injection
I hope that makes sense.

creating own service with default doctrine2 connection in zf2

i'm learning zf2 with doctrine2.
Doctrine2 entities is not good place for putting logic there, so i have to create something like services.
I created new catalog in src - Service, and i thought that i will create abstractService class, which will have by default access to doctrine2 entity manager, and other usefull stuff further.
But i'm not sure how to achieve it, i created constructor, where i should pass instance of object manager through injection, but i don't know how to do it. All found examples are for controllers.
Can somebody show me way ho to manage/organise it?
I didn't found any solutions.
You can retrieve the default doctrine entity manager from the service manager using eighter doctrine.entitymanager.orm_default or the alias Doctrine\ORM\EntityManager.
$serviceLocator->get('Doctrine\ORM\EntityManager');
Best practice for dependency injection in ZF2 is to use service factories which you register on the service manager. There is plenty of information about this available.
Anyway I'll explain the steps.
Your service
namespace MyNamespace
class MyService
{
public function __construct(EntityManager $em)
{
$this->em = $em;
}
}
Factory
Define the factory in your module.php
class Module
{
public function getServiceConfig()
{
return array(
'factories' => array(
'MyNamespace\MyService' => function($serviceLocator) {
return new MyService($serviceLocator->get('Doctrine\ORM\EntityManager');
}
)
);
}
}
I have used a closure for the factory for this example, but it's recommended to use a dedicated factory class.
Now you can retrieve your fully composed service from the service manager.
$serviceManager->get('MyNamespace\MyService');
If you want to use this service in your controller you'll need to define a factory for this in the same way I did above. There's just one small difference, because all controllers are managed by a seperate pluginManager (a serviceManager dedicated to creating classes of a certain type). You can use the method getControllerConfig in your module.php to define factories on the controller plugin manager.
Add this to your module.php
public function getControllerConfig()
{
return array(
'factories' => array(
'MyNamespace\Controller\MyController' => function($serviceLocator)
{
$rootLocator = $serviceLocator->getServiceLocator();
$service = $rootLocator->get('MyNamespace\MyService');
return new MyController($service);
}
)
);
}
Hope this helps.

Avoiding cyclic dependencies between entities in two modules

I’m currently wondering what’s a good way to keep ZF2 modules copyable from one project to another, if I have doctrine2 entities that reference each other.
My current situation is something like this: I have an entity User from which I want to be able to access all languages this user speaks.
Of course, Language is not a component of the Authentication module, because I might want to use it for other purposes, too.
namespace Authentication\Entity;
class User {
public function getSpokenLanguages();
}
And:
namespace Application\Entity;
class Language {
public function getUsersWhoSpeakThisLanguage();
}
The problem is, I want my Authentication module to be totally independent from the project-specific module Application.
Is there a good way to keep these relations out of my entities or possibly inject them from the Application module? Maybe also a UserService (in Application module) giving me the languages Language[] for a specific User would be a good idea? I could call it like this:
$userService->getUsersLanguages($user);
I think, especially injection might be a ZF2-solution, but I have no idea, how one could extend Doctrine2 entities like that from another module.
I think you're speaking to more of semantic issue than one specific to ZF2. Reading your question, I think your language becomes more of a managed layer that you can easily facilitate with factories and DI - luckily ZF2 has all the right tools. Consider something like this as a potential draft for a solution:
Create a LanguageAbstractFactory:
namespace Your\Namespace;
use Zend\ServiceManager\AbstractFactoryInterface,
Zend\ServiceManager\ServiceLocatorInterface;
class LanguageAbstractFactory implements AbstractFactoryInterface
{
/**
* Determine if we can create a service with name
*
* #param ServiceLocatorInterface $serviceLocator
* #param $name
* #param $requestedName
* #return bool
*/
public function canCreateServiceWithName( ServiceLocatorInterface $serviceLocator, $name, $requestedName )
{
return stristr( $requestedName, 'Namespace\Language' ) !== false;
}
public function createServiceWithName(ServiceLocatorInterface $locator, $name, $requestedName)
{
$filter = new $requestedName();
$filter->setServiceLocator( $locator );
return $filter;
}
}
Then, create your languages in that same namespace, as subclasses of Language, that implement ServiceLocatorAwareInterface (to give you database access down the road and such). The code in the factory above injects the service locator (and that's where you tweak it to inject other goodness to satisfy your language architecture):
namespace Your\Namespace;
use Zend\ServiceManager\ServiceLocatorAwareInterface,
Zend\ServiceManager\ServiceLocatorInterface;
class Language implements ServiceLocatorAwareInterface
{
protected $serviceLocator;
public function setServiceLocator(ServiceLocatorInterface $serviceLocator)
{
$this->serviceLocator = $serviceLocator;
}
public function getServiceLocator()
{
return $this->serviceLocator;
}
// ... other things your factory knows, that this class may not go here
}
A Language implementation might then look like:
namespace Your\Namespace\Language;
class English extends \Your\Namespace\Language
{
public function getUsersWhoSpeakThisLanguage()
{
$sm = $this->getServiceManager();
// get your entities or w/e using the SM
}
}
Connect the factory by tweaking your module's Module.php at getServiceConfig:
public function getServiceConfig() {
return array(
'abstract_factories' => array(
// this one generates all of the mass email filters
'Your\Namespace\LanguageAbstractFactory',
),
);
}
This gives you the ability to use the service manager to get a service-aware language very easily. e.g., from a service-aware class:
$sm->getServiceManager()->get( '\Your\Namespace\Language\English' );
Because of the config, and that the Factory can meet the request, your factory will auto-configure the English instance with whatever logic you build into it in a very portable fashion.
Where this is kind of a primer on Factories - if you rig an interface class that the service can use to speak to your user classes, you can invert control to the Language service from the User. Making your User implement LanguageAware (for example) which contains classes that the Language service can use should be a few steps away.
Hope this helps. There's probably 15 ways to skin this cat; this approach is one I have used to solve similar problems, e.g., that of "Filtering" data. A filter, can filter information, and information can be filtered by a filter.
Good Luck!