Symfony2: Get Service Inside Entity - php

Is there any way to call service inside entity
I need entity Manager inside entity so I can able to get custom result with repository functions.
I am thinking about inject ContainerInterface inside my entity like this.
use Symfony\Component\DependencyInjection\ContainerInterface;
class MyEntity
{
protected $container;
public function __construct(ContainerInterface $container)
{
$this->container = $container;
}
public function getEntityManager(){
return $this->container->get('doctrine')->getEntityManager();
}
}
But I think this is not right way to do that and it take more code I mean I have to do this for all entity where I need entity Manager
Is there any good solution ?

I don't know if you can but you shouldn't do it anyway. The entities are meant to be really simple...
need entity Manager inside entity so i can able to get custom result with repository functions
What do you want to do exactly, there must be a different solution...

As already mentioned, dependency injection is definitely the wrong way to go.
Use either Custom Entity Repositories (http://symfony.com/doc/2.0/book/doctrine.html#custom-repository-classes) for more complex queries or use a specific service where you can implement your custom result if more complexity is needed (http://symfony.com/doc/2.0/book/service_container.html#referencing-injecting-services)

Related

Why I can't access to the paginator DTO in EasyAdmin?

I'm trying to access the EasyCorp\Bundle\EasyAdminBundle\Dto\PaginatorDtoin my Crud Controller :
public function __construct(
private EntityManagerInterface $manager,
private EntityRepository $entityRepository,
private PaginatorDto $paginatorDto,
) {
}
But I've got this error => Cannot autowire service "App\Controller\Activity\ActivityCrudController": argument "$paginatorDto" of method "__construct()" references class "EasyCorp\Bundle\EasyAdminBundle\Dto\PaginatorDto" but no such service exists. and I don't understand why and How to fix it :(
Any idea ?
I'm not an expert of that bundle so take my answer with a pinch of salt but looking at bundle's code I've noticed PaginatorDto not to be a service (as the name suggests).
As that DTO is not a service (and it's ok it is not), you can't autowire it nor make it a service "locally" (eg.: in your application).
So, in order to retrieve the DTO object, inject AdminContextProvider (that is a service as you can notice here) instead and use it to get the DTO
$adminContext->getCrud()->getPaginator();
Your crud controller should extend AbstractCrudController which give you access to the current admin context.
So if you want to use it in one of your crud controller method you should be able to access the paginator with:
$paginator = $this->getContext()->getCrud()->getPaginator();
If you want to do the same outside your crud controller, let's say in another service. You need to inject the AdminContextProvider to first get the AdminContext and do it the same way.
private ?AdminContext $siteRepository;
public function __construct(AdminContextProvider $adminContextProvider)
{
$this->adminContext = $adminContextProvider->getContext();
}

PHPStan doesn't use custom entity repository

I am using PHPStan with its Doctrine extension.
I have a custom entity repository called App\Repository\Doctrine\UserRepository with the #extends doc block:
/**
* #extends \Doctrine\ORM\EntityRepository<\App\Entity\User>
*/
class UserRepository extends EntityRepository implements IUserRepository
{
public function customRepositoryMethod()
{
// ...
}
}
In a controller, this code:
public function getUserMatches(EntityManager $em)
{
$userRepo = $em->getRepository(\App\Entity\User::class);
$userRepo->customRepositoryMethod();
}
...results in this PHPStan error:
Call to an undefined method Doctrine\ORM\EntityRepository<meQ\Entity\User>::customRepositoryMethod().
Thanks to phpstan-doctrine, static analysis knows that $em->getRepository(User::class) returns a EntityRepository<User>.
However, it does not know to consider the custom repository class UserRepository as the implementation of that generic type.
How do I DocBlock the UserRepository class, or otherwise configure PHPStan, so that it interprets UserRepository as the implementation of EntityRepository<User>?
What else I've tried
I've also tried this DocBlock on UserRepository to no avail:
/**
* #implements \Doctrine\ORM\EntityRepository<\App\Entity\User>
*/
PhpStan has no way of knowing EntityManager::getRepository() is going to return your custom method.
Either add a #var annotation right there:
/** #var UserRepository $userRepo */
$userRepo = $em->getRepository(\App\Entity\User::class);
Or better yet, just inject the UserRepository, not the whole EntityManager:
public function getUserMatches(UserRepository $userRepository)
{
$userRepository->customRepositoryMethod();
}
The above would work with PhpStan or any static analysis tool out of the box. (And, injecting the specific repository instead of the Entity Manager is a better practice in any case).
But if not, you can always try installing the Doctrine Extensions for PHPStan, which may help the tool understand the codebase in relationship to Doctrine ORM.
If you are already using the Doctrine Extensions, note that from what I gather in the docs it's only able to extract typing if your mapping configuration is provided via annotations. If you configurate ORM mappings via XML (or YAML, in older versions of Doctrine), then I think the Extensions won't be able to extract the typing data, and the above solutions will be your only way to go.

Injecting dependancies - Doctrine: Multiple repositories vs single entity manager

I have a service class that has dependencies on multiple entity repositories, for example 4.
I could inject each repository and end up with many dependencies, or I could inject the entity manager as a single dependency; relying on EntityManager->getRepo('xyz').
Separate dependencies has the benefit of code hinting.
Single dependency means less verbose at construct.
Possibly easier mocking and less setup?
What is considered a better practice?
In this case EntityManager is something like Service Locator. When service depends on EntityManager, it also formally depends on all its API and all related objects (repositories, metatada, etc). Better inject only what you really need:
explicit injection of specific repositories makes your service easier to read and test.
Also, prefer interface over class if possible (ObjectRepository instead of EntityRepository, ObjectManager instead of EntityManager).
I assume, that you must use only one Doctrine Entity Manager in your service dependencies.
But if you want to have code hinting in your IDE, you can do it with phpdoc annotation like this
class SomeServiceWithDoctrineDependency
{
/** #var YourFirstObjectRepository */
protected $firstRepo;
/** #var YourSecondObjectRepository */
protected $secondRepo;
/** #var YourThirdObjectRepository */
protected $thirdRepo;
public function __construct(EntityManagerInterface $entityManager)
{
$this->firstRepo = $entityManager->getRepository('First:Repo');
$this->secondRepo = $entityManager->getRepository('Second:Repo');
$this->thirdRepo = $entityManager->getRepository('Third:Repo');
}
public function getCodeHint()
{
// You get hint here for find method
// $this->thirdRepo()->find()...
}
}

Dependency injection and Model entities - the right way ?

My symfony2 application has the following structure:
There is a service data_provider, which gets data from various external sources and represents it as entity objects.
Some objects has relations. Currently I am loading relations in controller or helper-services if needed.
It is not very convenient, sometimes I want to get relations from my entity ojbect. To do this I need access to data_provider service.
I want to implement something like doctrine lazy-loading, what is the right way of doing this ?
Some obvious solutions - to inject data_provider in every entity instacne, or to some static property, or to make some static methods in service, or to use evenet dispatcher, but I don't think it is the right way
Made some research of ObjectManagerInterface as Cerad suggested, and found this peace of code: https://github.com/doctrine/common/blob/master/lib/Doctrine/Common/Persistence/PersistentObject.php
PersistentObject implements ObjectManagerAware interface, it has private static property where ObjectManager is stored.
So I ended with this:
class DataProvider
{
public function __construct()
{
...
AbstractEntity::setDataProvider($this);
}
}
abstract class AbstractEntity
{
private static $dataProvider;
public static function setDataProvider() {...};
protected static function getDataProvider() {...};
}
The main purpose of services in Symfony (and not only) is exactly this one - to deliver distinguished functionalitys globally over your project.
In this regard, a single service, in your case - dataProvider, should always deliver a single entity. If you have to deal with multiple entities returned from one data source, wrap the data source deliverer into a service itself, and then define one service per each entity with the deliverer injected into it.
Then you can inject the respective entity services into your controllers.

Constructor-Injection when extending a class

This question is not strictly related to Symfony 2, but as I use Symfony 2 components and will later likely use a Symfony\Component\DependencyInjection\Container as DI-Container, it might be relevant.
I am currently building a small library using components from Symfony 2, e.g. HttpFoundation, Validator, Yaml. My Domain Services are all extending a basic AbstractService providing nothing but Doctrine\ORM\EntityManager and Symfony\Component\Validator\Validator via Constructor-Injection like this:
abstract class AbstractService
{
protected $em;
protected $validator;
/**
* #param Doctrine\ORM\EntityManager $em
* #param Symfony\Component\Validator\Validator $validator
*/
public function __construct(EntityManager $em, Validator $validator)
{
$this->em = $em;
$this->validator = $validator;
}
}
A Service-class extending this AbstractService may now need to inject additonal components, like Symfony\Component\HttpFoundation\Session. As of I do it like this:
class MyService extends AbstractService
{
/**
* #var Symfony\Component\HttpFoundation\Session
*/
protected $session;
/**
* #param Symfony\Component\HttpFoundation\Session $session
* #param Doctrine\ORM\EntityManager $em
* #param Symfony\Component\Validator\Validator $validator
*/
public function __construct(Session $session, EntityManager $em, Validator $validator)
{
parent::__construct($em, $validator);
$this->session = $session;
}
}
Is there a more elegant way to solve this without having to reiterate the parent's constructor arguments, e.g. by using Setter-Injection for Session instead?
As I see it, when I use Setter-Injection for Session, I have to add checks before accessing it in my methods, whether it is already injected, which I want to avoid. On the other hand I don't want to "repeat" injecting the basic components shared by all services.
An alternative would be not to extend your Services from the abstract type. Change the abstract type to a real class and then inject it to your Services, e.g.
class MyService
…
public function __construct(ServiceHelper $serviceHelper)
{
$this->serviceHelper = $serviceHelper;
}
}
Yet another option would be to not pass the dependencies until they are needed, e.g.
class MyService
…
public function somethingRequiringEntityManager($entityManager)
{
// do something with EntityManager and members of MyService
}
}
Is there a more elegant way to solve this without having to reiterate the parent's constructor arguments, e.g. by using Setter-Injection for Session instead?
Well, by using setter-injection like you've mentioned. Apart from that I see two possible solutions to your immediate problem:
Make the parent constructor accept a Session object as well, but make it null by default. This to me is ugly, since the parent doesn't actually need the session object, and if you have other implementations, this will result in a long parameter list for the parent constructor, so this is actually not really an option.
Pass in a more abstract object. Be it a ParameterObject for that specific type of object, or just a simple Registry, this should work. Again; not preferable and as you're using dependency injection, you probably already know why.
I do have to ask though; where's the harm in using your current way? I don't actually see the downside. I'd just go ahead and carry on. If you discover you're using even more parameters in the constructor, think about what the service's intentions are and why it needs it, chances are that particular pieces of behaviour can be moved to the objects you're requesting instead.
Well you could use singleton for the Session class and access it's instance anywhere, but that might not be an option if you want to restrict it's usage.
But then again Session::getInstance()->getStuff seem's pretty much the same as $this->session->getStuff so to answer your question - I don't think there's much of a choice here and you approach seems fine.
Oh and like Gordon said: you shouldn't change the order of the arguments.
I went through the same problem while developing my Abstract Controller Bundle — when controllers are defined as services — and ended up using the setter injection in the base class.

Categories