Doctrine relations for parent and child classes - php

I'm trying to implement a doctrine relation for a symphony 3 app.
I have two different classes, one extending from the other, which are related to the same entity with a many to one relation.
Here are my classes.
Country.php
class Country
{
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
* #Groups({"exposed"})
*/
private $id;
/**
* #ORM\OneToMany(targetEntity="Link", mappedBy="country")
*/
private $link;
/**
* #ORM\OneToMany(targetEntity="LinkChild", mappedBy="country")
*/
private $linkChild;
public function __construct()
{
$this->link = new ArrayCollection();
$this->linkChild = new ArrayCollection();
}
}
Link.php
/**
* Link
*
* #ORM\Table(name="link")
* #ORM\Entity(repositoryClass="Decathlon\AppCollaboratorBundle\Reposito\LinkRepository")
* #Vich\Uploadable
* #ORM\HasLifecycleCallbacks()
*/
class Link
{
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
* #Serializer\Groups({"link_list", "link_info"})
* #Serializer\Expose()
*/
protected $id;
/**
* #var Country
*
* #ORM\ManyToOne(targetEntity="Country", inversedBy="link", cascade={"persist"})
* #JoinColumn(name="country_id", referencedColumnName="id")
*/
protected $country;
}
LinkChild.php
/**
* #ORM\Entity(repositoryClass="Decathlon\AppCollaboratorBundle\Repository\LinkChildRepository")
*/
class LinkChild extends Link
{
/**
* #var Country
*
* #ORM\ManyToOne(targetEntity="Country", inversedBy="linkChild", cascade={"persist"})
* #JoinColumn(name="country_id", referencedColumnName="id")
*/
protected $country;
}
I need to create a relation between both Link and LinkChild to Country but no country column is created in LinkChild table.
I've told not to use recursive classes so I must create Link and LinkChild.
Is there a way to acomplish what I'm tryng to do.
Thank you in advance.

I think what you are looking for is single table inheritance?
<?php
namespace MyProject\Model;
/**
* #Entity
* #InheritanceType("SINGLE_TABLE")
* #DiscriminatorColumn(name="discr", type="string")
* #DiscriminatorMap({"person" = "Person", "employee" = "Employee"})
*/
class Person
{
// ...
}
/**
* #Entity
*/
class Employee extends Person
{
// ...
}
Take a look here:
http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/inheritance-mapping.html#single-table-inheritance

Try renaming your protected $country; variable to something like private $childCountry; to make it a variable that belongs specifically to LinkChild.
Your protected $country; override in LinkChild is ignored because it is exactly the same as the one in Link.

Related

Doctrine - OneToOne relationship not working with extends MappedSuperclass

I try to use #MappedSuperclass. It works well for simple variable (int, string...) and OneToMany/ManyToOne relationship. But OneToOne relationship doesn't work.
I have two MappedSuperclass with OneToOne relationship :
_SiteUser
/**
* #MappedSuperclass _SiteUser
*
* #ORM\Entity(repositoryClass="_SiteModule\_Repository\_SiteUserRepository")
* #ORM\Table(name="site_users")
*/
class _SiteUser
{
/**
* #var int
* #Groups("id")
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #var string
* #Groups({"username"})
*
* #ORM\Column(name="username", type="string", length=255, unique=true)
*/
protected $username;
/**
* #var string
* #Groups({"password"})
*
* #ORM\Column(name="password", type="string", length=255)
*/
protected $password;
/**
* #var _SiteUserTo
*
* #ORM\OneToOne(targetEntity="_SiteModule\_Entity\_SiteUserTo", mappedBy="user")
* #Gedmo\Versioned()
*/
protected $user_to;
_SiteUserTo
/**
* #MappedSuperclass _SiteUserTo
*
* #ORM\Entity(repositoryClass="_SiteModule\_Repository\_SiteUserToRepository")
* #ORM\Table(name="users_to")
*/
class _SiteUserTo
{
/**
* #var int
* #Groups("id")
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #var _SiteUser
*
* #ORM\OneToOne(targetEntity="_SiteModule\_Entity\_SiteUser", inversedBy="user_to")
* #JoinColumn(name="user_id", referencedColumnName="id")
*/
protected $user;
And this is my 2 class who extends these MappedSuperclass :
SiteUser
/**
* SiteUser
*
* #ORM\Entity(repositoryClass="SiteModule\Repository\SiteUserRepository")
* #ORM\Table(name="site_users")
*/
class SiteUser extends _SiteUser
{
}
SiteUserTo
/**
* SiteUserTo
*
* #ORM\Entity(repositoryClass="SiteModule\Repository\SiteUserToRepository")
* #ORM\Table(name="users_to")
*/
class SiteUserTo extends _SiteUserTo
{
}
When I generate entities from my MappedSuperclass (_SiteUser and _SiteUserTo), I got well a table named "users_to" with id and user_id. But when I generate entities from my others classes (SiteUser and SiteUserTo), it creates the table "users_to" with only id field. I don't know why...
If I update my SiteUser Class like this :
/**
* Class SiteUser
*
* #ORM\Entity(repositoryClass="SiteModule\Repository\SiteUserRepository")
* #ORM\Table(name="site_users")
*/
class SiteUser extends _SiteUser
{
/**
* #var boolean
*
* #ORM\Column(name="test", type="boolean")
*/
protected $test;
/**
* #var SiteUserTo
*
* #ORM\OneToOne(targetEntity="SiteModule\Entity\SiteUserTo", mappedBy="user")
*/
protected $user_to;
}
And SiteUserTo like this :
/**
* Class SiteUserTo
*
* #ORM\Entity(repositoryClass="SiteModule\Repository\SiteUserToRepository")
* #ORM\Table(name="users_to")
*/
class SiteUserTo extends _SiteUserTo
{
/**
* #var boolean
*
* #ORM\Column(name="test", type="boolean")
*/
protected $test;
/**
* #var SiteUser
*
* #ORM\OneToOne(targetEntity="SiteModule\Entity\SiteUser", inversedBy="user_to")
* #JoinColumn(name="user_id", referencedColumnName="id")
*/
protected $user;
}
I got the same problem, no field user_id in table users_to. But the field "test" is well created in the table site_users and in the table users_to...
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.
Simply said you can not have #MappedSuperClass and #ORM\Entity annotations at the same time, hence the unexpected results
https://www.doctrine-project.org/projects/doctrine-orm/en/2.6/reference/inheritance-mapping.html

How can I reference an existing entry to a new entity in Doctrine 2?

I working on the following Models:
User
Category
One User can only get one Category. Categories are "standalone". So I can update, create and delete (okay, maybe with cascading) Categories whenever I want.
When I create a new User, I want do reference one Category to the User.
How can I do so? I want to avoid bad practices.
User entity:
/**
* User
*
* #ORM\Table(name="user")
* #ORM\Entity(repositoryClass="XXX\Repository\UserRepository")
*/
class User
{
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\ManyToOne(targetEntity="Category", inversedBy="user")
* #ORM\JoinColumn(name="category", referencedColumnName="id")
*/
private $category;
//...
}
Category entity:
/**
* Category
*
* #ORM\Table(name="category")
* #ORM\Entity(repositoryClass="XXX\Repository\CategoryRepository")
*/
class Category
{
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\OneToMany(targetEntity="User", mappedBy="category")
*/
private $user;
//...
}
You write a User can have one Category, but it seems from your description that a Category can be used by several users. If this is true then your category definition should be changed a bit:
/**
* Category
*
* #ORM\Table(name="category")
* #ORM\Entity(repositoryClass="XXX\Repository\CategoryRepository")
*/
class Category
{
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var Collection
* #ORM\OneToMany(targetEntity="User", mappedBy="category")
*/
private $users;
public function __construct()
{
// It is important to initialize your collection
$this->users = new ArrayCollection();
}
// add methods for adding/removing and getting users:
// addUser, removeUser, addUsers, removeUsers and getUsers method
}
Read more on initializing collections in the doctrine documentation chapter 27.7. Initialize collections in the constructor.
Then in your User class you will need methods for setting and getting category.
public function setCategory(Category $category)
{
$category->addUser($user);
$this->category = $category;
}
public function getCategory()
{
return $this->category;
}
Now you can do:
$category = // get the category you want to set from entity manager
$user = new User();
$user->setCategory($category);
$entityManager->persist($user);
$entityManager->flush();
Now both your user and category are correctly set.
This is just a basic example that will help you get on your way.

Symfony ManyToOne Form add, delete in DB

I have entity developer and comment and relationship Many comment to One developer. And I need form when I see all comment for developer and edit - add, delete in DB . What are the solutions to this problem
entity Comment:
class Comments
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\ManyToOne(targetEntity="Developer", inversedBy="comments")
* #ORM\JoinColumn(name="talent_id", nullable = true, referencedColumnName="id")
* */
protected $talent;
/**
* #var string
*
* #ORM\Column(name="added_by", type="string", length=10, nullable=true)
*/
private $added_by;
/**
* #var string
*
* #ORM\Column(name="comment", type="string", length=10, nullable=true)
*/
private $comment;
entity Developer:
class Developer extends CustomUser
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/////
/**
* #ORM\OneToMany(targetEntity="Comments", mappedBy="talent", cascade={"persist", "remove"})
*/
protected $comments;
Maybe need form in form but how to do this?
You are looking for field type collection.
Example usage of collection type
class Comments
{
....
/**
*
*#ORM\ManyToOne(targetEntity="Developer", inversedBy="developer_to_comments")
* #ORM\JoinColumn(name="developer_to_comments_id", referencedColumnName="id", nullable=false)
*
*/
private $comments_to_developer;
...
}
And class Developer
class Developer extends CustomUser
{
....
/**
*
* #var ArrayCollection
* #ORM\OneToMany(targetEntity="Comments", mappedBy="comments_to_developer", cascade={"remove"})
*/
private $developer_to_comments;
public function __construct()
{
$this->developer_to_comments = new ArrayCollection();
}
....
}
And don't forget use Doctrine\Common\Collections\ArrayCollection

Many-To-Many relationship Doctrine

I have had quite a few problems with my current entities mapping, so I wanted an opinion on what is the issue with it.
Let's say with have the following tables example with Doctrine, but also keep in mind that a Group can have many Users, and a User can be part of many Groups
User
--
id
name
Group
--
id
title
User Groups
--
id
user_id
group_id
And this is my current approach:
/**
* User
*
* #ORM\Table(name="user")
* #ORM\Entity(repositoryClass="SomeBundle\Entity\Repository\UserRepository")
*/
class User
{
/**
* #var integer
* #ORM\Id
* #ORM\Column(name="user_id", type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $user_id;
// some other fields and functions here
}
Group
/**
* Group
*
* #ORM\Table(name="group", indexes={#ORM\Index(name="group_parent", columns={"parent_id"})})
* #ORM\Entity(repositoryClass="SomeBundle\Entity\Repository\GroupRepository")
*/
class Group
{
/**
* #var integer
*
* #ORM\Column(name="category_id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $categoryId;
// some other functions and variables here
}
...and user_groups
/**
* User_Groups
*
* #ORM\Table(name="user_groups",
* indexes={
* #ORM\Index(name="user_group_user", columns={"user_id"}),
* #ORM\Index(name="user_group_group", columns={"group_id"})
* }
* )
* #ORM\Entity(repositoryClass="SomeBundle\Entity\Repository\UserGroupRepository")
*/
class UserGroup
{
/**
* #var integer
*
* #ORM\Id
* #ORM\Column(name="user_group_id", type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $userGroupId;
/**
* #var \SomeBundle\Entity\User
* #ORM\Column(name="user_id")
*
* #ORM\ManyToMany(targetEntity="SomeBundle\Entity\User", inversedBy="userGroups")
* #ORM\JoinTable(name="users",
* joinColumns={#ORM\JoinColumn(name="user_id", referencedColumnName="userId")})
*/
private $user;
/**
* #var SomeBundle\Entity\Category
*
* #ORM\ManyToOne(targetEntity="SomeBundle\Entity\Group", inversedBy="groups")
* #ORM\JoinColumn(name="group_id", referencedColumnName="group_id")
*
*/
private $group;
/// etc
}
Any help would be appreciated!
/** #Entity **/
class User
{
// ...
/**
* #ManyToMany(targetEntity="Group", inversedBy="users")
* #JoinTable(name="users_groups")
**/
private $groups;
public function __construct() {
$this->groups = new \Doctrine\Common\Collections\ArrayCollection();
}
// ...
}
/** #Entity **/
class Group
{
// ...
/**
* #ManyToMany(targetEntity="User", mappedBy="groups")
**/
private $users;
public function __construct() {
$this->users = new \Doctrine\Common\Collections\ArrayCollection();
}
// ...
}
Your case is Many-to-Many, Bidirectional Association
Doctrine association mapping
In this example, I have a product and a mesureUnit they relate many to many.
In your case you only need user and group. The UserGroups is generated automatically.
Many to many relationship are done as follows:
<?php
namespace TeamERP\StoresBundle\Entity;
use Doctrine\ORM\Mapping AS ORM;
/**
* #ORM\Entity
*/
class MesureUnit
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\Column(type="string", length=100, nullable=true)
*/
private $name;
/**
* #ORM\Column(type="string", length=10, nullable=true)
*/
private $abreviation;
/**
* #ORM\OneToMany(targetEntity="TeamERP\StoresBundle\Entity\ProductActivity", mappedBy="mesureUnit")
*/
private $pproductActivity;
/**
* #ORM\ManyToMany(targetEntity="TeamERP\StoresBundle\Entity\Product", mappedBy="mesureUnit")
*/
private $product;
/**
* Constructor
*/
public function __construct()
{
$this->pproductActivity = new \Doctrine\Common\Collections\ArrayCollection();
$this->product = new \Doctrine\Common\Collections\ArrayCollection();
}
Then the produc class could be like:
<?php
namespace TeamERP\StoresBundle\Entity;
use Doctrine\ORM\Mapping AS ORM;
/**
* #ORM\Entity
*/
class Product
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\Column(type="string", length=50, nullable=true)
*/
private $code;
/**
* #ORM\Column(type="string", length=250, nullable=true)
*/
private $description;
/**
* #ORM\Column(type="datetime", nullable=true)
*/
private $date_received;
/**
* #ORM\OneToMany(targetEntity="TeamERP\StoresBundle\Entity\ProductActivity", mappedBy="product")
*/
private $pproductActivity;
/**
* #ORM\ManyToOne(targetEntity="TeamERP\StoresBundle\Entity\Category", inversedBy="product")
* #ORM\JoinColumn(name="category_id", referencedColumnName="id")
*/
private $category;
/**
* #ORM\ManyToOne(targetEntity="TeamERP\StoresBundle\Entity\AJob", inversedBy="product")
* #ORM\JoinColumn(name="job_id", referencedColumnName="id")
*/
private $aJob;
/**
* #ORM\ManyToMany(targetEntity="TeamERP\StoresBundle\Entity\MesureUnit", inversedBy="product")
* #ORM\JoinTable(
* name="MesureUnit2Product",
* joinColumns={#ORM\JoinColumn(name="product_id", referencedColumnName="id", nullable=false)},
* inverseJoinColumns={#ORM\JoinColumn(name="mesure_unit_id", referencedColumnName="id", nullable=false)}
* )
*/
private $mesureUnit;
/**
* Constructor
*/
public function __construct()
{
$this->pproductActivity = new \Doctrine\Common\Collections\ArrayCollection();
$this->mesureUnit = new \Doctrine\Common\Collections\ArrayCollection();
}
Yo do not need to create the table manually. It is generated by Doctrine.
Hope it helps

Symfony 2 relations issue

I nead help.
I have 3 entities. Book, Category And BookCategory - book can have multiple categories so i used another table.
I can easily acces Book and Category useing BookCategory table but i dont know how to do this by Book->BookCategory->Category.
class Category
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="name", type="text")
*/
private $name;
/**
* #var Category
* #ORM\ManyToOne(targetEntity="Category", inversedBy="Category")
* #ORM\JoinColumn(name="parent", referencedColumnName="id")
*/
private $parent;
class BookCategory
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var Book
* #ORM\ManyToOne(targetEntity="Book", inversedBy="BookCategory")
* #ORM\JoinColumn(name="book_id", referencedColumnName="id")
*/
private $bookId;
/**
* #var Category
* #ORM\ManyToOne(targetEntity="Category", inversedBy="BookCategory")
* #ORM\JoinColumn(name="category_id", referencedColumnName="id")
*/
private $categoryId;
/**
* #var integer
*
* #ORM\Column(name="priority", type="integer")
*/
private $priority;
class Book
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="title", type="text")
*/
private $title;
/**
* #var string
*
* #ORM\Column(name="author", type="text")
*/
private $author;
/**
* #var float
*
* #ORM\Column(name="price", type="float")
*/
private $price;
How i need to config my entities or how to make my DQL to achive wanted results?
With your code, you only established the relationship from BookCategory to Book. As you said, that enables you to get the Book associated to one BookCategory.
To go the other way and get all BookCategory that belong to one book, you also need to specify this relationship. What you want is a OneToMany relationship from Book to BookCategory.
<?php
//...
use Doctrine\ORM\Mapping\OneToMany;
class Book
{
//...
/**
* #OneToMany(targetEntity="BookCategory", mappedBy="bookId")
*/
private $bookCategories;
//...
}
class BookCategory
{
//...
/**
* #var Book
* #ORM\ManyToOne(targetEntity="Book", inversedBy="bookCategories")
* #ORM\JoinColumn(name="book_id", referencedColumnName="id")
*/
private $bookId;
//...
}
After adding the necessary getters and setters, getBookCategories() will give you an Array with all BookCategory that belong to the Book.
For more details, have a look at the official Symfony2 documentation:
http://symfony.com/doc/current/book/doctrine.html#relationship-mapping-metadata
Edit:
Included use statement. Corrected inversedBy property for bookId.

Categories