KnpLabs DoctrineBehaviors translatable query count - doctrine-orm

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)

Related

How to set parent id on a child entity when submitting symfony3 form

How can I set the task_id on the TaskLine entity?
Similar Questions:
Doctrine: How to insert foreign key value
Best practice for inserting objects with foreign keys in symfony2
Doctrine 2 entity association does not set value for foreign key
I get this error:
Neither the property "task_id" nor one of the methods "getTaskId()", "taskId()", "isTaskId()", "hasTaskId()", "__get()" exist and have public access in class "AppBundle\Entity\TaskLine"
NOTE: Normally I work with PropelORM.
I'm trying to save a new TaskLine entity which is related to Task entity. I'm posting JSON payload which look something like this.
{
"id": null,
"task_id": 1,
"note" : "new note"
}
In the controller I json_decode the request payload and submit that to $form->submit($note_data), $form is an instance of:
class TaskNoteType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add( 'task_id', NumberType::class )
->add( 'note', TextType::class )
;
}
/**
* #param OptionsResolver $resolver
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'AppBundle\Entity\TaskLine'
));
}
}
Here is my Task entity
class Task
{
/**
* #var string
*
* #ORM\Column(name="description", type="string", length=150, nullable=true)
*/
private $description;
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id;
}
TaskLine entity
class TaskLine
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id;
/**
* #var \AppBundle\Entity\Task
*
* #ORM\ManyToOne(targetEntity="AppBundle\Entity\Task")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="task_id", referencedColumnName="id")
* })
*/
private $task;
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set task
*
* #param \AppBundle\Entity\Task $task
*
* #return TaskLine
*/
public function setTask(\AppBundle\Entity\Task $task = null)
{
$this->task = $task;
return $this;
}
/**
* Get task
*
* #return \AppBundle\Entity\Task
*/
public function getTask()
{
return $this->task;
}
}
I found my answer in here:
Best Practice for inserting objects with foreign keys in Symfony2
Answered by: Tuan nguyen
In ORM you have to set Foreign key by an object which your entity
associated with. You could use EntityManager#getReference to get a
reference to category entity without loading it from DB. Like this
$category = $entityManager->getReference('YourProject\Entity\Category', $categoryId);
$product = new Product();
$product->setCategory($category);
Similar questions:
Doctrine: How to insert foreign key value
Following function you should have already in your Slider entity (or
similar).
public function addImage(Image $image) {
$image->setSlider($this); // This is the line you're probably looking for
$this->images[] = $image;
return $this; }
What it does is if you persist the entity it writes the ID of the Slider (sid) into your Image.
Doctrine 2 entity
association does not set value for foreign key
I found something in the Doctrine 2 documentation:
Changes made only to the inverse side of an association are ignored.
Make sure to update both sides of a bidirectional association (or at
least the owning side, from Doctrine’s point of view) As in my case
the owning side is the User I must update it. Doctrine 1 was able to
manage it automatically... too bad.

How to write exists subquery DQL for many to many association

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

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

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.