Doctrine 2 ManyToOne with Join Table - doctrine-orm

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.

Related

Doctrine Many-To-Many delete target entity instead of only jointable record

I have Photo Entity which has Man-To-Many relation with Tag Entity.
/**
* #var ArrayCollection
*
* #ORM\ManyToMany(targetEntity="Tag", cascade={"persist", "remove"})
* #ORM\JoinTable(name="photos_tags",
* joinColumns={#ORM\JoinColumn(name="photo_id", referencedColumnName="id", onDelete="CASCADE")},
* inverseJoinColumns={#ORM\JoinColumn(name="tag_id", referencedColumnName="id", onDelete="CASCADE")}
* )
**/
protected $tags;
Let say that I have a PhotoA which has TagA and TagB. But also I have and PhotoB which also has TagA. When I delete PhotoB, the TagA is also deleted. This is what i DONT want. I define cascade operations as I thought that this should delete only JoinColumn values... but its appears that the TagEntity is also delete as cascade operation.
Where I am wrong?
/**
* #var ArrayCollection
*
* #ORM\ManyToMany(targetEntity="Tag")
* #ORM\JoinTable(name="photos_tags",
* joinColumns={#ORM\JoinColumn(name="photo_id", referencedColumnName="id", onDelete="CASCADE")},
* inverseJoinColumns={#ORM\JoinColumn(name="tag_id", referencedColumnName="id")}
* )
**/
protected $tags;
This annotation solves the problem. I didnt realize that doctrine manage by itself these realtions and remove records in joined table without any need of describe. Obviously if want to delete and Tag Entity, then should annotated cascade operations.

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 Single Table Inheritance and bidirectional OneToMany: columns not being generated

The data model:
The entities:
Pet:
/**
* #ORM\Entity
* #ORM\Table(name="pet")
* #ORM\InheritanceType("JOINED")
* #ORM\DiscriminatorColumn(name="pet_type", type="string")
*/
abstract class Pet
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
*/
protected $id;
/**
* #ORM\OneToMany(
* targetEntity="Collar",
* mappedBy="pet",
* cascade={"persist", "remove"},
* orphanRemoval=TRUE
* )
* #ORM\JoinColumn(name="collars", referencedColumnName="id")
*/
protected $collars;
/**
* #ORM\Column(type="integer")
*/
protected $age;
}
Cat:
/**
* #ORM\Entity
* #ORM\Table(name="cat")
*/
class Cat extends Pet
{
/**
* #ORM\Column(type="decimal")
*/
private $purringDecibels;
}
Collar:
/**
* #ORM\Entity
* #ORM\Table(name="collar")
*/
class Collar
{
/**
* #ORM\Id
* #ORM\Column(name="id", type="integer")
*/
private $id;
/**
* #ORM\ManyToOne(targetEntity="Pet", inversedBy="collars")
* #ORM\JoinColumn(name="pet", referencedColumnName="id", nullable=false)
*/
private $pet;
/**
* #ORM\Column(type="string", length="255")
*/
private $color;
}
The problem:
The generated tables are fine except pet: It lacks the collars column, therefore the bidirectionality is broken. While the pet ids do get stored on the collar table, I cannot fetch collars from a pet (i.e. $pet->getCollars()) as it always returns an empty collection.
What am I missing out here?
PS:
The validate console helper says all (mapping & db) is OK.
Yes, I have my getters and setters (i.e. adders and removers)
I know about the infamous performance impact of the combination between a CTI and this kind of relationship.
You do not need to have a "collars" column on the pet table. It is a one-to-many relationship, so the only table that need to "know" about pets are the collar table.
A bidirectional relationship does not mean you need two columns on two tables.
Your map seems correct.

Doctrine2: Many-to-Many from Object X to Object X

i try to set up a "following" feature for users, so users can follow users.
I tried doing this by adding a Many-to-many Relation:
#ORM\ManyToMany(targetEntity="User",inversedBy="User",fetch="LAZY",cascade={"persist"})
#ORM\JoinTable=(name="user_followers",
joinColumns={
#ORM\JoinColumn(name="user_id", referencedColumnName="id")
},
inverseJoinColumns={#JoinColumn(name="follower_id",referencedColumnName="id")}
)
Now doctrine creates a table user_user with just one field user_id.
I really have no idea how to declare this.
Any thoughts?
I found the Solution myself.
You can actually use a self refferencing Many-to-Many Solution. From the Doctrine Website:
<?php
/** #Entity */
class User
{
// ...
/**
* #ManyToMany(targetEntity="User", mappedBy="myFriends")
*/
private $friendsWithMe;
/**
* #ManyToMany(targetEntity="User", inversedBy="friendsWithMe")
* #JoinTable(name="friends",
* joinColumns={#JoinColumn(name="user_id", referencedColumnName="id")},
* inverseJoinColumns={#JoinColumn(name="friend_user_id", referencedColumnName="id")}
* )
*/
private $myFriends;
public function __construct() {
$this->friendsWithMe = new \Doctrine\Common\Collections\ArrayCollection();
$this->myFriends = new \Doctrine\Common\Collections\ArrayCollection();
}
// ...
}
http://docs.doctrine-project.org/projects/doctrine-orm/en/2.0.x/reference/association-mapping.html#many-to-many-self-referencing

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.