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

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

Related

Doctrine delete items from collection - different column name

I have problem with Doctrine collection. In PromoCodeTerm entity I have #OneToMany $days collection, but when doctrine delete items from this collection, generates SQL query with different column name, like a promoCodeTerm instead of promo_code_term_id.
class PromoCodeTerm {
/**
* #var string
* #ORM\Column(name="id", type="guid")
* #ORM\Id()
*/
private $id;
/**
* #var PromoCodeTermDay[]
* #ORM\OneToMany(targetEntity="AppBundle\Entity\PromoCode\PromoCodeTermDay", mappedBy="promoCodeTerm", cascade={"persist"}, orphanRemoval=true)
*/
private $days;
}
class PromoCodeTermDay {
/**
* #var integer
* #ORM\Column(name="day_id", type="integer")
* #ORM\Id()
*/
private $day;
/**
* #var PromoCodeTerm
* #ORM\ManyToOne(targetEntity="AppBundle\Entity\PromoCode\PromoCodeTerm", inversedBy="days")
* #ORM\JoinColumn(name="promo_code_term_id", referencedColumnName="id", onDelete="CASCADE")
* #ORM\Id()
*/
private $promoCodeTerm;
}
What am I doing wrong?
An exception occurred while executing 'DELETE FROM `promo_code__promo_code_term_day` WHERE `day` = ? AND `promoCodeTerm` = ?' with params [1, \"c2fce03a-0507-47ca-a060-362cce683b06\"]:\n\nSQLSTATE[42S22]: Column not found: 1054 Unknown column 'promoCodeTerm' in 'where clause'

Doctrine2, DQL, association not initialized

is here some doctrine expert, who can explain me, why these DQLs will not initialize tallyRevs field on Tally entity? I supposed, that when I fetch TallyRevs (owner side) and fetchJoin Tally entity to them, that field tallyRevs will be initialized. What am I doing wrong? I need to select TallyRev based on some criteria via DQL and since it is a bi-directional association, I would like it to be initialized from the other (Tally.tallyRevs) side also.
Screen of dump
<?php
/**
* #ORM\Entity
* #ORM\Table(name="v3_overview_calloff_tally")
*/
class Tally
{
/**
* #var int
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var TallyRev[]|Collection
* #ORM\OneToMany(targetEntity="STI\Model\Entity\V3\Overview\CallOff\TallyRev", mappedBy="tally")
*/
private $tallyRevs;
}
/**
* #ORM\Entity
* #ORM\Table(name="v3_overview_calloff_tallyrev")
*/
class TallyRev
{
/**
* #var int
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var int
* #ORM\Column(type="integer", nullable=false)
*/
private $revision;
/**
* #var Tally
* #ORM\ManyToOne(targetEntity="STI\Model\Entity\V3\Overview\CallOff\Tally", inversedBy="tallyRevs")
* #ORM\JoinColumn(name="tally_id", referencedColumnName="id", nullable=false)
*/
private $tally;
}
Here is some repository code:
$qb = $repository->createQueryBuilder();
$qb
->select('tallyRev')
->from(TallyRev::class, 'tallyRev', 'tallyRev.id')
// complicated filtering, this is just an example
->andWhere($qb->expr()->in('tallyRev.revision', ':rev'))
->setParameter('rev', $rev)
;
$tallyRevs = $qb->getQuery()->getResult();
$ids = array_keys($tallyRevs);
$qb2 = $repository->createQueryBuilder();
$qb2
->select('partial tallyRev.{id}')
->from(TallyRev::class, 'tallyRev', 'tallyRev.id')
->andWhere($qb2->expr()->in('tallyRev.id', ':ids'))
->setParameter('ids', $ids)
->leftJoin('tallyRev.tally', 'tally')
->addSelect('tally')
;
$qb2->getQuery()->getResult();
I know, that I can write DQL from the Tally side like this:
$qb
->select('tally')
->from(Tally::class, 'tally', 'tally.id')
->leftJoin('tally.tallyRev', 'tallyRev')
->addSelect('tallyRev')
->andWhere($qb->expr()->in('tallyRev.revision', ':rev'))
->setParameter('rev', $revs)
;
If you want to fetch join tally when you fetch tallyRev you should write something like this in the first qb, and delete qb2
->select(['tallyRev', 'tally'])
->from(TallyRev::class, 'tallyRev', 'tallyRev.id')
->join('tallyRev.tally', 'tally')
// complicated filtering, this is just an example
->andWhere($qb->expr()->in('tallyRev.revision', ':rev'))
->setParameter('rev', $rev)
;

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)

Symfony2 Form with Doctrine Relationship Persistence Issue

So I have 3 entities with their properties.
A USER which has MANY USERSKILLS with each ONE having a single corresponding SKILL
Here are the entities:
/**
* #ORM\Table(name="skills")
* #ORM\Entity()
*/
class Skill
{
/**
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\Column(name="name", type="string", length=30, unique=true)
*/
private $name;
/**
* #ORM\Column(name="active", type="boolean")
* #var bool
*/
private $active = false;
/**
* #ORM\OneToMany(targetEntity="UserSkill", mappedBy="skill")
*/
private $userSkills;
}
/**
* #ORM\Table(name="user_skill")
* #ORM\Entity()
*/
class UserSkill
{
/**
* #ORM\Id
* #ORM\ManyToOne(targetEntity="User", inversedBy="skills")
* #var User
*/
private $user;
/**
* #ORM\Id
* #ORM\ManyToOne(targetEntity="Skill", inversedBy="userSkills")
* #var Skill
*/
private $skill;
/**
* #ORM\Column(type="integer")
* #var int
*/
private $level = 0;
}
/**
* StartupDriven\UserBundle\Entity\User
*
* #ORM\Table(name="users")
* #ORM\Entity(repositoryClass="StartupDriven\UserBundle\Entity\UserRepository")
*/
class User implements AdvancedUserInterface, \Serializable
{
/**
* #ORM\OneToMany(targetEntity="UserSkill", mappedBy="user")
* #var ArrayCollection
*/
private $skills;
}
They are created using 2 symfony form objects which works fantastically.
The problem is when I go to persist these objects in the controller.... I get an error
$form->handleRequest($request);
if ($form->isValid()) {
/**
* #var User $user
*/
$user = $form->getData();
/**
* #var UserSkill $userSkill
*/
foreach ($user->getSkills() as $userSkill) {
// No updating skills... Only new ones.
if (!$userSkill->getId()) {
// Check skill name match.
if ($matched_skill = $em->getRepository('StartupDrivenUserBundle:Skill')->findOneBy(array('name' => $userSkill->getName()))) {
$userSkill->setSkill($matched_skill);
}
else {
// No match. Create new generic skill.
$em->persist($userSkill->getSkill());
// THE ERROR HAPPENS ON THE FOLLOWING LINE!
$em->flush();
}
// Set the User
$userSkill->setUser($user);
// Persist the user skill.
$em->persist($userSkill);
$em->flush();
}
}
$em->persist($user);
$em->flush();
Error Message:
A new entity was found through the relationship 'StartupDriven\UserBundle\Entity\User#skills' that was not configured to cascade persist operations for entity: StartupDriven\UserBundle\Entity\UserSkill#0000000053e8628e00007f7f2fd56e1f. To solve this issue: Either explicitly call EntityManager#persist() on this unknown entity or configure cascade persist this association in the mapping for example #ManyToOne(..,cascade={"persist"}). If you cannot find out which entity causes the problem implement 'StartupDriven\UserBundle\Entity\UserSkill#__toString()' to get a clue.
I have tried every combination of persist & flush that I can think of. I have tried the cascade option above (which gives a different error that the "user id which is required by the primary key rules is not set")... I am completely lost. It seems like such a simple relationship... Where am I going wrong?

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