How to optimise the Symfony4 Doctrine Repository usage inside the controller - php

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

Related

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.

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
}

How to get the EntityManager in Symfony 3, correctly?

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

PHPStorm and Slim 3.X DIC

I recently moved from Slim 2.X to Slim 3.X and I've found a problem that may be a little stupid but annoys me in some ways.
The newly Slim 3.X, as a difference between the older 2.X version, it implements a new container system using Dependency Injection Containers (DIC) build on Pimple.
As I've been reading, I find this a very great enhancement because it lets you manage your PHP app in a lot of different ways.
When I started playing around with the new things it has, I found something confusing that maybe is something I'm missing.
I use PHPStorm; one of the things that I like about this IDE is its code-completion and the facility for writing code and understanding classes (I'm actually a student, so this helps me a lot).
When I write a Slim route, if I want to access, for example, to my view object stored inside the container, I have to refer to it with the $this->view variable. The thing is that, normally, PHPStorm lists me the methods and properties inside an object when I mention it, but that didn't happen with the $this object.
I suppose that, inside a route, Slim stores all route functionalities and all the container objects into the $this assigner.
$container = $app->getContainer();
$container['view'] = new \Slim\Views\PhpRenderer('protected/views/');
$app->get('/products', function(Request $request, Response $response) {
$response = $this->view->render($response, 'products.php');
return $response;
})->setName('products');
When I access my /products route, it successfully renders my products template and shows what it's expected to show, so no problems with that.
The matter with it is that I want PHPStorm to know that $this variable inside a route it stores all the containers that are set previously before the route is called.
I thought about /* #var */ and /* #global */ or something like this but after searching for a while and trying different things, I haven't found anything that could work.
In conclusion, what I'm trying to say is if it's possible that PHPStorm could list me the properties and methods of the container objects like this:
but with the $this object inside a route:
Thanks!
The easiest way to do this is to have separate Action classes rather than use closures. This also has the benefit of being easier to test.
Firstly create your action, inject its dependencies into its constructor and write an `__invoke`` method that will be called by Slim:
class ProductsListAction {
protected $view;
public function __construct(\Slim\Views\PhpRenderer $view) {
$this->view = $view;
}
public function __invoke($request, $response, $args) {
$response = $this->view->render($response, 'products.php');
return $response;
}
}
For this to work, you now need a DIC factory for it:
$container['ProductsListAction'] = function ($c) {
return new ProductsListAction($c['view']);
};
You can now register your new action as a route callable:
$app->get('/products', 'ProductListAction');
Now, PhpStorm will correctly autocomplete within your ProductsListAction class.
I was trying to find something like this for Eclipse, and someone suggested you modify the properties with PHPDOCS of Slim\App. Since I didn't want to change the Slim files themself, I tried to make an empty class that extends Slim\App, and use PHPDOCS to add propeties to that:
/**
* OurApp
*
* Extends Slim\App with properties so we have code completion for a bunch of stuff!
*
* #property-read SomeClass $something
* #property-read SomeotherClass $someOtherThing
* #property-read string $someString
* #property-read \Slim\Views\PhpRenderer $renderer
*/
class OurApp extends \Slim\App {}
$app = new OurApp($settings);
And that works nicely. This way you can start typing $app-> and get the completion for whatever is in the standard Slim\App and get something, someOtherThing and someString, etc. For our project we changed to a bunch of values in the $container
items Dependency.php that we needed to access.

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