How to write exists subquery DQL for many to many association - doctrine-orm

Given the entities below, would someone please help me understand how to write the DQL equivalent of the following SQL? I can't seem to find a good example of a DQL subquery that translates to a select on a pivot table. Thank you!
select *
from event a
where exists (
select *
from event_category b
where b.event_id = a.id
and b.category_id = 1
)
Entities:
/**
* #Entity
* #Table(name="event")
*/
class Event
{
/**
* #Column(type="integer")
* #Id
*/
protected $id;
/**
* #JoinTable(
* inverseJoinColumns={
* #JoinColumn(name="category_id", referencedColumnName="id")
* },
* joinColumns={
* #JoinColumn(name="event_id", referencedColumnName="id")
* },
* name="event_category"
* )
* #ManyToMany(targetEntity="Category")
*/
protected $categories;
}
/**
* #Entity
* #Table(name="category")
*/
class Category
{
/**
* #Column(type="integer")
* #Id
*/
protected $id;
}

Please have a look at Doctrine Query Language
Your example could be written :
SELECT event FROM Event event
WHERE EXISTS (
SELECT cat FROM Category cat
WHERE IDENTITY(cat.event) = event.id
AND cat.id = 1
)
Now I might be wrong but I don't think you need a subquery here.
If you want events that have a given category :
SELECT event FROM Event event JOIN event.category WHERE category.id = 1

Related

KnpLabs DoctrineBehaviors translatable query count

I am using KnpLabs/DoctrineBehaviors translatable.
I have a Post entity and a BlogCategory entity.
Post.php
class Post
{
use Timestampable;
/**
* #ManyToMany(targetEntity="BlogCategory")
* #JoinTable(name="post_categories")
* #var ArrayCollection
*/
protected $categories;
...
}
class BlogCategory
{
use Translatable;
/**
* #Id()
* #GeneratedValue(strategy="AUTO")
* #Column(type="integer")
* #var int
*/
private $id;
...
}
class BlogCategoryTranslation
{
use Translation;
/**
* #Column()
* #NotBlank()
* #var string
*/
protected $name;
...
}
I want to show posts with related categories. But now I have a lot of queries.
How can I join translation in many-to-many association to optimize query count?
What you're looking for is a LEFT JOIN on the translations:
SELECT post, category, category_trans
FROM Post post
LEFT JOIN post.categories category
LEFT JOIN category.translations category_trans
If you want to only select the translation for the current language:
LEFT JOIN category.translations category_trans WITH category_trans.locale = :locale
(bind a locale parameter to the query).
If you're using query builder:
->leftJoin('category.translations', 'category_trans', 'WITH', category_trans.locale = :locale)

Doctrine Dql for One-To-Many, Unidirectional with Join Table

So I've read the following article.
I've done something similar with my own entities and am trying to create a DQL.
DQL:
$em = $this->getDoctrine()->getManager();
$query = $em->createQuery(
'SELECT s,st,tt
FROM CrmClientBundle:Session s
INNER JOIN s.session_treatments st
INNER JOIN st.treatment_type_id tt
WHERE s.client_id = ' . $id
);
$sessions = $query->getResult();
I receive the following error though:
[Semantical Error] line 0, col 152 near 'tt
': Error: Class Crm\ClientBundle\Entity\TreatmentType has no association named
treatment_type_id
However if I remove the second join and check the symfony profiler it creates the following query which seems to me that I have created my entities properly:
SELECT
s0_.id AS id0,
s0_.client_id AS client_id1,
s0_.date AS date2,
s0_.session_id AS session_id3,
s0_.session_type AS session_type4,
s0_.session_cost AS session_cost5,
s0_.products_bought AS products_bought6,
s0_.products_cost AS products_cost7,
s0_.total_cost AS total_cost8,
s0_.total_paid AS total_paid9,
s0_.notes AS notes10,
t1_.id AS id11,
t1_.type AS type12,
t1_.cost AS cost13,
t1_.category_id AS category_id14
FROM
sessions s0_
INNER JOIN session_treatments s2_ ON s0_.id = s2_.session_id
INNER JOIN treatment_types t1_ ON t1_.id = s2_.treatment_type_id
WHERE
s0_.client_id = 1
Crm\ClientBundle\Session.php:
/**
* #ORM\Entity
* #ORM\Table(name="sessions")
*/
class Session {
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/** #ORM\Column(type="integer") */
private $client_id;
/** #ORM\Column(type="date") */
private $date;
/**
* **
* #ORM\ManyToMany(targetEntity="TreatmentType")
* #ORM\JoinTable(name="session_treatments",
* joinColumns={#ORM\JoinColumn(name="session_id", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="treatment_type_id", referencedColumnName="id ", unique=true)}
* )
*/
private $session_treatments;
/**
* Constructor
*/
public function __construct()
{
$this->session_treatments = new ArrayCollection();
}
}
Crm\ClientBundle\TreatmentType.php:
/**
* #ORM\Entity
* #ORM\Table(name="treatment_types")
*/
class TreatmentType {
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/** #ORM\Column(type="string", length=255) */
private $type;
/** #ORM\Column(type="decimal") */
private $cost;
}
You have 2 entities and you are trying to retrieve 3 entities. The second join is unnecessary. It's only needed if TreatmentType has yet another relationship (other then the one with Session). For clarification:
$em = $this->getDoctrine()->getManager();
$query = $em->createQuery(
'SELECT s,st,tt
FROM CrmClientBundle:Session s // <-- s is your Session
INNER JOIN s.session_treatments st // <-- st is your TreatmentType
INNER JOIN st.treatment_type_id tt <-- TreatmentType does not have a property $treatement_type_id that points to non-defined relationship. tt would be your 3rd entity.
WHERE s.client_id = ?1'
);
$query->setParameter(1, $id);
$sessions = $query->getResult();
Bonus: used bound a parameter -- helps against SQL injection and speeding up your query in the future

DQL INNER JOIN with Zend Framework 2 / Doctrine 2

With this SQL-Statement, i can fetch the latest messages from all users:
SELECT m1.*
FROM message AS m1
INNER JOIN user
AS u1
ON m1.sender_id = u1.user_id
INNER JOIN (
SELECT sender_id,
MAX(dateSent) MaxDate
FROM message
WHERE receiver_id = 4
GROUP BY sender_id
) AS m2
ON m1.sender_id = m2.sender_id
AND m1.datesent = m2.MaxDate;
Those are my Entities in my Zend Framework 2 application:
First, i got the Message. A User can send a Message to another user.
/**
* #ORM\Entity
* #ORM\Table(name="message")
*/
class Message
{
/**
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\ManyToOne(targetEntity="Application\Entities\User", inversedBy="messagesSent")
* #ORM\JoinColumn(referencedColumnName="user_id")
*/
private $sender;
/**
* #ORM\ManyToOne(targetEntity="Application\Entities\User", inversedBy="messagesReceived")
* #ORM\JoinColumn(referencedColumnName="user_id")
*/
private $receiver;
/**
* #ORM\Column(type="string", length=1024)
*/
private $message;
And the User entity here:
/**
* #ORM\Entity
* #ORM\Table(name="user")
*/
class User
{
/**
* #var int
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $user_id;
I tried to translate that SQL into DQL and got this:
SELECT m1.*
FROM Application\Entities\Message AS m1
INNER JOIN Application\Entities\User AS u1
WITH m1.sender_id = u1.user_id
INNER JOIN (
SELECT sender_id,
MAX(dateSent) MaxDate
FROM Application\Entities\Message
WHERE receiver_id = 4
GROUP BY sender_id
) AS m2
WITH m1.sender_id = m2.sender_id
AND m1.datesent = m2.MaxDate;
If i execute it, i get an error from Doctrine:
[Semantical Error] line 0, col 206 near '(
': Error: Class '(' is not defined.
What have i done wrong?
Subqueries are unavailable in doctrine(as far as I know) in DQL(from this). Can you write it query builder?
public function innerJoin($join, $alias = null, $conditionType = null, $condition = null);
See Reference

Doctrine 2 ManyToOne with Join Table

I'm looking for a suggestion on how to map a OneToMany/ManyToOne relationship that uses a join table. The mapping I have is not taking, and I get an error that article_id is not set in the media table. 
class Media
{
// ...
/**
* #ManyToOne(targetEntity="Document", inversedBy="media")
* #JoinTable(name="articles_x_media", referencedColumnName="id")
* joinColumns={#JoinColumn(name="media_id", referencedColumnName="id")},
* inverseJoinColumns={#JoinColumn(name="bid_id", referencedColumnName="id")})
* )
*/
protected $document;
}
class Document
{
// ...
/**
* #OneToMany(targetEntity="Media", mappedBy="document"))
* #JoinTable(name="articles_x_media", referencedColumnName="id")
* joinColumns={#JoinColumn(name="article_id", referencedColumnName="id")},
* inverseJoinColumns={#JoinColumn(name="media_id", referencedColumnName="id")}
* )
*/
protected $media;
}
There's a specific paragraph in the documentation about OneToMany mapping with join table.
Anyway, what you probably want is an uni-directional ManyToMany association.
Also, #OneToMany does not come with a #JoinTable, and the same for #ManyToOne either.

Doctrine2: Why is Mapping not detecting my composite keys

I have two tables/entities client and site that have a many to many relationship combined by a join table client_site. Here are how my entities are setup.
Here is the client table entity
/**
* #Entity
* #Table(name="client")
*/
class Client
{
/**
* #Id #Column(type="bigint")
* #GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ManyToMany(targetEntity="Site", inversedBy="clients")
* #JoinTable(name="client_site",
* joinColumns={#JoinColumn(name="c_id", referencedColumnName="id")},
* inverseJoinColumns={#JoinColumn(name="s_id", referencedColumnName="id")}
* )
*/
private $sites;
And the site table entity
/**
* #Entity
* #Table(name="site")
*/
class Site
{
/**
* #Id #Column(type="bigint")
* #GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ManyToMany(targetEntity="Client", mappedBy="sites")
*/
private $clients;
This is the client_site table entity
/**
* #Entity
* #Table(name="client_site",indexes={#index(name="FK_client_site",columns={"c_id"}),#index(name="FK_client_site_2",columns={"s_id"})})
*/
class ClientSite
{
/**
* #Id
* #ManyToOne(targetEntity="Client", inversedBy="ClientSite")
*/
private $client;
/**
* #Id
* #ManyToOne(targetEntity="Site", inversedBy="ClientSite")
*/
private $site;
This is the query I am trying to run
$query = Zend_Registry::get('em')
->createQuery('SELECT c, s
FROM Application\Models\Client c
JOIN c.sites s
WHERE c.is_active = 1');
$clients = $query->getResult();
And this is my error
No identifier/primary key specified for Entity 'Application\Models\ClientSite'. Every Entity must have an identifier/primary key.
I put the #Id on both fields in the ClientSite entity, as they are the composite primary keys for my joiner table. Can this not be done in Doctrine2? If it can't, what are my alternative options.
If you CAN do this, what have I done incorrectly?
This isn't currently supported by Doctrine 2.
However, they are working on adding support for this in an experimental branch. Apparently this might be included in the 2.1 release if it works without issues.