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.
Related
I know Ember has a logger, but I wanted to create my own for learning purposes. I have a service called logger, and I want to be able to use this service everywhere. I have no problem injecting this service into components, controllers, and etc... I cannot figure out how to inject this service into a Utility I created without passing it through the create function. I don't want to have to pass my logger everywhere I create the utility. When I try to inject it into the object it complains about not being in a container. What's the best way to do this?
Okay, its important to understand what Ember.inject.service actually does! Its like a shorter version for this:
myService: Ember.computed({
get() {
return Ember.getOwner(this).lookup('service:myService);
}
}),
So what is this getOwner? It gives you the owner of an Object. Most of your objects like models, controllers, components, views and so on are created by the Dependency Injection (DI) container. For a class to be available on the DI container it needs to be registered.
Your default classes like controllers, routes, views are automatically registered by the Resolver. After registration you can inject them into other classes automatically when they are created by the container. Also into all instances created by the container the owner is injected.
Because the container itself is private, these public APIs are on the Application. getOwner also returns the application.
If you want to manually lookup an instance on the container you can use lookup.
For your utility class you probably use a normal .create() to get the object. This of course will not automatically couple it to your application, so the owner is not available. Also automatic injection will not work.
You can manually inject the owner with the ownerInjection:
myClass.create(Ember.getOwner(this).ownerInjection(), {...});
Then Ember.inject.service will work because getOwner will return the injected owner.
The other thing you could do is to register your utility objects on the container and then look them up. Then the owner is automatically injected.
Not sure which Ember version initiated this pattern but the Ember documentation contains the answer to that question starting from v4.3:
import { inject as service } from '#ember/service';
import { getOwner, setOwner } from '#ember/application';
class Item {
#service('shopping-cart') cart;
constructor(context) {
setOwner(this, getOwner(context));
}
function addToCart() {
this.cart.add(this);
}
}
// On any framework object...
let item = new Item(this);
item.addToCart();
I hit a similar problem a little while back.
The utilities are of type Ember.Object.
So, all you have to do is inject the service into the Ember.Object class as a property.
Like this:
Ember.Object.reopen({
testService:Ember.inject.service('testService')
});
Et Voila!Now you can literally use your service anywhere
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.
So I've build a WebAPI from scratch, including some best practices that I've found online such as Dependency Injection and Domain<->DTO mapping using auto mapper etc.
My API Controllers now look similar to this
public MyController(IMapper mapper)
{
}
and AutoMapper Registry:
public AutoMapperRegistry()
{
var profiles = from t in typeof(AutoMapperRegistry).Assembly.GetTypes()
where typeof(Profile).IsAssignableFrom(t)
select (Profile)Activator.CreateInstance(t);
var config = new MapperConfiguration(cfg =>
{
foreach (var profile in profiles)
{
cfg.AddProfile(profile);
}
});
For<MapperConfiguration>().Use(config);
For<IMapper>().Use(ctx => ctx.GetInstance<MapperConfiguration>().CreateMapper(ctx.GetInstance));
}
I'm also building a few test cases, implementing MOQ, and this is where i feel a little unsure. whenever calling my controllers, I need to pass in an IMapper like this:
var mockMapper = new Mock<IMapper>();
var controller = new MyController(mockMapper.Object);
But then, how do i configure the IMapper to have the correct mappings? It feels redundant to recreate the same logic I've already created before to configure the Mapper. so I am wondering what is the recommended approach to do this?
That's pretty simple: if you mock IMapper and imagine it as a fully abstract concept of mapping data from one object to another, then you have to treat is an abstraction and not imply there's a real automapper behind it.
First you should not register any existing profile at all, you should instead setup IMapper.Map method to return specific object when given another object.
So for each profile used for specific method you have to do a setup, looking approximately like this:
var mockMapper = new Mock<IMapper>();
mockMapper.Setup(x => x.Map<DestinationClass>(It.IsAny<SourceClass>()))
.Returns((SourceClass source) =>
{
// abstract mapping function code here, return instance of DestinationClass
});
In this case, your test knows nothing about actual IMapper implementation - it just uses it methods to get the data you expect from actual IMapper implementation to receive.
This might me another solution
//auto mapper configuration
var mockMapper = new MapperConfiguration(cfg =>
{
cfg.AddProfile(new AutoMapperProfile()); //your automapperprofile
});
var mapper = mockMapper.CreateMapper();
And then call then controller like so
var controller = new YourController(imapper:mapper,..otherobjects..);
This way it will serve the purpose or else if you create mock object for IMapper then it will return what you ask it to return.
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!
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();