I've created custom decorator for EntityManager and now when I'm doing doctrine->getManager(), then I can get my custom manager class, but inside repository class I still have native EntityManager how can I fix this. Or maybe there is another way to set something inside repository classes from container?
Decorator calls getRepository on $wrapped(EntityManager) and then $wrapped pass $this inside RepositoryFactory $this == $wrapped == EntityManager
My solution is:
public function getRepository($className)
{
$repository = parent::getRepository($className);
if ($repository instanceof MyAbstractRepository) {
$repository->setDependency();
}
return $repository;
}
There are a couple of approaches:
Copy the static EntityManager::createRepository code to your entity manager class and adjust it accordingly. This is fragile since any change to the EntityManager code might break your code. You have to keep track of doctrine updates. However, it can be made to work.
A second approach is to define your repositories as services. You could then inject your entity manager in the repository. Bit of a hack but it avoids cloning the createRepository code.
The third approach is the recommended approach. Don't decorate the entity manager. Think carefully about what you are trying to do. In most cases, Doctrine events or a custom base repository class can handle your needs. And it saves you from fooling around with the internals.
One option would be to override the entity manager service classes or parameters via a compiler pass.
Related
For those of you that are familiar with the building of the Symfony container, do you know what is the differences (if any) between
Tagged service Collector using a Compiler pass
Tagged service Collector using the supported shortcut
Service Locator especially, one that collects services by tags
Specifically, I am wondering about whether these methods differ on making these collected services available sooner or later in the container build process. Also I am wondering about the ‘laziness’ of any of them.
It can certainly be confusing when trying to understand the differences. Keep in mind that the latter two approaches are fairly new. The documentation has not quite caught up. You might actually consider making a new project and doing some experimenting.
Approach 1 is basically an "old school" style. You have:
class MyCollector {
private $handlers = [];
public function addHandler(MyHandler $hamdler) {
$handlers[] = $handler;
# compiler pass
$myCollectorDefinition->addMethodCall('addHandler', [new Reference($handlerServiceId)]);
So basically the container will instantiate MyCollector then explicitly call addHandler for each handler service. In doing so, the handler services will be instantiated unless you do some proxy stuff. So no lazy creation.
The second approach provides a somewhat similar capability but uses an iterable object instead of a plain php array:
class MyCollection {
public function __construct(iterable $handlers)
# services.yaml
App\MyCollection:
arguments:
- !tagged_iterator my.handler
One nice thing about this approach is that the iterable actually ends up connecting to the container via closures and will only instantiate individual handlers when they are actually accessed. So lazy handler creation. Also, there are some variations on how you can specify the key.
I might point out that typically you auto-tag your individual handlers with:
# services.yaml
services:
_instanceof:
App\MyHandlerInterface:
tags: ['my.handler']
So no compiler pass needed.
The third approach is basically the same as the second except that handler services can be accessed individually by an index. This is useful when you need one out of all the possible services. And of course the service selected is only created when you ask for it.
class MyCollection {
public function __construct(ServiceLocator $locator) {
$this->locator = $locator;
}
public function doSomething($handlerKey) {
/** #var MyHandlerInterface $handler */
$handler = $serviceLocator->get($handlerKey);
# services.yaml
App\MyCollection:
arguments: [!tagged_locator { tag: 'app.handler', index_by: 'key' }]
I should point out that in all these cases, the code does not actually know the class of your handler service. Hence the var comment to keep the IDE happy.
There is another approach which I like in which you make your own ServiceLocator and then specify the type of object being located. No need for a var comment. Something like:
class MyHandlerLocator extends ServiceLocator
{
public function get($id) : MyHandlerInterface
{
return parent::get($id);
}
}
The only way I have been able to get this approach to work is a compiler pass. I won't post the code here as it is somewhat outside the scope of the question. But in exchange for a few lines of pass code you get a nice clean custom locator which can also pick up handlers from other bundles.
I'm developing a medium scale application using Symfony2 and Doctrine2. I'm trying to structure my code according to the SOLID principles as much as possible. Now here is the question:
For creating new Entities, I use Symfony Forms with proxy objects i.e: I don't bind the form directly to my Entity, but to some other class that will passed to some service which will take the needed action based on the received data, i.e: the proxy class serves as a DTO to that service which I will call the Handler. Now considering the Handler doesn't have a dependency on the EntityManager, where should I do calls to EntityManager::persist() and EntityManager::flush()? I am usually comfortable with putting flush in the controller but I'm not so sure about persist since the controller shouldn't assume anything about what the Handler does, and maybe Handler::handle (the method that the form data is passed to) does more than just persist a new Entity to the database. One Idea is to create some interfaces to encapsulate flush and persist and pass them around, which will act as wrappers around EntityManager::flush() and EntityManager::persist(), but I'm not so sure about it since EntityManager::flush() might create unwanted consequences. So Maybe I should just create an interface around persist.
So My question is where and how to make the call to persist and flush, in order to get the most Solid code? Or am I just overcomplicating things in my quest of best practices?
If you have a service that will handle tasks upon your entities, to me, the right way is to inject EntityManager into your service definition and do persist and flush operation inside it.
Another way to proceed, if you want to keep separate that logic, is to create an EventSubscriber and raise a custom event from your "entity service" when you're ready to do persist and flush operations
My 2 cents:
about flush, as it calls the DB, doing it like you already do when needed in your controllers sounds good to me.
about presist, it should be called in your Handler when your entity is in a "ready to be flushed" state. A Persister interface with only the persist method as a dependency of your Handlers, and a DoctrinePersister implementation injected in them looks OK.
Another option here - you can implement save() method in your entity repository class and make persistence there. Inject your entity repository as dependency into your Handler class.
If you don't want to couple your service and business logic to the EntityManager (good job), SOLID provides a perfect solution to separate it from your database logic.
//This class is responsible for business logic.
//It knows nothing about databases
abstract class CancelOrder
{
//If you need something from the database in your business logic,
//create a function that returns the object you want.
//This gets implemented in the inherited class
abstract protected function getOrderStatusCancelled();
public function cancel($order)
{
$order->setOrderStatus($this->getOrderStatusCancelled());
$order->setSubmittedTime(new DateTime());
//and other business logic not involving database operations
}
}
//This class is responsible for database logic. You can create a new class for any related CRUD operations.
class CancelOrderManager extends CancelOrder
{
public function __construct($entityManager, $orderStatusRepository)...
public function getOrderStatusCancelled()
{
return $this->orderStatusRepository->findByCode('cancelled');
}
public function cancel($order)
{
parent::cancel($order);
$this->entityManager->flush();
}
}
I know I can do a lot of stuff...but I'd like to avoid overkill.
I have a class with general util methods. I want to be able to call one of those methods from inside a formType.
What I did is the following:
Inside the formType I added
use AppBundle\Util\GeneralUtil;
class HRMgmtFormType extends AbstractType
{
public function __construct(GeneralUtil $util)
{
$this->util = $util;
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
$timeCommitment = $this->util->generatePercentVectorByStep(1);
...all the rest of the stuff
And when I call it from the controller I have:
$form = $this->createForm(new HRMgmtFormType(new GeneralUtil()), array(...all the stuff I need here...);
It works.
Is this ok/elegant?
I basically avoided declaring my general util class as a service, and most important declaring my form as a service (and injecting the generalUtil service inside).
Thank you!
SN
As far as you have the class "GeneralUtil" such simple as you have it now: without any dependencies and any configuration parameters, your solution is fair. But there are some more aspects that you should think about:
Will be "GeneralUtil" reusable at other places?
Will you need to make unit tests? if so, do you plan to mock the util if you plan to get "pure" unit test and make dependency injection(DI) with mock?
Do you plan extend the util's functionality? will it get some dependencies or configuration in future?
If yes, then it is better to get the benefit from Symfony SOA (Service Oriented Architecture) approach and refactor your code to SOA and DI, that will allow you follow another important patter as DRY (Don't repeat yourself) http://en.wikipedia.org/wiki/Don%27t_repeat_yourself
Update to your commet:
So you can see from http://symfony.com/doc/current/book/forms.html#defining-your-forms-as-services
Defining your form type as a service is a good practice and makes it
really easy to use in your application.
but as I already explained about the utils class, similar logic is about forms. So if you plan to reuse your form, then you can do it as service, but if you will have several different forms that will use your utils class, then it's better leave the form as class, but make a service for the utils. In addition you can do it if you see possibility to overwrite your form some other i.e 3rd party forms.
So benefits list from form as service:
in case of multiply usage, it initialized only once
easy to overwrite by other form
globally configured from parameters ans DI other services
Using https://insight.sensiolabs.com to scan / check my code, I get the following warning:
The Doctrine Entity Manager should not be passed as an argument.
Why is it such a bad practice to inject the Entity Manager in a service? What is a solution?
With respect to the comment that repositories cannot persist entities.
class MyRepository extends EntityRepository
{
public function persist($entity) { return $this->_em->persist($entity); }
public function flush () { return $this->_em->flush (); }
I like to make my repositories follow more or less a "standard" repository interface. So I do:
interface NyRepositoryInterface
[
function save($entity);
function commit();
}
class MyRepository extends EntityRepository implements MyRepositoryInterface
{
public function save ($entity) { return $this->_em->persist($entity); }
public function commit() { return $this->_em->flush (); }
This allows me to define and inject non-doctrine repositories.
You might object to having to add these helper functions to every repository. But I find that a bit of copy/paste is worth it. Traits might help here as well.
The idea is move away from the whole concept of an entity manager.
I am working on a quite a large project currently and have recently started following the approach with repositories that can mutate data. I don't really understand the motivation behind having to inject EntityManager as a dependency which is as bad as injecting ServiceManager to any class. It is just a bad design that people try to justify. Such operations like persist, remove and flush can be abstracted into sth like AbstractMutableRepository that every other repository can inherit from. So far it has been doing quite well and makes code more readable and easier to unit test!
Show me at least one example of a service that has EM injected and unit test for it looked correctly? To be able to unit test sth that has EM injected is more about testing implementation than anything else. What happens is then you end up having so many mocks set up for it, you cannot really call it a decent unit test! It is a code coverage hitter, nothing more!
I'm building an application in Symfony 2.2.
I use a custom repository class for one of my entities. I include it like this:
#ORM\Entity(repositoryClass="MyApp\MainBundle\Entity\CategoryRepository")
I want to use methods on my category entites from two other repository classes.
#ORM\Entity(repositoryClass="Gedmo\Sortable\Entity\Repository\SortableRepository")
#ORM\Entity(repositoryClass="Gedmo\Tree\Entity\Repository\NestedTreeRepository")
The entity, however, only expects one repositoryClass. How can I use all three of these repositories for the same entity?
I believe, you can only have one repository per Entity.
So you have referenced
ORM\Entity(repositoryClass="MyApp\MainBundle\Entity\CategoryRepository")
If you must use another repositories within the repository,
You can get the EntityManager for those Repositories (if not same) eg: $em2 = whatEverIsTheEntity()
Now you can have proxy methods in CategoryRepository to the methods in the repository you want to use. eg: $em2->getMethodFromEm2().
Note: But I wouldn't prefer above solution unless there is really a need. I would rather create a service layer which has Doctrine Entity Manager as Dependency Injected. This service layer will connect to all the repositories and collect final result whatever you want to achieve (to be used in the controller). This way, the code will be much cleaner and testable as well.
Hope this helps.
Repositories are class (so entity) reserved. If you inherit from super classes (even if those classes doesn't inherit directly, but repos can), than you can make your repository (CategoryRepository) extended by other two (one extend the other, since PHP doesn't allow more than one class extension).
Pretty much something like that
class NestedTreeRepository extends SortableRepository
{
}
class CategoryRepository extends NestedTreeRepository
{
}
don't know if this fits your needs, but this a way (and I'll not surprised if is the only one) for realize what you're trying to do here