I'm trying resolve following problem.
I have OrderEntity and in OrderEntity is relation ship to OrderStatusHistory (OneToMany).
/**
* #ORM\OneToMany(targetEntity="OrderStatusHistory", mappedBy="order")
*/
protected $statuses;
OrderStatusHistory entity:
class OrderStatusHistory extends BaseEntity
{
/**
* #ORM\ManyToOne(targetEntity="Order",cascade={"persist"})
* #ORM\JoinColumn(name="order_id", referencedColumnName="id")
**/
protected $order;
/**
* #ORM\ManyToOne(targetEntity="OrderStatus",cascade={"persist"})
* #ORM\JoinColumn(name="status_id", referencedColumnName="id")
**/
protected $status;
/**
* #ORM\Column(type="datetime")
*/
protected $time;
public function __construct()
{
$this->time = new \DateTime('now');
}
}
And now i need select order where was last status with ID 4. I'm tried many variants with QB, but any of them does not work.
There's several ways, you can join to that table and use WITH, or you can use WHERE, or you can just use the ID. Here's some examples:
Using DQL:
$dql = "SELECT o
FROM Order o
WHERE o.orderStatus = 4"
or
$dql = "SELECT o
FROM Order o
INNER JOIN o.orderStatus orderStatus
WITH orderStatus.id = 4"
OR:
$dql = "SELECT o
FROM Order o
INNER JOIN o.orderStatus orderStatus
WHERE orderStatus.id = 4"
Or Query Builder:
$queryBuilder->select('o')
->from('Order', 'o')
->where('o.orderStatus = 4');
Related
I have two entities Person and Skill, where a Person may have multiple skills.
Person
/**
* #ORM\Entity(repositoryClass="App\Repository\PersonRepository")
*/
class Person
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\ManyToMany(targetEntity="App\Entity\Skill", inversedBy="people")
*/
private $skills = [];
// other fields and getters/setters
}
Skill
<?php
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity(repositoryClass="App\Repository\SkillRepository")
*/
class Skill
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\ManyToMany(targetEntity="App\Entity\Person", mappedBy="skills")
*/
private $people = [];
// other fields and getters/setters
}
I have a form where I can filter people by skills, each skill is a checkbox and I want the number of people having that skill along with the checkbox's label.
The result is that:
I got it working using the following native query:
SELECT s.id, COUNT(*) AS c
FROM skill s
JOIN person_skill ps /* table required by the M2M relation between person and skill */
ON s.id = ps.skill_id
GROUP BY s.id
As you can see, I require a JOIN on the ManyToMany table in order to get those counts.
How could I do this using Doctrine's DQL instead of using a native query?
Actually when mapping entities with relations, Doctrine uses a custom object named ArrayCollection.
It comes with many methods, such as filter() and count().
You can add a method to your skill entity if you want that would use the count method of the ArrayCollection (people).
To make sure you use the ArrayCollection properly you'll have to change your Skill class like this:
class Skill
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\ManyToMany(targetEntity="App\Entity\Person", mappedBy="skills")
*/
private $people; //<-- Removed the default array definition
public function __construct()
{
$this->people = new ArrayCollection(); //Add this line in your constructor
}
public function countPeople()
{
return $this->people->count(); //Will return the number of people joined to the skill
}
// other fields and getters/setters
}
Allright, I found the solution:
$rows = $this->_em->createQueryBuilder('s')
->select('s.id, COUNT(p.id) AS c')
->from(Skill::class, 's')
->join('s.people', 'p')
->groupBy('s.id')
->getQuery()
->getArrayResult();
It generates the following query:
SELECT s0_.id AS id_0, COUNT(p1_.id) AS sclr_1
FROM skill s0_
INNER JOIN person_skill p2_
ON s0_.id = p2_.skill_id
INNER JOIN person p1_
ON p1_.id = p2_.person_id
GROUP BY s0_.id
I have a trouble to implement this SQL Request :
SELECT *
FROM project
LEFT JOIN user
ON project.idAuthor=user.id
WHERE project.isVisible = 1 AND
user.role = 'agency'
To a simple Symfony Query Builder :
$query = $this->createQueryBuilder('p')
->leftJoin('WebAwardsBundle:User', 'u')
->where('p.isVisible = 1')
->andwhere("u.role = 'agency'")
->orderBy('p.id', 'DESC')
->getQuery();
The response of this query give me all the project, include the project when role !== agency ...
I don't know where I can put the ON project.idAuthor=user.id
Mapping :
Project :
/**
* Project
*
* #ORM\Table(name="project")
*#ORM\Entity(repositoryClass="WebAwardsBundle\Repostory\ProjectRepository")
*/
class Project
{
...
/**
* #var int
*
* #ORM\ManyToOne(targetEntity="User", inversedBy="projects")
* #ORM\JoinColumn(name="idAuthor", referencedColumnName="id")
*/
private $idAuthor;
...
User :
/**
* User
*
* #ORM\Table(name="user")
* #ORM\Entity(repositoryClass="WebAwardsBundle\Repository\UserRepository")
*/
class User implements UserInterface, \Serializable{
...
/**
* #ORM\OneToMany(targetEntity="Project", mappedBy="idAuthor")
*/
private $projects;
...
The correct answer to implement this SQL Request :
SELECT *
FROM project
LEFT JOIN user
ON project.idAuthor=user.id
WHERE project.isVisible = 1 AND
user.role = 'agency'
is to join the correct column of the entity (project.idAuthor to user in this case) :
$query = $this->createQueryBuilder('p')
->join('p.idAuthor', 'u')
->where('p.isVisible = 1')
->andWhere("u.role = 'agency'")
->orderBy('p.id', 'DESC')
->getQuery();
i have 2 entity with one-to-many relation : Article & ArticleCategory
class Article {
/**
* #var integer
*
* #ORM\Column(name="rate", type="integer",options={"default" : 0})
*/
private $rate = 0;
/**
* #var \ArticleCategory
*
* #ORM\ManyToOne(targetEntity="ArticleCategory",inversedBy="articles")
* #ORM\JoinColumn(name="article_category_id", referencedColumnName="id")
*/
private $category;
}
class ArticleCategory {
/**
*
* #var \Article
* #ORM\OneToMany(targetEntity="Article", mappedBy="category")
*/
protected $articles;
}
now i want to fetch categories which has much articles with top rates..
" i mean top N categories ordered by highest rated article in them (categories which has more articles with rate above the average rate)"
how can i do this?
finally i found this for my question, maybe it can be useful for others! :)
$query = $em->createQuery('SELECT c, avg(a.rate) x
FROM YoutabsGeneralModelBundle:ArticleCategory c
JOIN c.articles a
GROUP BY c.id
ORDER BY x DESC');
i add ORDER BY because i wanted to set limit on this :
$query->setMaxResults($limit);
I've got a doctrine2 problem with a MySQL database:
I have a model User and a model Documents. Each User may have 0 or n Documents. Each Document is assigned to exactly one User. My models:
User
<?php
namespace Entity;
/**
* #Entity(repositoryClass="Entity\Repository\UserRepository")
* #Table(name="user")
*/
class User extends Object
{
/**
* #Id #GeneratedValue(strategy="UUID")
* #Column(type="string", length=36)
* #var string
*/
protected $id;
/**
* #OneToMany(targetEntity="\Entity\Document", mappedBy="user")
*/
private $documents;
public function __construct($options = array())
{
$this->documents = new \Doctrine\Common\Collections\ArrayCollection;
}
}
Document
<?php
namespace Entity;
/**
* #Entity(repositoryClass="Entity\Repository\DocumentRepository")
* #Table(name="document")
*/
class Document extends Object
{
/**
* #Id #Column(type="string")
* #var string
*/
protected $id;
/**
* #ManyToOne(targetEntity="\Entity\User", inversedBy="documents")
* #JoinColumn(name="user_id", referencedColumnName="id")
* #var User
*/
private $user;
}
Now I want to get the User of a given Document ID. The SQL-query would be:
SELECT u.*
FROM `user` u
INNER JOIN `document` d ON d.user_id = u.id
WHERE d.id = 'mydocumentid'
But this does't work:
$user = $queryBuilder
->select('u.*')
->from('\\Entity\\User', 'u')
->innerJoin('\\Entity\\Document', 'd', \Doctrine\ORM\Query\Expr\Join::ON, 'd.user_id = u.id')
->where('d.id = :documentId')
->setParameter('documentId', 'mydocumentid')
->setMaxResults(1)
->getQuery()
->getSingleResult();
Also the direct query doesn't work:
$query = $em->createQuery('
SELECT
u.*
FROM
Entity\\User u
INNER JOIN
Entity\\Document d ON d.user_id = u.id
WHERE
d.id = "mydocumentid"
');
Could you please help me to get this run?
Error message
[Semantical Error] line 0, col 66 near 'd ON d.user_id': Error: Identification Variable \Entity\Document used in join path expression but was not defined before.
Instead of using:
->innerJoin('\\Entity\\Document', 'd', \Doctrine\ORM\Query\Expr\Join::ON, 'd.user_id = u.id')
Try using this:
->innerJoin('u.documents', 'd', \Doctrine\ORM\Query\Expr\Join::ON, 'd.user_id = u.id')
This is because Doctrine needs to know to which field in user the documents have to be joined. Knowing the field Doctrine will fetch the target entity and that way Doctine directly knows the class.
I'm trying to do an association of 5 objects with Doctrine2 (PHP).
I'm using PostgreSQL.
Here is the Database schema:
Database schema
A company may have many Hubs, each Hub have one Harbor.
A company may have many Line, each Line have a Linelist.
A Linelist have 2 Harbors.
For example, a Linelist is "Los Angeles-Seattle", and multiple companies may own it thanks to the Line table.
I'm trying to query all the Hub, Harbor, Linelist, and Line for one company.
I have the SQL query:
SELECT *
FROM hub h
JOIN harbor a
ON a.id = h.harbor_id
JOIN linelist l
ON (l.harborstart_id = a.id OR l.harborend_id = a.id)
JOIN line m
ON m.linelist_id = l.id
WHERE h.company_id = 41
AND m.company_id = 41"
I'm trying to do the same using DQL.
I tried this, but it doesn't worked:
$query = $this->getEntityManager()
->createQuery('SELECT h, a, l, m
FROM AmGameBundle:Hub h
JOIN h.harbor a
JOIN a.linelist l
JOIN l.line m
WHERE h.company = :company_id
AND m.company = :company_id')
->setParameter('company_id', $company_id);
As a result, I only have the LineList and Line objects matching harborstart_id, but I want the one matching either harborstart_id or harborend_id.
Do you think this is possible in DQL?
It might be better to change the relation between Harbor and Linelist for a many to many?
I think that's a matter of defining 2 relations from harbor to linelist in your Entities. I imagine you have something like
<?php
/**
* #Entity
* #Table(name="LineList")
*/
class LineList {
/**
* #var object $startHarbor
* #ManyToOne(targetEntity="Harbor", inversedBy="startHarbors")
* #JoinColumn(name="harborstart_id", referencedColumnName="id", nullable=FALSE)
*/
protected $startHarbor;
}
/**
* #Entity
* #Table(name="Harbor")
*/
class Harbor {
/**
* #var object $startHarbors
* #OneToMany(targetEntity="LineList", mappedBy="startHarbor")
*/
protected $startHarbors;
}
That will let you join Harbors to LineLists via harborstart_id (you named the variable linelist, but I think now it's better to change the identifiers as there will be 2 referring to the same foreign table), then if you want to harborend_id
<?php
/**
* #Entity
* #Table(name="LineList")
*/
class LineList {
/**
* #var object $startHarbor
* #ManyToOne(targetEntity="Harbor", inversedBy="startHarbors")
* #JoinColumn(name="harborstart_id", referencedColumnName="id", nullable=FALSE)
*/
protected $startHarbor;
/**
* #var object $endHarbor
* #ManyToOne(targetEntity="Harbor", inversedBy="endHarbors")
* #JoinColumn(name="harborend_id", referencedColumnName="id", nullable=FALSE)
*/
protected $endHarbor;
}
/**
* #Entity
* #Table(name="Harbor")
*/
class Harbor {
/**
* #var object $startHarbors
* #OneToMany(targetEntity="LineList", mappedBy="startHarbor")
*/
protected $startHarbors;
/**
* #var object $endHarbors
* #OneToMany(targetEntity="LineList", mappedBy="endHarbor")
*/
protected $endHarbors;
}
Now you can change the DQL to:
$query = $this->getEntityManager()
->createQuery('SELECT h, a, sh, eh, m
FROM AmGameBundle:Hub h
JOIN h.harbor a
JOIN a.startHarbors sh
JOIN a.endHarbors eh
JOIN l.line m
WHERE h.company = :company_id
AND m.company = :company_id')
->setParameter('company_id', $company_id);
That should get you in the right direction. If it becomes troublesome though, a many-to-many approach as you speculated should be a well documented solution.