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.
Related
I am using current versions of Symfony (3.2.3) and Doctrine ORM (2.5.6) and I'm still trying to wrap my head around how to handle the deprecation of getEntityManager() and related methods correctly.
For instance, the current documentation states that this is the way to create a query builder instance:
$repository = $this->getDoctrine()->getRepository('AppBundle:Product');
$queryBuilder = $repository->createQueryBuilder('p')
But the way I see it (and my IDE seems to agree), getRepository() is type hinted to return an instance of ObjectRepository and the method createQueryBuilder() is not declared in that interface but in the implementing class EntityRepository.
An other example would be explicitly starting a transaction in a controller:
$this->getDoctrine()->getEntityManager()->getConnection()->beginTransaction()
I'm supposed to call getManager() instead of getEntityManager() because the latter method is deprecated. But getManager() is type hinted to return an instance of ObjectManager which does not declare getConnection().
PHP being PHP the code still seems to be working in all cases, but my IDE and I are left with an uneasy feeling. So, what is the correct way to create a query builder or begin a transaction in Symfony 3? Am I missing something?
Doctrine has a two types of Data Mappers: the ORM for the RDBMS and the ODM for document-oriented DBs (mostly for MongoDB). Data mappers try to achieve in a sort of persistence-ignorance and both of them implements a generic interfaces defined in the Doctrine\Common, such as ObjectManager or ObjectRepository.
In the Symfony the getDoctrine() method returns an instance of Registry class from the DoctrineBundle (by default). In short this service holds all available connections and entity managers. His method getManager() implemented in the Doctrine\Common, it don't knows what type of data mapper and declares return type as generic ObjectManager.
To avoid IDE warnings you can explicitly define the inline PHPDoc. Netbeans and PHPStorm are support it.
/* #var $entityManager Doctrine\ORM\EntityManager */
$entityManager = $this->getDoctrine()->getManager();
Other solution is a simple helper method:
public function getEntityManager(): EntityManager
{
return $this->getDoctrine()->getManager();
}
With above solutions we assume that requested service is actually from the ORM, so is a sort of type downcasting: actual class will be checked only in runtime. For the type-safety you can directly inject the entity manager in the your service.
If your problem is only your IDE, you can tell it that the return isn't an ObjectRepository with this, for example:
/** #var ProductRepository $repository */
$repository = $this->getDoctrine()->getRepository('AppBundle:Product');
$queryBuilder = $repository->createQueryBuilder('p');
Because you know that that's what you'll get.
try this:
$queryBuilder = $this->getDoctrine()->getManager()->getRepository('AppBundle:Product')->createQueryBuilder('p');
and update your symfony plugin if you are using phpstorm or ignore your ide
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()...
}
}
I have created several modular applications, which shared Bundles. One of these bundles responsible for handling all entity/repository related logic (lets call it MyBundle).
This MyBundle contains several custom hydrators. How can I register these hydrators in the bundle given that the doctrine configuration is contained in the specific applications and I don't wish to have to register the bundle hydrators in their config.yml files?
I've tried creating a CompilerPass in my bundle, then calling the addCustomHydrationMode on the doctrine orm configuration service but this doesn't work.
I also tried creating a 'HydrationManager' class, injecting the entity manager, then calling an addHydrator method on the manager in the compiler pass which in turn calls getConfiguration()->addCustomHydrationMode(...) but this also did not work
I was looking for a similar solution, and stumbled upon this example:
https://github.com/vivait/user-bundle/blob/master/src/Vivait/UserBundle/DependencyInjection/DoctrineCompilerPass.php
<?php
namespace Vivait\UserBundle\DependencyInjection;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
class DoctrineCompilerPass implements CompilerPassInterface
{
/**
* You can modify the container here before it is dumped to PHP code.
*
* #param ContainerBuilder $container
*
* #api
*/
public function process(ContainerBuilder $container)
{
$ormConfigDef = $container->getDefinition('doctrine.orm.configuration');
$ormConfigDef->addMethodCall(
'addCustomHydrationMode',
['UserCustomerHydrator', 'Vivait\UserBundle\Adapter\Hydrator\CustomerHydrator']
);
}
}
I am novice Laravel developer and I am trying to understand and apply the SOLID principles like a good programmer. So I recently learnt and applied the repository pattern in laravel.
To do this, I created a directory archive and loaded it with psr-4 like so:
"Archive\\": "archive/"
Then I created a folder called Repositories and another in it called Contracts. Now in the Contracts folder I have interfaces like UserRepositoryInterface and ServicesRepositoryInterface and so on, and outside in the Repositories folder, I have implementation like DbUserRepository and DbServiceRepository and so on.
I am using a service provider called DataServiceProvider where I bind these like so:
$this->app->bind(UserRepositoryInterface::class, DbUserRepository::class);
$this->app->bind(ServiceRepositoryInterface::class, DbServiceRepository::class);
So this way I can inject the Contacts like UserRepositoryInterface and ServiceRepositoryInterface in my controllers and Laravel automatically resolves my dependencies out of the IoC container. So if in the future I need a FileUserRepository, I just need to create that class and change the binding in my service provider and nothing will break in my controllers.
This is what I have learnt from Taylor and Jeffrey. But now I am trying to use a package https://github.com/andersao/l5-repository for my project.
According to this, I will extend my DbUserRepository with the BaseRepository which comes with it like so:
namespace App;
use Prettus\Repository\Eloquent\BaseRepository;
class UserRepository extends BaseRepository {
/**
* Specify Model class name
*
* #return string
*/
function model()
{
return "App\\Post";
}
}
Now in this I am obviously reusing all the code and power that comes with the BaseRepository like all(), panginate($limit = null, $columns = ['*']), find($id) and so on but now am I breaking the Inversion of Control Principle because now I will have to inject concrete implementations into my controller?
I am still a novice developer and trying to understand all this and may have gone wrong somewhere in the question when explaining things. What is the best way to go around using the package while also maintaining a loose coupling in the controllers?
There is no reason why you still can't implement your interface:
namespace App;
use Prettus\Repository\Eloquent\BaseRepository;
class DbUserRepository extends BaseRepository implements UserRepositoryInterface {
/**
* Specify Model class name
*
* #return string
*/
function model()
{
return "App\\Post";
}
}
However, you are now faced with a problem; if you swap out your implementation, there is nothing in your UserRepositoryInterface to say that the BaseRepository methods must also be implemented. If you take a look at the BaseRepository class, you should see that it implements two interfaces of it's own: RepositoryInterface and RepositoryCriteriaInterface and 'luckily' php allows for multiple interface inheritence which means you can extend your UserRepositoryInterface as follows:
interface UserRepositoryInterface extends RepositoryInterface, RepositoryCriteriaInterface {
// Declare UserRepositoryInterface methods
}
and you can then bind and use your interface as normal:
$this->app->bind(UserRepositoryInterface::class, DbUserRepository::class);
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)