Symfony2 - how to construct id = product mapping - symfony

I want to do mapping that does this:
User can own multiple Games. Games can have multiple owners.
I have id column in game table and game and user columns in ownership table. How I can connect these fields? I want to have game and user fields in ownership related to user and game tables.
I tried OneToMany and ManyToMany, but the first one results in generating additional columns. I don't want to insert anything in game table.
--edit--
My #ManyToMany code:
/**
* #ORM\ManyToMany(targetEntity="Ownership")
* #ORM\JoinTable(name="ownership",
* joinColumns={JoinColumn(name="user", referencedColumnName="id")},
* inverseJoinColumns={JoinColumn(name="game", referencedColumnName="id")}
* )
*/
It causes an error in Symfony's command line:
[Doctrine\Common\Annotations\AnnotationException]
[Semantical Error] Couldn't find constant JoinColumn, property GameShelf\Us
ersBundle\Entity\User::$ownership.

Of course you need many to many relations if, like you said:
User can own multiple Games. Games can have multiple owners.
The Doctrine should create third table (which could contains only two foreign keys: game_id and ownership_id).
The error is caused because Doctrine dosen't know what JoinColumn is. You just did wrong annotation because you forgot (again! ;)) precede JoinColumn with `#ORM. The right annotation should look like this:
/**
* #ORM\ManyToMany(targetEntity="Ownership")
* #ORM\JoinTable(name="ownership",
* joinColumns={#ORM\JoinColumn(name="user", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="game", referencedColumnName="id")}
* )
*/

Sounds like you need to introduce a new entity (something like OwnedGame) that holds the connection between a Game and an Owner (and possibly its own properties like when it was bought and such).
Then OwnedGame would be ManyToOne with Game and with Owner, and neither Game nor Owner would need to include a new column.

Your description of what you want doesn't fit at all.
On the one hand you want to have a relation between only Users and Games, and only between Games and Owners.
But on the other hand you say that Users and Owners are related in some way, but Games and Users aren't.
You should read through
http://docs.doctrine-project.org/en/2.0.x/reference/association-mapping.html#many-to-many-unidirectional
and try to find what you really need.
I would recommend a ManyToMany relation with a join table. Thus you don't have to deal with additional fields in your tables

Related

Doctrine2 OneToMany with multiple Entities

I have three kinds of Users, every user has some FiscalData and is linked to a UserCredential entry.
Which translates in:
UserX (X=1,2,3) has two FKs referring to FiscalData and
UserCredential tables.
Using Doctrine2, reading the docs http://doctrine-orm.readthedocs.io/projects/doctrine-orm/en/latest/reference/association-mapping.html, I believe to need the MappedSuperClass pattern.
I have also read the following questions:
Doctrine 2 - One-To-Many with multiple Entities
Many-To-One with multiple target entities
Doctrine2, Symfony2 - oneToOne with multiple entities?
But in the docs is clearly stated that
A mapped superclass cannot be an entity, it is not query-able and persistent relationships defined by a mapped superclass must be unidirectional (with an owning side only). This means that One-To-Many associations are not possible on a mapped superclass at all. Furthermore Many-To-Many associations are only possible if the mapped superclass is only used in exactly one entity at the moment. For further support of inheritance, the single or joined table inheritance features have to be used.
So, how to achieve what I'm trying to achieve, which is a bidirectional relationship between UserX and FiscalData/UserCredential? (so that f.e. via Doctrine I can get a UserCredential and check what kind of profiles it has associated)
Any complete minimal code example showing how to enforce the relationship I'm looking for (and not just the MappedSuperClass inheritance shown in the docs will be highly appreciated.
Use an abstract entity instead of MappedSuperClass. Single-table is usually the way to go unless you're sure you want class/table.
<?php
/**
* #ORM\Entity
* #ORM\InheritanceType("SINGLE_TABLE")
* #ORM\DiscriminatorColumn(name="discr", type="string")
* #ORM\DiscriminatorMap({
* "mentor" = "Mentor",
* "protege" = "Protege"
* })
*/
abstract class User { ... }

Many-to-many relations to itself with Symfony/Doctrine

I have a data model that contains persons and companies (amongst other things). I have created a many-to-many relation between them by creating an intermediate object PersonCompanyRelation that contains many-to-one relations to person and companies. The reason for this is that I want to store information about the relation. This works fine
Now I want to do the same but for linking persons to persons. I have again created a relationship object called PersonPersonRelation. The problem I run into is that in the Person object I have to specify what property the object is mapped to in the relationship object, however this is problematic since it will differ from relationship to relationship. Sometimes this will be A, sometimes it will be B. It doesn't matter which one it is but without a logical system I cannot logically retrieve the relations. How do I do this?
the code in PersonPersonRelation
/**
* #ORM\ManyToOne(targetEntity="Person", inversedBy="personPersonRelations")
*/
protected $personA;
/**
* #ORM\ManyToOne(targetEntity="Person", inversedBy="personPersonRelations")
*/
protected $personB;
the code in Person. Note how I need to specify the property in the relation it is mapped to, but from the perspective of Person I do not know whether this will be PersonA or PersonB
/**
* #ORM\OneToMany(targetEntity="PersonPersonRelation", mappedBy="personA")
*/
protected $personPersonRelations;
To clarify as asked below; The case is that I want to store professional relations between people, including details about that relation like how they met (colleagues, friends, family etc) as well as dates etc.
I'm sure that if you specify less generic names, all should be more clear.
Just to make and example, let's say that Person-Person relationship is a relation between Boss and Employee.
So, basically, every person will have two attributes
class Person
{
//other properties
/**
* #ORM\OneToMany(targetEntity="person", mappedBy="employees")
*/
$boss;
/**
* #ORM\ManyToOne(targetEntity="person", inversedBy="boss")
*/
$employees;
}
If I understood correctly the question, in every moment, you can alternatively try to retrieve:
from a boss-person point of view, all employees under him
from a employee-person point of view, its boss
Of course mine is only an example, don't know if you could fit your mind in it to adapt to your specific case. Hope so.

Symfony2 and Doctrine - ManyToOne

I am trying to understand Symfony2, but there is something that is not making sense to me. I reversed engineered an existing database to produce my entities, so maybe thats the problem.
I have a table called availability_alert, nothing special, a few fields including an id (which is the primary key). This table has no link to anything else.
I then have a second table called booking_class, once again nothing special, but it does have the field $availabilityAlert which links to the availability_alerts tables id.
In essence, an Availability Alert can have one or many Booking Class.
Now in my booking class entity, I have the link
/**
* #var \AlertBundle\Entity\AvailabilityAlert
*
* #ORM\ManyToOne(targetEntity="AlertBundle\Entity\AvailabilityAlert")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="availability_alert_id", referencedColumnName="id")
* })
*/
private $availabilityAlert;
So this all looks ok. The setter for it though came out like so
public function setAvailabilityAlert(\AlertBundle\Entity\AvailabilityAlert $availabilityAlert = null)
{
$this->availabilityAlert = $availabilityAlert;
return $this;
}
So that appears to take an AvailabilityAlert Object as a parameter, not the AvailabilityAlert id?
So with the above, I am presuming doing something like this in my controller will not work?
$alert = new AvailabilityAlert();
$bookingClass = new BookingClass();
$bookingClass->setAvailabilityAlert($alert->getId());
Could someone give me some advice on whether things are correct here, or if I should be doing something else? Essentially AvailabilityAlert should be a static table, other tables link to this.
Any advice appreciated.
Thanks
Yes this is correct.
In the Doctrine world, you are working with objects, not with integers or strings, when it comes to relationships between Entities.
You can read more about Doctrine 2 relationships here: http://symfony.com/doc/current/book/doctrine.html#entity-relationships-associations
That's correct. You don't use integers, strings. It's because the relationships are given in entity annotations and Doctrine uses them to figure out what is used exactly to reference the one object from the other. This even let you change how objects reference themselves - for example you change the id to a compound primary key in AvailabilityAlert class and your code wouldn't change much except for the annotations.

How would you set the Cascade on removal?

I'm having some issues while deleting some objects, because those objects have other objects related to them.
For example. I've an entity named Home. This entity has some fields like name and description, aswel as id.
And then, I've an entity named Citizen which contains name, surname, home. This last field is directly related to Home.
So if I try to remove a HOME object, it crashes because there are some foreign key relating this object. What I'm doing at the moment, is searching for Citizens, and removing them, but this is not the best way to achieve this.
I know there's an annotation like "CASCADE" but I don't know where to place this annotation.
Where do I've to set this annotation to achieve what I'm trying to do?
Thanks.
In your home entity, you should have field, which contains all citizens.
/**
*
* #OneToMany(targetEntity="Citizen", mappedBy="home", cascade={"remove"})
*/
protected $citizens;
There are actually two ways of achieving your goal. You can use ORM level and Database level cascades.
ORM Level
// Home Entity
/**
* #comment The owning side has to use the inversedBy attribute of ManyToOne
*
* #ManyToOne(targetEntity="Citizen", inversedBy="home", cascade={"remove"})
*/
protected $citizens;
// ...
Database Level
// Home Entity
/**
* onDelete="CASCADE" is used on the joinColumn
* It will add On Delete Cascade to the foreign key column in the database.
*
* #ManyToOne(targetEntity="Citizen", inversedBy="home", cascade={"remove"})
* #ORM\JoinColumn(name="citizen_id", referencedColumnName="id", onDelete="CASCADE")
*/
protected $citizens;
// ...
Attention
You have to be careful where you set your cascade option.
You most likely don't want to have doctrine remove your owning side of the relation ( in this case delete the Home if you remove a citizen - therefore only set it on your Home entity ).
Alesh's example is wrong in your use case as mappedBy is always the inverse side of a relationship!
In your case Home will be the owning side as one Home will own multiple Citizens.
Now if you delete Home you want your Citizens to be homeless ( aka removed ).
Doctrine also only tracks the owning side of the relation for changes, now if you add a citizen to the collection ( for example with a collection form type ) with ...
$home->addCitizen($citizen);
... you likely want your citizen to be automatically persisted ( with cascade persist set ) when calling
$em->flush();

Implementing Doctrine many-to-many -- sometimes

I have a site with many Users and many Designs, each of which was created by one User. Users can also select other Designs as a favorite, and can go to a page that shows their favorites. So, there's a many-to-many relationship between Users and Designs termed "Favorite".
I'm wondering what the best way is to implement this Favorite relationship. I'm only ever looking for Favorites for a particular user, and only on one page. It seems that Doctrine defaults to returning an object with all its related objects, so I'm concerned that by adding this relationship I'll suddenly get a ton of extra objects added to all my Design list API calls.
Advice on how to set this up? I have repositories in place for all entities.
I'll share an implementation I have between Tags and Posts (Posts can have multiple tags, and each tag can be associated with more than one post). I think it's similar enough to get you started. It's super simple to setup:
In the Post entity, I have this:
/**
* #ManyToMany(targetEntity="Tag", inversedBy="posts")
* #JoinTable(name="activity_relationship",
* joinColumns={#JoinColumn(name="object", referencedColumnName="id")},
* inverseJoinColumns={#JoinColumn(name="subject", referencedColumnName="id")}
* )
*/
protected $tags;
In the Tag entity, I have this:
/**
* #ManyToMany(targetEntity="Post", mappedBy="tags")
*/
protected $posts;
There no entity necessary for the joining table.
In the case of posts carrying tags, it's enough for me to just access the tags property since there will always be a small amount, but each tag is likely to have tons and tons of posts, so I want some LIMIT and OFFSET controls over this. For this reason, as Cerad mentioned above, you don't need to use the $posts variable directly, instead I query that separately using dql.
SELECT t
FROM Post p
JOIN p.tags t
<add whatever other stuff you want>
Whichever way you prefer to execute that query, you will probably have a query object from Doctrine, to apply the LIMIT and OFFSET, just do
$query->setMaxResults($limit);
$query->setFirstResult($offset);

Resources