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
}
Related
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
For my company I'm working on a Symfony webapp that needs to fetch users and businesses from the database.
However I am forced to use an ODBC connection. I went with pdo_odbc and this is working fine from the controller (Connection, queries, fetching, rendering).
But I'm a bit lost on how to use Entities and Repositories if I'm unable to use Doctrine to connect.
So what I'm doing now is connecting to the database, do queries, fetching data and rendering to the views in one controller function. Without using Entities or Repositories, this is obviously not what it should be.
If any of you need more information feel free to comment.
PS: I'm new to Symfony in general, so examples are much appreciated
We're doing the same stuff on some projects.
We've done the following steps:
Write Repositories, but instead of using the Doctrine ORM and QueryBuilder, we just use the PDO connection with PDO statements.
We create Models instead of Entites. Every Model has a "setFromDb()" function which receives an array of data. Those informations will be matched to the attributes of the Model.
Within services, Controller whatsoever we're only working with the models.
The repositories we've written just are classes that are defined as services and will receive the doctrine connection as constructor injection. Then you can get the connection and work with PDO.
Example for a repository class (note that we just call them repositories because they handle the database queries, but they have nothing to do with the Repository classes from Symfony / Doctrine)
class DemoRepositoryClass
{
private $connection;
public function __construct(Registry $doctrine)
{
// Get the database connection from doctrine
$this->connection = $doctrine->getConnection();
}
public function test()
{
$query = "SELECT * FROM XXX WHERE FOO = 'BAZ";
$stmt = $this->conncetion->prepare($query);
// [...] Do as always with PDO;
}
}
This class will be defined as service in the services.yml
app.demo_repository_class:
class: AppBundle\Path\DemoRepositoryClass
arguments: ['#doctrine']
public: true
With that, you can call the service functions from the controller as example:
// In a controller action/function
$this->get('app.demo_repository_class')->test();
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
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.
I'm trying to practice a good design and extending Doctrine entity.
My extended class, the model basically, will have extra business logic + access to the entity basic data.
I am using Doctrine 2.2.1 & Zend Framework 1.11.4 & php 5.3.8
When I use DQL, doctrine return successfully the Model entity.
When I use Doctrine native find() function, it returns nothing :(.
HELP...
This is how it rolls:
Bootstrap.php:
$classLoader = new \Doctrine\Common\ClassLoader('Entities', APPLICATION_PATH.'/doctrine');
$classLoader->register();
$classLoader = new \Doctrine\Common\ClassLoader('Models', APPLICATION_PATH);
$classLoader->register();
Model in APPLICATION_PATH\models\User.php:
namespace Models;
use Doctrine\ORM\Query;
/**
* Models\User
*
* #Table(name="user")
* #Entity
*/
class User extends \Entities\User {
public function __wakeup() {
$this->tools = new Application_App_Tools();
}
Entity retrieval functions:
DOESN'T WORK:
$userEntity = $registry->entityManager->find('Models\User', $userEntity);
WORKS:
$qry = $qb
->select('u')
->from('Models\User','u');
You shouldn't add the business logic to the entities, you should use models for that instead. One way of doing it would be:
Use models for business logic.
Create custom Doctrine 2 repositories for your all your database queries (DQL or otherwise) [1].
Leave your entities alone.
In practise this means that models are plain PHP classes (or maybe framework extended depending on what your're using) but your models have no relation to your database. Your models do however, instantiate your custom Doctrine 2 repositories. E.g. a UserRepository might contain a method called getUserById. Within your repositories is where your run your actual queries and return entity instances for the models to work with.
[1] http://docs.doctrine-project.org/en/latest/reference/working-with-objects.html#custom-repositories
As I understand Doctrine, entityManager is responsible only for persistent entities, and extending Entities\User entity with Model\User will create another entity (stored in same table as stated in docblock), but not managed by entityManager or in collision with it because you probably didn't mention #InheritanceType("SINGLE_TABLE") in Entities\User docblocks:
Read this docs for more info http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/inheritance-mapping.html
What I tried to do was a bad practice.
I coupled my DB entity and tools from zend as #Ivan HuĆĄnjak mentioned.
What should be done is de-coupling.
Business logic should be in services\controller and these should address the entity and it's methods.
you can\should add helper functions to the doctrine entity that only relates to the entity properties.
Regarding my main purpose (to have an entity class that the Doctrine CLI can rewrite & update):
doctrine only searches from changes in the native fields\methods, updates them accordingly and discard all other functions (helpers). so there is no problem when letting doctrine update the php entity!
p.s.
move to symfony2.