I've been trying to solved this issue about the Doctrine configuration I have set with three Entities in Symfony2 (v2.3.9)
Let's assume my application's goal is to manage the enrollment of certain people to different training courses. And I do want to split these courses in different groups: A, B and C.
So, my entities are:
Enrollment
Course
Group
The enrollment of a certain person can be to more than one course, but he/she cannot enroll to the same Course in different groups. That means, having some sort of table which looks like this one:
----------------------------------------
| enrollment_id | course_id | group_id |
----------------------------------------
| 1 | 1 | 1 |
----------------------------------------
| 1 | 2 | 3 |
----------------------------------------
So far, I've been able to create a ManyToMany relationship between the Enrollment entity and the Course entity.
Enrollment.php
...
/**
*
* #ORM\ManyToMany(targetEntity="Course", inversedBy="enrollments")
* #ORM\JoinTable(name="enrollment_course",
* joinColumns={#ORM\JoinColumn(name="enrollment_id", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="course_id", referencedColumnName="id")}
* )
*
*/
protected $courses;
.....
And the Course entity looks like this in terms of relationship:
Course.php
....
/**
*
* #ORM\ManyToMany(targetEntity="Enrollment", mappedBy="courses")
*/
protected $enrollments;
....
That makes the configuration incomplete.
-----------------------------
| enrollment_id | course_id |
-----------------------------
| 1 | 1 |
-----------------------------
| 1 | 2 |
-----------------------------
Considering the fact that, in order to complete the enrollment, I need both informations the Course and the Group. How can I translate this into Doctrine annotation?
Do you have any clue?
Thank you so much for your help.
Dani.
You need a make an entity for the sole purpose of joining the fields.
/**
* #ORM\Entity
* #ORM\Table(name="base",uniqueConstraints={#ORM\UniqueConstraint(name="enrollment_course_group_idx", columns={"enrollment_id", "course_id", "group_id"})})
*/
class Base
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\ManyToOne(targetEntity="Enrollment")
* #ORM\JoinColumn(name="enrollment_id", referencedColumnName="id")
**/
private $enrollment;
/**
* #ORM\ManyToOne(targetEntity="course")
* #ORM\JoinColumn(name="course_id", referencedColumnName="id")
**/
private $course;
/**
* #ORM\ManyToOne(targetEntity="Group")
* #ORM\JoinColumn(name="group_id", referencedColumnName="id")
**/
private $group;
Related
I have an entity called Item which has a ManyToMany connection with a JoinTable to an entity called Tags
This is working well. The problem is, that I need the tags in the exact order as they are chosen, not sorted by tag id, as I get them now when I call getTags() on the Item class. Is there a way to do that?
Use the join table to define that order.
Item_id | Tags_id | Order
--------+---------+------
1 | 1 | 2
1 | 2 | 1 <-- order for tags of item 1
1 | 3 | 3
-------+---------+------
2 | 42 | 1
2 | 1 | 2 <-- order for tags of item 2
-------+---------+------
3 | 3 | 1 <-- order for tags of item 3
... And the entities equivalent :
You are going from
//MToM : Many To Many
//OToM : One To Many
//MToO : Many To One
[Item] -MToM-> [Tags]
[Tags] -MToM-> [Item]
To
[Item] -OToM-> [ItemTags] -MToO-> [Tags]
[Tags] -OToM-> [ItemTags] -MToO-> [Item]
And, in your entities classes :
class Item
{
// some properties ...
/**
* #OneToMany(targetEntity="ItemTags", mappedBy="Item")
*/
private $ItemTags; //array
//more properties, and get/set
}
class Tags
{
// some properties ...
/**
* #OneToMany(targetEntity="ItemTags", mappedBy="Tags")
*/
private $ItemTags; //array
//more properties, and get/set
}
class ItemTags
{
/**
* #ManyToOne(targetEntity="Item", inversedBy="ItemTags")
* #JoinColumn(name="Item_id", referencedColumnName="id")
* ^-----^-------------------------^^---- Make sure to use the same than in your table !
*/
private $Item; // single Item
/**
* #ManyToOne(targetEntity="Tags", inversedBy="ItemTags")
* #JoinColumn(name="Tags_id", referencedColumnName="id")
* ^-----^-------------------------^^---- Make sure to use the same than in your table !
*/
private $Tags; // single Tags
private $Order;
// get/set and stuff
}
I am using Symfony3.4 and have the following relationship in the database between House and Type entities:
house
+----------------------------------------------+
| id | title | description |
+----------------------------------------------+
| 1 | some title 1 | some description 1 |
+----------------------------------------------+
| 2 | some title 2 | some description 2 |
+----------------------------------------------+
type
+----------------+
| id | name |
+----------------+
| 1 | shortstay |
+----------------+
| 2 | rent |
+----------------+
| 4 | sell |
+----------------+
house_types
+-----------------------------------+
| id | house_id | type_id | price |
+-----------------------------------+
| 1 | 1 | 2 | 1000 |
+-----------------------------------+
| 2 | 1 | 3 | 1000000 |
+-----------------------------------+
| 3 | 2 | 1 | 100 |
+-----------------------------------+
| 4 | 2 | 3 | 200000 |
+-----------------------------------+
Here are my entities:
House entity
class House extends EntityBase
{
/**
* #ORM\OneToMany(targetEntity="AppBundle\Entity\HouseHasTypes", mappedBy="houses", cascade={"persist","remove"})
*/
protected $hasTypes;
Type entity
class Type extends EntityBase
{
/**
* #ORM\OneToMany(targetEntity="AppBundle\Entity\HouseHasTypes", mappedBy="types", cascade={"persist","remove"})
*/
protected $hasHouses;
HouseHasTypes entity
class HouseHasTypes extends EntityBase
{
/**
* #ORM\ManyToOne(targetEntity="AppBundle\Entity\House", cascade={"persist"}, fetch="EAGER")
* #ORM\JoinColumn(name="house_id", referencedColumnName="id", nullable=true)
*/
protected $houses;
/**
* #ORM\ManyToOne(targetEntity="AppBundle\Entity\Type", cascade={"persist","remove"}, fetch="EAGER" )
* #ORM\JoinColumn(name="type_id", referencedColumnName="id",nullable=true)
*/
protected $types;
/**
* #var int
*
* #ORM\Column(name="price", type="integer")
*/
protected $price;
I did not quite get the difference between ManyToOne and OneToOne relationship. Which one should mine be?
Also, what would be the easiest way to handle inserting new data into the 2 entities (House and HouseHasTypes). Right now I can render the checkboxes for each row in type table as follows:
$builder
...
->add('hasTypes', EntityType::class, array(
'required' => true,
'class' => Type::class,
'expanded' => true,
'multiple' => true,
))
And the in the controller I am planning to make a foreach(){} for each of the checked Types and setHouse() and setPrice() there. Is there any other more efficient way to do this?
How can I render as many textboxes as there are the abovementioned EntityType checkboxes to define a price for each Type selected? I am currently hardcoding three html input text elements and hiding them with javascript based on checked checkboxes. If I make a custom form, how can I tell it to render 3 text inputs (since there're 3 rows in the entity table)
Sorry for asking so many questions. Any help would be much appreciated
Your database model seems correct.
To define you the OneToMany, ManyToOne and OneToOne relationship, I made you little examples:
Imagine we have two entities: USER and ORDER.
OneToMany:
An user can do 0, 1 or many orders right ? It means that One user can be linked To 0, 1 or Many orders.
USER(id:1) --> ORDER(id:1)
--> ORDER(id:3)
--> ORDER(id:8)
ManyToOne:
An order can be done by one and only one user right ? It means that Many orders can be linked To One user.
ORDER(id:1) --> USER(id:1)
ORDER(id:3) --> USER(id:1)
ORDER(id:8) --> USER(id:1)
OneToOne:
Now imagine we have two others entities: USER and EMAIL.
An user can have one and only one email address and an email address can have one an only one user right ? It means that One user can be linked To One email address and One email address can be linked To One user.
USER(id:1) --> EMAIL(id:6)
USER(id:3) --> EMAIL(id:7)
USER(id:8) --> EMAIL(id:1)
Hope I was clear.
For your others questions, Symfony will automatically insert data on form submit if you have correctly defined your getter and setter in entities no needs to do a foreach
Is there any way to use the Symfony2's:
EntityManager::getReference($entityName, $id)
With some other field than a Primary Key?
Some code:
I have an entity:
class Category
{
/**
* #ORM\Id()
* #ORM\GeneratedValue(strategy="AUTO")
* #ORM\Column(name="id", type="integer")
*/
protected $id;
/**
* #ORM\Id()
* #ORM\Column(name="my_id", type="integer", nullable=false)
*/
protected $myId;
/**
* #ORM\ManyToMany(targetEntity="Category", inversedBy="child")
* #ORM\JoinTable(
* name="tcb_category_relations",
* joinColumns={#ORM\JoinColumn(name="category_id", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="parent_category_id", referencedColumnName="id")}
* )
*/
protected $parent;
/**
* #ORM\OneToMany(targetEntity="Category", mappedBy="parent")
*/
protected $child;
Which have a many to many relation to itself. This way, I make tree structures.
The thing is, $myId is much more important to and static - this id is not auto generated. So in case of import data, whoose can looks like this csv:
| my_id | my_id_parent |
| 1 | 0 |
| 4 | 1 |
| 5 | 1 |
| 7 | 5 | \ my_id could be unique!
| 7 | 4 | /
| 8 | 7 |
MyId is not unique, because any category can have multiple parents. So I can't use it as my primary key. I need to find reference for Category using the myId because while data import I need to flush entity manager every time after single category insert and every time run $em->find().
Of course I could use $em->getReference() but it's based on primary key and I don't know pk in data import.
I have three tables:
game columns: id | name (eg. 1 | Halo)
own columns: id | type_id | type_name (eg. 1 | 1 | on wishlist)
user columns: id | name (eg. 1 | Tomek)
Now what I have is a ManyToMany connection between those tables:
User (table user_own)
/**
* #ORM\ManyToMany(targetEntity="Own")
* #ORM\JoinTable(
* joinColumns={#ORM\JoinColumn(name="user_id", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="type_id", referencedColumnName="id")}
* )
*/
Own (table own_game)
/**
* #ORM\ManyToMany(targetEntity="Game")
* #ORM\JoinTable(
* joinColumns={#ORM\JoinColumn(name="user_id", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="game_id", referencedColumnName="id")}
* )
*/
My query:
public function getOwned() {
return $this->getEntityManager()
->createQuery('
SELECT u, o, g FROM AcmeGoBundle:User u
JOIN u.owners o
JOIN u.games g
')
->getResult();
}
I can retrieve games owned by particular user, but I can't connect type of ownership. How can I do this?
To crate ManyToMany with additional column with some data you need to create this by hand.
Doctrine ManyToMany relation crates a join table with id's of joined entities. So you need to create by your own Entity named ex. UserOwns with OneToMany relation to User and another OneToMany relation Own. Then you can add additional field with your data inside.
Check that question: Doctrine2: Best way to handle many-to-many with extra columns in reference table
I have two tables in my database: user and media_contact. The media_contact table has a user_id, so, obviously, my intent is to be able to select media contacts based on a user.
The problem I'm having is that this fails:
// VNN/PressboxBundle/Entity/User.php
// $this in this case is a User object with id 26
$customContacts = $em->getRepository('VNNPressboxBundle:MediaContact')->findByUser($this);
This gives me nothing. $customContacts is empty. But:
mysql> select * from media_contact where user_id=26;
+-----+-------------+-----------------------+---------------+---------+
| id | name | email | media_area_id | user_id |
+-----+-------------+-----------------------+---------------+---------+
| 177 | Jason Swett | jason.swett#gmail.com | NULL | 26 |
| 183 | Sam | sam#sam.com | NULL | 26 |
+-----+-------------+-----------------------+---------------+---------+
2 rows in set (0.00 sec)
It should really be returning two objects. I really don't understand why this is happening.
Here are my annotations:
// User.php
/**
* #ORM\OneToMany(targetEntity="MediaContact", mappedBy="user")
*/
private $mediaContacts;
-
// MediaContact.php
/**
* #ORM\ManyToOne(targetEntity="User", inversedBy="mediaContacts")
* #ORM\JoinColumn(name="user_id", referencedColumnName="id")
*/
private $user;
What could be going wrong?
You have to use $this->getId() instead of $this in order for it to work:
$customContacts = $em->getRepository('VNNPressboxBundle:MediaContact')->findByUser($this->getId());