Automatically add a Join statement in findBy magic method - symfony

I'm using Symfony2 / Doctrine2.
I'm trying to override the BaseEntityRepository Class so the magic findBy method automatically adds a JOIN on a "translations" relation. It was easy to do in Symfony 1.4/Doctrine 1, because it was manipulating a Doctrine_Query object, so I simply had to $query->addJoin() and it did the trick.
Unfortunately, in Doctrine 2 you only receive an array of criteria as parameter, and that's where I'm stucked.
I got many entities which have a one-to-many relationship with a translation entity.
For exemple : Section as a one-to-many relationship with SectionTranslation
The goal is to retrieve only the "active" sections (active is in SectionTranslation) when using SectionRepository->findAll(); (or even findBy).
The wanted DQL result is : Select * from Section INNER JOIN SectionTranslation ON Section.id = SectionTranslation.translatable_id WHERE SectionTranslation.locale = $locale AND SectionTranslation.active = 1;
Any idea?

The solution could be adding event listener for particular entity and particular method.

Related

Symfony/Doctrine2 and Associative entity

Usually when querying in a custom repository class, I use something like this :
SELECT * FROM BundleName:Entity
But how do I do for associative entity ?
I have an entity "Ticket" and an entity "Tag".
It's a ManyToMany relation.
In phpMyAdmin, I've got a ticket_tag associative table but how do I get it with Doctrine ?
Thank you
You should use createQueryBuilder to handle your custom query requirement, in case if you are having a valid relationship over entities. for example:
Inside ticket repository you should handle like this, if you want to do more operations then you should learn more from here: https://symfony.com/doc/3.3/doctrine/repository.html
$query = $this->createQueryBuilder('t')
->select('count(t.id) as total_ticket, tag.id as tagId')
->leftJoin('t.tags', 'tag')
->groupBy('tag.id')
;
return $query->getQuery()->getResult();

Doctrine ManyToMany, find related and no related results

I have an entity called tournament to which users can register to participate in.
The relationship between the two entities is ManyToMany and need to create a view of Symfony2 in which list all tournaments, with or without registered users so that they can join.
This is my DoctrineQueryBuilder
$em->createQueryBuilder('d')
->select('d, i, u')
->leftJoin('d.item','i')
->leftJoin('d.users','u')
->where('d.active = 1')
->andWhere('d.state = 1')
->orderBy('d.dateStart', 'ASC');
I also need to get the number of users who have joined the tournament.
Preamble
There are various ways to achieve what you want. You can create a sub-query to do the count, however a simpler solution is to let doctrine handle this for you.
The solution described below is based on Doctrine lazy/eager loading capability. When doctrine loads an entity, it will also populate it's associations, either lazily or eagerly (default is lazy).
Solution
Assuming your Tournament entity maps the users association as a ManyToMany relation. You can create a new method which counts your Tournament->users.
ManyToMany associations would populate the entity property (in this cases $users) with an ArrayCollection.
A method called countUsers would do the trick, example implementation below:
...
class Tournament {
...
/**
* #ManyToMany(targetEntity="User")
* #JoinTable(name="tournament_users",
* joinColumns={#JoinColumn(name="user_id", referencedColumnName="id")},
* inverseJoinColumns={#JoinColumn(name="tournament_id", referencedColumnName="id"}
* )
**/
private $users;
...
public function countUsers(){
return $this->users->count();
}
In the view when iterating over the collection of tournaments, simply call the $tournament->countUsers() method to display the count.
References:
ArrayCollection: http://www.doctrine-project.org/api/common/2.1/class-Doctrine.Common.Collections.ArrayCollection.html

How do I setup up Doctrine's Class Inheritance in Symfony?

My issue is, I'm having trouble grasping DiscriminatorColumn and DiscriminatorMap in Doctrine's Class Inheritance.
I have a products entity that is considered the parent class / table.
There are several child entities that inherit the product entity. (models, parts, and options)
I feel like I should be able to use the primary key to link both tables... But how do I do that with DiscriminatorColumn?
Here is the general idea of what I want to happen...
Fetch all model objects from database while inheriting product parent entity
SELECT object
FROM parts_object parts
LEFT JOIN products_object po
ON parts.product_fk = po.product_id
Or... Fetch all part objects from database while inheriting product parent entity
SELECT object
FROM parts_object parts
LEFT JOIN products_object po
ON parts.product_fk = po.product_id
Ideally I want this done using Doctrine instead of some custom SQL.
Do I need to setup a "type" column for the parent table so each row defines whether it's a part, model, or option?
Doctrine inheritance docs
Okay, I'll try to explain this as simple as possible.
Let's start with DiscriminatorColumn
Discriminator column is basically, as it says, a column in your database. Its used to store, a key, if you like which helps to identify what kind of object you're currently querying, based on your DiscriminatorMap configuration.
DiscriminatorMap is the way you map each of those keys to an entity. You said you have the following
Product [parent]
Model [child of parent]
Part [child of parent]
Option [child of parent]
Then, your discriminator map should look something like this, for example:
#DiscriminatorMap({
"model" = "AppBundle\Entity\Model",
"Part" = "AppBundle\Entity\Part",
"Option" = "AppBundle\Entity\Option"
})
Always pay attention to your last definition in your mapping. The last line must end without a comma!
As of InheritanceType I would suggest you to use #InheritanceType("JOINED") because this will let you have single table for each of your child classes.
Every child class must extend your Product entity class, which is obviously the parent. Each child class must not define $id property, because of the inheritance mapping.
Then querying for records by specific type comes with the following query:
"SELECT product FROM AppBundle\Entity\Product product WHERE product INSTANCE OF AppBundle\Entity\Part"
The query will search only for records mapped to this entity only.
If you have any questions, don't hesitate to ask.
Edit as of new comment
-----------------------
A little bit more explanation. You do not need to create any extra property/column in your entity mappings. The moment you add this annotation #DiscriminatorColumn(name="discr", type="string") doctrine will create that column automatically for you. The column from this example would be named discr with type of VARCHAR.
I still don't understand what is used to join the tables. How does doctrine know to link the ids between the product and model
About this part. If you use #InheritanceType("JOINED") this would mean that your GeneratedValue ID would be set in your main entity - Product. Then each of the child entities that extend Product would automatically get the same ID, which is why you don't need to specify $id property in your child entities.
Lastly, how can you check which entity type you're currently viewing for example. Consider the following scenario, each of your child entities extends Product and we will perform a dummy search for a record:
$product = $entityManager->find('AppBundle:Product', 1); // example
Now, if you actually go and do a var_dump($product) you will notice something interesting. The object would be an instance of either Model,Part or Option because each of these entities are defined in your discriminator map and Doctrine automatically maps your records based on that.
Later, this can come handy in situations like this:
if( $product instanceof \AppBundle\Entity\Part ) {
// do something only if that record belongs to part.
}
If you want to use DiscriminatorMap for Doctrine, so you should use Doctrine, but not SQL.
Basic setup is:
/**
* #ORM\Table(name="product")
* #ORM\Entity(repositoryClass="MyApp\ProductBundle\Repository\ProductRepository")
* #ORM\InheritanceType("SINGLE_TABLE")
* #ORM\DiscriminatorColumn(name="productType", type="string")
* #ORM\DiscriminatorMap({
* "Product" = "Product",
* "Model" = "Model",
* "Part" = "Part",
* "Option" = "Option",
* })
*/
class Product
{
...
}
MyApp\ProductBundle\Entity\Model
/**
* #ORM\Entity(repositoryClass="MyApp\ProductBundle\Repository\ModelRepository")
*/
class Model extends Product
{
}
MyApp\ProductBundle\Entity\Part
/**
* #ORM\Entity(repositoryClass="MyApp\ProductBundle\Repository\PartRepository")
*/
class Part extends Product
{
}
MyApp\ProductBundle\Entity\Option
/**
* #ORM\Entity(repositoryClass="MyApp\ProductBundle\Repository\OptionRepository")
*/
class Option extends Product
{
}
Then if you need to get all products at controller
$em = $this->getDoctrine()->getManager();
$repo = $em->getRepository("MyAppProductBundle:Product");
$products = $repo->findAll();
Then if you need select all models, just setup proper repository
$repo = $em->getRepository("MyAppProductBundle:Model");
$models = $repo->findAll();

Doctrine DQL Delete from relation table

Using Doctrine 2 and Symfony 2.0.
I have two Doctrine entities (let's suppose EntityA and EntityB).
I have a ManyToMany relation between them. So a EntityA_EntityB table has been created in database.
Using DQL or QueryBuilder, how can I delete from that relation table EntityA_EntityB?
Docrtine offers something like this to perform something similar:
->delete()
->from('EntityA a')
->where('a.id', '?', $id);
But I don't really get how to perform the deletion of row from the relation table.
$em = ...; // instance of `EntityManager`
// fetch both objects if ID is known
$a = $em->getRepository("YourProjectNamespace:EntityA")->find($id_of_A);
$b = $em->getRepository("YourProjectNamespace:EntityB")->find($id_of_B);
// suppose you have `EntityA::getObjectsOfTypeB` which retrieves all of linked objects of type `EntityB`.
// This method return instacne of ArrayCollection
$a->getObjectsOfTypeB()->removeElement($b);
$em->flush();
Something like this?
Basically, you need to remove related object from collection rather than delete relation itself. you want to remove relation directly you can always use pure SQL, but in DQL that is not possible.
Raw DELETE SQL statement via DBAL Connection object
$conn = $this->getDoctrine()->getManager()->getConnection();
$stmt = $conn->prepare("DELETE FROM EntityAEntityB WHERE id_b IN (:ids_of_b)");
$stmt->bindParam('ids_of_b', $to_delete_ids); // BEWARE: this array has to have at least one element
$stmt->executeUpdate();

Doctrine many-to-many association is not resolved

When i am using a Many-To-Many relationship in Symfony2 using Doctrine ORM i get the problem that my many-to-many relationship is not resolved.
Example:
Class A:
/**
* #ORM\ManyToMany(targetEntity="StoreItem", mappedBy="itemOptions")
*/
protected $storeItems;
Class B:
/**
* #ORM\ManyToMany(targetEntity="StoreItemOption", inversedBy="storeItems")
* #ORM\JoinTable(name="store_item_itemoptions")
*/
protected $itemOptions;
now i store the object in a session, note that i did not called the many to many relationship yet by using
->getItemOptions()
When i get my session object now and do ->getItemOptions() then it is empty.
Anybody has an idea what is causing this?
(PS: I found a hacky solution by saying that when i add an item to my cart i do a empty foreach that calls the method ->getItemOptions())
This is called 'lazy loading', and is a doctrine feature designed to reduce memory overhead.
You can set loading to 'eager' or explicitly add a select for the other field in your DQL to avoid lazy loading: e.g.:
$objectsA=$em->createQueryBuilder('\Class\A', 'a')
->join('a.b', 'b')
->addSelect('b')
->getQuery()
->getResult();

Resources