How to get the EntityManager in Symfony 3, correctly? - php

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

Related

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.

How to use symfony, twig, and doctrine in a controller inside an existing project

I have been using the SymfonyCast on Symfony 4 to try and help me to connect the pieces. I have learned a lot about doctrine Symfony and twig and the environment that they are supposed to work together in. However, that is not what I have to build on. I need to build inside an existing project OpenEMR.
I posted my project code here:
https://github.com/juggernautsei/symfony_twig_doctrine_component/blob/master/library/financialreports/src/FinancialSummaryByInsuranceController.php
The controller works to load the twig template. Now, I am trying to populate the controller with data from the database. I have built the entity class and the repository class. I just can't figure out what to put in this line.
public function getpaidata($insurerid)
{
$payments = $this->repository
}
To access the class in the repository. The IDE suggested the code in the repository class.
public function getInsurerPaid(ArSession $insurerId)
{
/*$insurerPaid = $this->_em->getRepository($this->_entityName)->findBy([
"payer_id" => $insurerId
]);*/
$insurerPaid = $this->findBy([
'payer_id' => $insurerId
]);
return $insurerPaid;
}
But as I am typing in out the code in the controller, the IDE PHPStorm is not suggesting anything. So, I am stuck. I have tried the suggested code here
https://symfony.com/doc/2.0/book/doctrine.html#creating-an-entity-class
https://symfonycasts.com/screencast/symfony-doctrine/repository
but nothing tells me how to access the method that is in the repository class.
UPDATE:
The getpaiddata() method is now changed to
/**
* #return Fully hydrated object.
*/
public function getpaidata($insurerid)
{
$row = $this->repository->findBy([
"payer_id" => $insurerid
]);
return $row;
}
The problem is likely how you get $this->repository. If you fetch it via the entity manager, via $this->_em->getRepository($entityName) like in the commented snippet, the return value has a type hint which tells the IDE that it is just a generic EntityRepository instead of your custom repository class.
You can install the Symfony-plugin to PhpStorm, which will give you better autocompletion if your entity has the right annotation #Entity(repositoryClass="...").
In a typical Symfony 4 application you could also just inject the correct repository, instead of the EntityManager, e.g. in your constructor:
public function __construct(PaidDataRepository $repository)
{
$this->repository = $repository;
}
From what it looks like, OpenEMR has it's own way of creating the EntityManager using Connector::instance(). So this will probably not work for you easily, unfortunately.
Another way around this would be to just place a type hint above assigning your variable:
/** #var App\Repository\PaidDataRepository $repository */
$repository = $this->_em->getRepository(PaidData::class)
or, since you have a class variable you can put a similar annotation on there.

How to optimise the Symfony4 Doctrine Repository usage inside the controller

I'm looking for a optimised usage of the Doctrine Repositories inside the Symfony 4 Controllers.
At the moment i've to build the code like this:
/** #var ArticleRepository $repository */
$repository = $this->getRepository(Article::class);
$articles = $repository->findBySearchterm($search_term);
To tell truth, i don't like this approach. If i have to use $this->getRepository(Article::class), i've to tell PHPStorm through extra annotation, that the return of that method is of type ArticleController. Otherwise PHPStorm warns me, that the called method ->findBySearchterm($search_term); is unknown.
I would like to optimise this and use the ArticleRepository directly, maybe like this: ArticleRepository::findBySearchterm($search_term);
Is there a chance to build something, to access the Repository directly without the overhead of fetching the repository? In my opinion it would also increase the readability of the code.
You can inject the repository directly into the controller method like so:
public function index(ArticleRepository $repository)
{
$articles = $repository->findBySearchterm($search_term);
// The rest of the code
}
This is done by symfony autowiring

Utility functions that a Symfony entity should have

I don't know maybe it is me or may be it is symfony that is lame, please why is symfony entity class not having utility functions like these:
//Assume product is an entity class
$product = Product::findById($id);
$productExample = new Product(array("price"=>20, "vendor"=>'friends'));
$products = Product::findByExample($productExample);
$Dql = "SELECT * FROM product p WHERE p.id IN (SELECT si.pid FROM solditem si WHERE si.sold_date BETWEEN 1-1-2017 AND 2-2-2017 ORDER BY si.price)";
$products = Product::findByDql($Dql) or Product::findBySql($sql);
$product = new Product(array('id' => $id)); // I should be able to get the entity with that Id
In other frameworks like Zend (even small and simple ones like cakePHP, codeIgniter) it is very easy to implement functions like these and they will be callable everywhere, you will not have to be running after one entityManagers or entityCEOs.
Is there an easy way to implement way to implement these in symfony, if there is please let me know, if not please give me why.
Thanks
In general I suggest you read a little bit more about the documentations of the frameworks you are comparing.
I don't know maybe it is me or may be it is symfony that is lame, please why is symfony entity class not having utility functions like these
To be honest in this case it is you ;-) ... no offense, but the systems are implementing different storage layers. Other frameworks like CodeIgniter, CakePHP and Laravel are implementing the Active Record Pattern. The latter e.g. by providing the Eloquent ORM
The Eloquent ORM included with Laravel provides a beautiful, simple ActiveRecord implementation for working with your database. Each database table has a corresponding "Model" which is used to interact with that table.
Symfony on the other hand is using Doctrine 2 ORM as storage layer:
As the term ORM already hints at, Doctrine 2 aims to simplify the translation between database rows and the PHP object model. The primary use case for Doctrine are therefore applications that utilize the Object-Oriented Programming Paradigm. For applications that do not primarily work with objects Doctrine 2 is not suited very well.
You can make up your own mind which one you favour, but it is not an easy task to swap the storage layer in these frameworks. If you search for it I think you will find some discussions around this topic.
In Symfony Entity Repositories are usually the place where you define your desired functionality:
When you query for a particular type of object, you always use what's
known as its "repository". You can think of a repository as a PHP
class whose only job is to help you fetch entities of a certain class.
Create a custom repository class following the official documentation or take it a step further and set up your own repository class without using Doctrines EntityRepository as described in this highly recommended article.
There is no one preventing you from adding static functions to it, but the recommended way is to make a service out of your repository, e.g.
Repository
// src/AppBundle/Product/DoctrineBasedProductRepository.php
namespace AppBundle\Product;
use Doctrine\ORM\EntityManager;
class DoctrineBasedProductRepository implements ProductRepository
{
private $entityManager;
public function __construct(EntityManager $entityManager)
{
$this->entityManager = $entityManager;
}
public function all()
{
// Create a basic DQL query to fetch all entities
return $this->entityManager->createQuery('SELECT p FROM '.Product::class.' p')
->getResult();
}
...
}
Service definition
# app/config/services.yml
services:
app.product_repository:
class: AppBundle\Product\DoctrineBasedProductRepository
arguments: ['#doctrine.orm.entity_manager']
Now you can use the repository e.g. in your controller:
public function listAction()
{
$products = $this->get'app.product_repository')->all();
// ... do something, like pass the $products object into a template
}

Symfony2: Get Service Inside Entity

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)

Categories