Stop related entities to be queried in doctrine2 while using QueryBuilder - symfony

I am having two entities User and Profile with one to one relationship.
$qb = $this->getDoctrine()->getEntityManager()->createQueryBuilder();
$qb->add('select', 'u')
->add('from', '\Acme\TestBundle\Entity\User u')
->add('orderBy', 'u.id DESC');
$query = $qb->getQuery();
$customer = $query->execute();
When i Check the number of queries in Symfony profiler I could see n number for queries triggered on Profile table for n users in User table. Is there any way where I can stop the querying of the Profile table.
Please let me know if there is better way of implementing it.
Thanks in advance
Added Entity Classes
class User
{
/**
* #var integer $id
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string $email
*
* #ORM\Column(name="email", type="string", length=255)
*/
private $email;
/**
* #var Acme\TestBundle\Entity\Profile
*
* #ORM\OneToOne(targetEntity="Acme\TestBundle\Entity\Profile", mappedBy="user")
*/
private $profile;
}
class Profile
{
/**
* #var integer $id
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var integer $user_id
*
* #ORM\Column(name="user_id", type="integer")
*/
private $user_id;
/**
* #var string $user_name
*
* #ORM\Column(name="user_name", type="string", length=100)
*/
private $user_name;
/**
* #var Acme\TestBundle\Entity\User
*
* #ORM\OneToOne(targetEntity="Acme\TestBundle\Entity\User", inversedBy="profile")
* #ORM\JoinColumn(name="user_id", referencedColumnName="id")
*/
private $user;
}
Response from the mysql log
120110 15:14:29 89 Connect root#localhost on test
89 Query SET NAMES UTF8
89 Query SELECT c0_.id AS id0, c0_.email AS email1, c0_.password AS password2, c0_.is_demo_user AS is_demo_user3, c0_.status AS status4, c0_.current_service AS current_service5, c0_.registration_mode AS registration_mode6, c0_.verification_code AS verification_code7, c0_.account_type AS account_type8, c0_.activated_date AS activated_date9, c0_.status_updated_at AS status_updated_at10, c0_.created_at AS created_at11, c0_.updated_at AS updated_at12 FROM user c0_ WHERE c0_.id = 1 ORDER BY c0_.email ASC
89 Query SELECT t0.id AS id1, t0.user_id AS user_id2, t0.user_name AS user_name3, t0.age AS age4, t0.created_at AS created_at5, t0.updated_at AS updated_at6, t0.user_id AS user_id7 FROM profile t0 WHERE t0.user_id = '1'
89 Quit

Your answer is here!!
http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/tutorials/extra-lazy-associations.html
"In many cases associations between entities can get pretty large. Even in a simple scenario like a blog. where posts can be commented, you always have to assume that a post draws hundrets of comments. In Doctrine 2.0 if you accessed an association it would always get loaded completly into memory. This can lead to pretty serious performance problems, if your associations contain several hundrets or thousands of entities.
With Doctrine 2.1 a feature called Extra Lazy is introduced for associations. Associations are marked as Lazy by default, which means the whole collection object for an association is populated the first time its accessed. If you mark an association as extra lazy the following methods on collections can be called without triggering a full load of the collection:"
<?php
namespace Doctrine\Tests\Models\CMS;
/**
* #Entity
*/
class CmsGroup
{
/**
* #ManyToMany(targetEntity="CmsUser", mappedBy="groups", fetch="EXTRA_LAZY")
*/
public $users;
}
It's a bit late, but it might help others out there!

Related

With ManyToOne relation entities, how may i access a foreign key id?

I'm trying to access to a foreign key stored in an entity using doctrine and querybuilder.
I got an entity named User which is linked to another entity called Client with a ManyToOne relationship.
I wanted to build a querybuilder that get me the field client_id in user table, that match the id of a client.
My User Entity :
/**
* AppBundle\EntityAppBundle\Entity\User
*
* #ORM\Table(name="user")
* #ORM\Entity(repositoryClass="AppBundle\Repository\UserRepository")
*/
class User extends FOSUser
{
/**
* #var integer $id
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
protected $id;
/**
* #var string
*
* #ORM\Column(name="nom", type="string", length=255)
*
*
*/
private $nom;
/**
*
* #ORM\ManyToMany(targetEntity="AppBundle\Entity\Etablissement", inversedBy="users")
*
*/
private $etablissements;
/**
*
* #ORM\ManyToOne(targetEntity="Client", inversedBy="users")
*
*/
private $client;
My Client Entity :
/**
* AppBundle\EntityAppBundle\Entity\Client
*
* #ORM\Table(name="client")
* #ORM\Entity()
*/
class Client{
/**
* #var integer $id
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="nom", type="string", length=255)
*
*
*/
private $nom;
/**
* #var string
*
* #ORM\Column(name="adresse", type="string", length=255)
*
*
*/
/**
*
* #ORM\OneToMany(targetEntity="AppBundle\Entity\User", mappedBy="client",
cascade={"persist"}, orphanRemoval=true)
*
*/
private $users;
In my database, my entity user has the client_id in foreign key column.
So in my queryBuilder in UserRepository, I do :
public function findClientIdViaUserId($myUserId, $myClientID)
{
return $this->createQueryBuilder('e')
->from('AppBundle:User', 'i')
->join('AppBundle:Client', 'c')
->where('c.id = :myClientID')
->andWhere('e.id = :myUserId')
->setParameter('myuserId', $myUserId)
->setParameter('myClientId', $myClientID)
->getQuery()
->getOneOrNullResult();
}
I expect to get the id of the client_id for a user_id.
Let's say that i wanted to get one client_id with the user_id called 1.
With my queryBuilder i got an error like :
[Syntax Error] line 0, col 67: Error: Expected Doctrine\ORM\Query\Lexer::T_WITH, got ','
How may I process to get the client_id from the user_id ?
Thank you for your replies !
Why don't you use EntityManager ?
$em = $this->getDoctrine()->getManager();
$user = $em->getRepository('AppBundle:User')->find(YOURUSERID);
$client = $user->getClient();
You made a mistake in chaining of Doctrine methods. You've already set an alias for User entity by calling createQueryBuilder('e'). Repository knows about entity it linked to.
When you call from('AppBundle:User', 'i') - an alias for User entity is i now. That's why Doctrine is throwing an error about wrong syntax in resulting DQL.
So, try this piece of code:
return $this
->createQueryBuilder('e')
->join('e.client', 'c')
->where('c.id = :myClientID')
->andWhere('e.id = :myUserId')
->setParameter('myUserId', $myUserId)
->setParameter('myClientId', $myClientID)
->getQuery()
->getOneOrNullResult();

Symfony join entities in one-to-many relationship

I am working on a Symfony project recording sales as relating to stock.
My reasoning:
one sale item is associated to one stock item
one stock item can be associated to multiple sale items
As a result, I setup a one-to-many sale-to-stock relationship as show in the following code snippets:
class Sale
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var float
*
* #ORM\Column(name="cost", type="float")
*/
private $cost;
/**
* #var \DateTime
*
* #ORM\Column(name="date", type="datetime")
*/
private $date;
/**
* #ORM\ManyToOne(targetEntity="iCerge\Salesdeck\StockBundle\Entity\Stock", inversedBy="sales")
* #ORM\JoinColumn(name="sid", referencedColumnName="id")
*/
protected $stock;
...
... and ...
class Stock
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="name", type="string", length=255)
*/
private $name;
/**
* #var \DateTime
*
* #ORM\Column(name="created", type="datetime")
*/
private $created;
/**
* #var \DateTime
*
* #ORM\Column(name="updated", type="datetime")
*/
private $updated;
/**
* #ORM\OneToMany(targetEntity="iCerge\Salesdeck\SalesBundle\Entity\Sale", mappedBy="stock")
*/
protected $sales;
...
NOW, if my code for implementing a one-to-many relationship is correct, I am trying to load a sale object with it's associated stock data in one query using the following code:
public function fetchSale($sid){
$query = $this->createQueryBuilder('s')
->leftjoin('s.stock', 't')
->where('s.id = :sid')
->setParameter('sid', $sid)
->getQuery();
return $query->getSingleResult();
}
The fetchSale function is from my projects SaleRepository.php class.
The leftjoin part of the query is what I hoped would successfully fetch the related stock information but I just get no output ([stock:protected]) as is shown below:
myprog\SalesProj\SalesBundle\Entity\Sale Object
(
[id:myprog\SalesProj\SalesBundle\Entity\Sale:private] => 50
[cost:myprog\SalesProj\SalesBundle\Entity\Sale:private] => 4.99
[date:myprog\SalesProj\SalesBundle\Entity\Sale:private] => DateTime Object
(
[date] => 2015-04-18 17:12:00
[timezone_type] => 3
[timezone] => UTC
)
[stock:protected] =>
[count:myprog\SalesProj\SalesBundle\Entity\Sale:private] => 5
)
How I can successfully fetch a sales' related stock data in the same query?
Doctrine is lazy-loading by default, so it's possible that $stock hasn't been initialized and the dump is not showing it. Try dumping $sale->getStock(). That tells Doctrine to go fetch it.
You can also force the loading of the Stock by selecting it:
public function fetchSale($sid){
$query = $this->createQueryBuilder('s')
->leftjoin('s.stock', 't')
->where('s.id = :sid')
->setParameter('sid', $sid)
->select('s', 't')
->getQuery();
return $query->getSingleResult();
}
By the way, fetchSale($sid) as it is now is the same as calling:
$entityManager->getRepository('SalesBundle:Sale')->find($sid);
You can use Doctrine's eager loading feature.
1. Always load associated object:
If you always want to fetch the stock object when loading the sale object, you can update your entity definition (see docs for #ManyToOne) by adding a fetch="EAGER" to the #ManyToOne definition:
/**
* #ORM\ManyToOne(targetEntity="iCerge\Salesdeck\StockBundle\Entity\Stock", inversedBy="sales", fetch="EAGER")
* #ORM\JoinColumn(name="sid", referencedColumnName="id")
*/
protected $stock;
Doctrine will then take care of loading all required objects in as few queries as possible.
2. Sometimes load associated object:
If you want to load the associated object only in some queries and not by default, according to the manual you can also tell Doctrine to use eager loading on a specific query. In your case, this may look like:
public function fetchSale($sid){
$query = $this->createQueryBuilder('s')
->where('s.id = :sid')
->setParameter('sid', $sid)
->getQuery();
$query->setFetchMode("FQCN\Sale", "stock", \Doctrine\ORM\Mapping\ClassMetadata::FETCH_EAGER);
return $query->getSingleResult();
}

Error when persisting doctrine entity with OneToMany association

New entities in a collection using cascade persist will produce an Exception and rollback the flush() operation. The reason is that the "UserGroupPrivilege" entity has identity through a foreign entity "UserGroup".
But if the "UserGroupPrivilege" has its own identity with auto generated value the code works just fine, and I don't want that I want the identity to be a composite key to enforce validation. here is my code:
Entity UserGroup:
class UserGroup
{
/**
* #var integer
*
* #ORM\Column(type="integer", nullable=false)
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id;
/**
* #var integer
*
* #ORM\Column(type="boolean", nullable=false)
* #Type("integer")
*/
private $active;
/**
* #ORM\OneToMany(targetEntity="UserGroupPrivilege", mappedBy="userGroup", cascade={"persist"})
*/
private $privileges;
Entity UserGroupPrivilege:
class UserGroupPrivilege
{
/**
* #var integer
*
* #ORM\Id
* #ORM\Column(type="integer", nullable=false)
*
*/
private $privilegeId;
/**
* #var UserGroup
*
* #ORM\Id
* #ORM\ManyToOne(targetEntity="UserGroup", inversedBy="privileges")
* #ORM\JoinColumn(name="userGroupId", referencedColumnName="id")
*/
private $userGroup;
/**
* #var string
* #ORM\Column(type="string", nullable=false)
*/
private $name;
/**
* #var string
* #ORM\Column(type="string", nullable=false)
*/
private $value;
Controller:
$userGroup = new UserGroup();
$userGroupPrivilege = new UserGroupPrivilege();
userGroupPrivilege->setUserGroup($userGroup)
->setName($arrPrivilege['name'])
->setValue($arrPrivilege['value'])
->setPrivilegeId($arrPrivilege['privilegeId']);
$userGroup->addPrivilege($userGroupPrivilege);
$data = $repo->saveUserGroup($userGroup);
return $data;
Repository:
$em = $this->getEntityManager();
$em->persist($userGroup);
$em->flush();
I get the following error:
Entity of type UserGroupPrivilege has identity through a foreign entity UserGroup, however this entity has no identity itself. You have to call EntityManager#persist() on the related entity and make sure that an identifier was generated before trying to persist 'UserGroupPrivilege'. In case of Post Insert ID Generation (such as MySQL Auto-Increment or PostgreSQL SERIAL) this means you have to call EntityManager#flush() between both persist operations.
Error message is pretty self explanatory. To relate UserGroupPrivilege to UserGroup, UserGroup must have it's ID set. However, since you've just created both entities it has no id because it hasn't been persisted to database yet.
In your case :
$em = $this->getEntityManager();
$em->persist($userGroup);
$em->persist($userGroupPrivilege);
$em->flush();
Can you "enforce validation" with unique constraint:
/**
* #Entity
* #Table(uniqueConstraints={#UniqueConstraint(name="ugppriv_idx", columns={"priviledgeId", "userGroup"})})
*/
class UserGroupPriviledge
{
...

Entity containing other entities without having a table

I have an Entity ( Invoice ) which is purely for calculation purposes and has no table, that associates with two other entities having relations by tables. (Although there are so many other entities involved ).
class Row{
/**
* #var integer
*
* #ORM\Column(name="row_id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\ManyToOne(targetEntity="File")
* #ORM\JoinColumn(name="file_id", referencedColumnName="file_id")
*/
protected $file;
/**
* #var \DateTime
*
* #ORM\Column(name="date", type="date")
*/
private $date;
}
class File
{
/**
* #var integer
*
* #ORM\Column(name="file_id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="name", type="string", length=255)
*/
private $name;
}
class Invoice
{
/**
* #ORM\Id
* #ORM\Column(name="invoice_id", type="integer")
* #ORM\GeneratedValue
*/
protected $id = null;
/**
* #ORM\OneToMany(targetEntity="Row", mappedBy="row_id")
*/
protected $row;
/**
* #ORM\OneToMany(targetEntity="File", mappedBy="file_id")
*/
protected $file;
}
I want to be able to query for Invoices :
$sDate = //Some date
$this->getEntityManager()
->createQuery("SELECT Invoice, Row, File
FROM
ReportsEntitiesBundle:Invoice Invoice
LEFT JOIN
Row.row Row
LEFT JOIN
Row.file File
WHERE date=:date"
)
->setParaMeter(':date', $sDate)
->setFirstResult($iPage*$iLimit)
->setMaxResults($iLimit)
->getResult();
The questions :
# Doctrine tries to query the database, how can I prevent that and have it find the relevant entities?
# How can I relate the date ( which is in Row entity and cannot be in Invoice ) to the query?
Later this Invoice will become a part of another big entity for calculating/search purposes.
Thank you
Short Answer: You can't
Long Answer : You can't because an entity with #ORM annotations means its persisted to a database - querying that entity relates to querying a database table. Why not just create the table ?!?!?
You need somewhere to persist the association between file and row - a database table is a perfect place !!!!
Update
Just to clarify ... an Entity is just a standard class - it has properties and methods ... just like any other class - When you issue doctrine based commands it uses the annotations within the entities to configure the tables / columns / relationships etc if remove those you can use it however you like ... but you will need to populate the values to use it and you wont be able to use it in a Doctrine query and it obviously wont be persisted !
You can use a read-only entity. It's contents are backed by a view which you create manually in SQL.
PHP:
/** #ORM\Entity(readOnly =true) */
class InvoiceView
{ ...
SQL:
CREATE VIEW invoice_view AS (
SELECT ...

Symfony2 Doctrine2 column being set to null

I have a system that took form information detailing a project, added it to a project table and is meant to add an entry into an assigned projects table to associate user with project (point of this is allowing multiple users for each project). Anyway I got this working without foreign keys, struggled to add them but eventually got them.
Unfortunately this additional has caused this error 'SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'projectId' cannot be null' whenever something is added to the assignedProjects table.
So my question is, have I missed something in my codes?
The code to add a new row to assignedProjects:
$assignedProject = new AssignedProjects();
$assignedProject->setProjectId($project->getId());
$assignedProject->setUserId($user[0]['id']);
$em = $this->getDoctrine()->getEntityManager();
$em->persist($assignedProject);
$em->flush();
The code for the assignProjects entity:
class AssignedProjects
{
/**
* #var integer $id
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var integer $projectId
*
* #ORM\Column(name="projectId", type="integer")
*/
private $projectId;
/**
* #ORM\ManyToOne(targetEntity="Projects", inversedBy="assignment")
* #ORM\JoinColumn(name="projectId", referencedColumnName="id")
*/
private $project;
/**
* #var integer $UserId
*
* #ORM\Column(name="userId", type="integer")
*/
private $userId;
/**
* #ORM\ManyToOne(targetEntity="Dev\UserBundle\Entity\User", inversedBy="assignment")
* #ORM\JoinColumn(name="userId", referencedColumnName="id")
*/
private $user;
(followed by the usual getters and setters)
and the project tables entity is:
class Projects
{
/**
* #var integer $id
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string $projectName
*
* #ORM\Column(name="projectName", type="string", length=255)
*/
private $projectName;
/**
* #ORM\OneToMany(targetEntity="AssignedProjects", mappedBy="project")
*/
protected $assignment;
Any help would be much appreciated!
Either you use the ProjectId and UserId columns and manage the relationship manually (not recommended) or you use the doctrine relationships(recommended), but don´t do both things. If you go for the second option, don´t include the projectId and userId columns, they are automatically created for you by doctrine. So, your AssignedProjects class should be:
class AssignedProjects {
/**
* #var integer $id
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id * #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\ManyToOne(targetEntity="Projects", inversedBy="assignment")
* #ORM\JoinColumn(name="projectId", referencedColumnName="id")
*/
private $project;
/**
* #ORM\ManyToOne(targetEntity="Dev\UserBundle\Entity\User", inversedBy="assignment")
* #ORM\JoinColumn(name="userId", referencedColumnName="id")
*/
private $user;
and in your controller you would do:
$assignedProject = new AssignedProjects();
$assignedProject->setProject($project);
$assignedProject->setUser($user[0]);
$em = $this->getDoctrine()->getEntityManager();
$em->persist($assignedProject);
$em->flush();
Note that I am setting the Project and User fields, not the ids
By the way, unless you need to save extra data about this project assignement (things like the date or similar), you can declare a direct ManyToMany relationship between User and Project and do away with this class, Doctrine would generate the needed table by itself
With Doctrine2, you don't have to declare the foreign key (projectId) but only the association (project). So you can delete $projectId property, as well as setProjectId ans getProjectId methods. Same fix for $user...
Instead, you will use setProject like that :
$assignedProject = new AssignedProjects();
$assignedProject->setProject($project);
$assignedProject->setUser($user[0]);
$em = $this->getDoctrine()->getEntityManager();
$em->persist($assignedProject);
$em->flush();
Have a look to Doctrine2 documentation, it will help you, for sure !
http://docs.doctrine-project.org/projects/doctrine-orm/en/2.1/reference/association-mapping.html

Resources