Doctrine 2 OneToOne from PFK of another OneToOne association - php

I have one question. I'm using Doctrine 2.0 and i want to know if is possible to made OneToOne association by using PFK created by another OneToOne assotiation as you can see on the picture below.
My code looks like this:
User class:
/**
* #ORM\Entity()
*/
class User extends \Kdyby\Doctrine\Entities\IdentifiedEntity
{
/**
* #ORM\OneToOne(targetEntity="AllianceMember", mappedBy="user")
* var AllianceMember
*/
protected $allianceMembership;
}
AllianceMember class:
/**
* #ORM\Entity()
*/
class AllianceMember extends \Kdyby\Doctrine\Entities\BaseEntity
{
/**
* #ORM\Id
* #ORM\OneToOne(targetEntity="User", inversedBy="allianceMembership")
* #ORM\JoinColumn(name="user_id", referencedColumnName="id", nullable=false)
* #var User
*/
protected $user;
/**
* #ORM\OneToOne(targetEntity="AllianceRole", mappedBy="allianceMember")
* var AllianceRole
*/
protected $role;
AllianceRole class:
/**
*
* #ORM\Entity()
*/
class AllianceRole extends \Kdyby\Doctrine\Entities\BaseEntity
{
/**
* #ORM\Id
* #ORM\OneToOne(targetEntity="AllianceMember", inversedBy="role")
* #ORM\JoinColumn(name="role_id", referencedColumnName="user_id", nullable=false)
* #var AllianceMember
*/
protected $allianceMember;
}
I'm getting this error, when I'm trying to get instance of User entity:
The column user_id must be mapped to a field in class App\Entity\AllianceMember since it is referenced by a join column of another class.
Is it even possible?
Thanks.

Yes, using mapped entities for your #Id has been possible since doctrine 2.1 see Primary key and foreign key at the same time with doctrine 2
I think you simply need to remove some of your JoinColumn statements. Since #user is already designated as your #Id, it isn't necessary to specify user_id as the JoinColumn:
/**
* #ORM\Entity()
*/
class AllianceMember extends \Kdyby\Doctrine\Entities\BaseEntity
{
/**
* #ORM\Id
* #ORM\OneToOne(targetEntity="User", inversedBy="allianceMembership")
* #var User
*/
protected $user;
/**
* #ORM\OneToOne(targetEntity="AllianceRole", mappedBy="allianceMember")
* var AllianceRole
*/
protected $role;
and
/**
*
* #ORM\Entity()
*/
class AllianceRole extends \Kdyby\Doctrine\Entities\BaseEntity
{
/**
* #ORM\Id
* #ORM\OneToOne(targetEntity="AllianceMember", inversedBy="role")
* #var AllianceMember
*/
protected $allianceMember;
}
The id of the AllianceMember is the user_id, and the id of the AllianceRole is the id of the AllianceMember, which is user_id.

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

symfony 3 doctrine does not accept primary key

I am new to Symfony and trying to setup entities and relationships. Even though I seemed to have correctly annotated the primary keys.
When running
php bin/console doctrine:schema:validate
I got an error as follows:
The referenced column name 'brandId' has to be a primary key column on the target entity class 'AppBundle\Entity\Brands'.
The entities look like ( only relevant portions):
Thanks
Brands
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
/**
* #ORM\Entity
* #ORM\Table(name="brands")
*/
class Brands
{
/**
* #ORM\Id
* #ORM\Column(type="smallint",length=3,unique=true,options={"unsigned":true})
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $brandId;
/**
* one brands has many models
* #ORM\OneToMany(targetEntity="Models", mappedBy="brandId")
* #ORM\JoinColumn(name="brandId", referencedColumnName="brandId")
*/
private $models;
public function __construct()
{
$this->models = new ArrayCollection();
}
Models:
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity(repositoryClass="AppBundle\Repository\ModelsRepository")
* #ORM\Table(name="models")
*/
class Models
{
/**
* #ORM\Column(type="smallint",length=4,unique=true,options={"unsigned":true})
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $modelId;
/**
* #ORM\Column(type="string", length=25)
*/
private $model;
/**
* Many models for one brand
* #ORM\ManyToOne(targetEntity="Brands",inversedBy="models")
* #ORM\JoinColumn(name="brandId", referencedColumnName="brandId")
*/
private $brandId;
A valid and clean entity mapping should be something like:
Brand.php
/**
* #ORM\Entity
* #ORM\Table(name="brands")
*/
class Brand
{
/**
* #var integer
*
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
protected $id;
/**
* one brands has many models
*
* #ORM\OneToMany(targetEntity="AppBundle\Entity\Model", mappedBy="brand")
*/
private $models;
public function __construct()
{
$this->models = new ArrayCollection();
}
}
Model.php
class Model
{
/**
* #var integer
*
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
protected $id;
/**
* #ORM\Column(type="string", length=25)
*/
private $name;
/**
* Many models for one brand
* #ORM\ManyToOne(targetEntity="AppBundle\Entity\Brand",inversedBy="models")
*/
private $brand;
}
for clean and easy usage:
$brand->getId(); //get id of brand
$brand->getModels(); //get array of Model object, ArrayCollection
$model->getBrand()->getId(); // Get id of related brand of some model
$model->getBrand()->getName(); //get the name of other propery of related brand
In your Brands model, this annotation should not be present.
* #ORM\JoinColumn(name="brandId", referencedColumnName="brandId")
JoinColumn only applies to ManyToOne and OneToOne fields. This is because in a one to many relationship, there will not be a join column in the table that contains the data for the owning ("one") side of the relationship.
JoinColumn is for defining a column on the "many" side that identifies which record on the "one" side owns it, so including it in the Models model is okay.
Wrong answer removed...
Bonus tips: Entity classes or better their instances should represent single datasets of a table. So their names should be singular ;)
By chosing not prefixed names for your primary key columns, you can spare some code, because Doctrine can work its magic then.
Also you should not prefix one column with the table name, if you do not prefix all of them.
This all ended up being an issue with Doctrine's naming strategy, which I did not know about. See this article for details
That strategy was set as default, while I was trying to adapt mysql's underscore naming strategy to PHP's camel case naming strategy. This was causing Symfony not to find the primary and thus the error.
Once I learned about that and with the valuable input I've received with this post, I have refactored the entities as below:
Thank you all.
Brands
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
/**
* #ORM\Entity
* #ORM\Table(name="brands")
*/
class Brands
{
/**
* #var integer
*
* #ORM\Id
* #ORM\Column(type="smallint",length=3,unique=true,options={"unsigned":true})
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* one brands has many models
*
* #ORM\OneToMany(targetEntity="Models", mappedBy="brandId")
*/
private $models;
public function __construct()
{
$this->models = new ArrayCollection();
}
/**
* #ORM\Column(type="string", length=25)
*/
private $brand;
.......
}
Models
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity(repositoryClass="AppBundle\Repository\ModelsRepository")
* #ORM\Table(name="models")
*/
class Models
{
/**
* #var integer
*
* #ORM\Id
* #ORM\Column(type="smallint",length=4,unique=true,options={"unsigned":true})
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\Column(type="string", length=25)
*/
private $model;
/**
* Many models for one brand
* #ORM\ManyToOne(targetEntity="Brands",inversedBy="models")
* #ORM\JoinColumn(name="brand_id", referencedColumnName="id")
*/
private $brandId;
/**
* Many models for one segment
* #ORM\ManyToOne(targetEntity="Segments", inversedBy="models")
* #ORM\JoinColumn(name="segment_id", referencedColumnName="id")
*/
private $segmentId;
.....
}

OneToMany ManyToOne DoctrineORM Error

I'm newbie with PHP. I started work with symfony but i have this problem
/**
* #ORM\Entity
* #ORM\Table(name="fos_user")
*/
class User extends BaseUser
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #param \Doctrine\Common\Collections\Collection $carList
* #ORM\OneToMany(targetEntity="AppBundle\CarBundle\Entity\Car", mappedBy="name", cascade={"persist"})
*/
private $carList;
//getters and setters
}
*
* #ORM\Entity(repositoryClass="AppBundle\CarBundle\Repository\Entity\CarRepository")
* #ORM\Table(name="car")
*/
class Car
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*
*
*/
protected $id;
/**
* #ORM\Column(type="string", length=100)
* #ORM\ManyToOne(targetEntity="AppBundle\UserBundle\Entity\User" , inversedBy="carList")
* #ORM\JoinColumn(name="user_id", referencedColumnName="id")
*/
private $name;
//getters and setters
}
The stacktrace says:
Symfony\Component\Debug\Exception\ContextErrorException: Notice: Undefined index: name
at n/a
and when i run php bin/console doctrine:schema:validate
[Mapping] FAIL - The entity-class 'AppBundle\UserBundle\Entity\User'
mapping is invalid:
* The association AppBundle\UserBundle\Entity\User#carList refers to the owning side field AppBundle\CarBundle\Entity\Car#name which is not
defined as association, but as field.
*The association AppBundle\UserBundle\Entity\User#carList refers to the owning side field Appbundle\CarBundle\Entity\Car#name which does
not exist
I have no idea whats going on, can you help me?
You are mixing up association names with column names. When you create an association you don't need to manually add the columns for that association, doctrine will work that out for you.
This code (in the Car class) says that the $name field is a normal text column in the car table, which of course is wrong
* #ORM\Column(name="name",type="string", length=100)
What you're describing is that one user can own many cars, and many cars can belong to one user. I'd then call the associations owner and cars, but you are of course free to call them whatever you want. Note that you do not need to define the join columns.
/**
* #ORM\Entity
* #ORM\Table(name="fos_user")
*/
class User extends BaseUser
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #param \Doctrine\Common\Collections\Collection $cars
* #ORM\OneToMany(targetEntity="AppBundle\CarBundle\Entity\Car", mappedBy="owner", cascade={"persist"})
*/
private $cars;
public function __construct()
{
$this->cars = new \Doctrine\Common\Collections\ArrayCollection();
}
//getters and setters
}
 
/**
*
* #ORM\Entity(repositoryClass="AppBundle\CarBundle\Repository\Entity\CarRepository")
* #ORM\Table(name="car")
*/
class Car
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\ManyToOne(targetEntity="AppBundle\UserBundle\Entity\User" , inversedBy="cars")
*/
private $owner;
//getters and setters
}
Read more: Doctrine association mapping
Hope it makes sense :)

Doctrine one-to-many relation - "No identifier/primary key specified"

Doctrine fails with a simple bi-directional many-to-one relationship between FoodDes (many) and FoodGroup (one). Both entities are shown here:
/**
* #ORM\Entity
* #ORM\Table(name="FOOD_DES")
*/
class FoodDes
{
public function __construct()
{
$this->foodGroup = new ArrayCollection();
}
/**
* #ORM\Id
* #ORM\Column(name="NDB_No", type="string", length=10)
*/
protected $id;
/**
* #ORM\ManyToOne(targetEntity="FoodGroup", inversedBy="fdGroupCode")
* #ORM\JoinColumn(name="FdGrp_Cd", referencedColumnName="FdGrp_CD")
*/
protected $foodGroup;
}
>
/**
* #ORM\Entity
* #ORM\Table(name="FD_GROUP")
*/
class FoodGroup
{
/**
* #ORM\Id();
* #ORM\GeneratedValue(strategy="NONE");
* #ORM\OneToMany(targetEntity="FoodDes", mappedBy="foodGroup")
*/
protected $fdGroupCode;
When I run doctrine orm:schema-tool:create, it fails with error:
No identifier/primary key specified for Entity
'Acme\Entities\FoodGroup'. Every Entity must have an
identifier/primary key.
However, I labeled $fdGroupCode as my only identifier.
Next approach
I've also tried creating a new primary key $id on the FoodGroup entity and removing the primary key label from $fdGroupCode on FoodGroup. Below is the new FoodGroup entity.
/**
* #ORM\Entity
* #ORM\Table(name="FD_GROUP")
*/
class FoodGroup
{
/**
* #ORM\Id
* #ORM\Column(name="id", type="integer", nullable=false)
*/
protected $id;
/**
* #ORM\OneToMany(targetEntity="FoodDes", mappedBy="foodGroup")
*/
protected $fdGroupCode;
When I run doctrine orm:schema-tool:create again, it results with a new error:
[Doctrine\ORM\ORMException]
Column name FdGrp_CD referenced for relation from
Acme\Entities\FoodDes towards Acme\Entities\FoodGroup does not exist.
This error doesn't make any sense. Of course it wouldn't exist. I am running it against an empty database!
These error occur running from the command line, but they also occur when querying the entities against a database. Can somebody please help me?
I'd rather give you working example of OneToMany from one of my projects, so you can see the difference and format code in proper way. If it does not work, then try to get a new Symfony dist and start over.
<?php
// SomeBundle/Entity/Shop/Product.php
namespace SomeBundle\Entity\Shop;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity
* #ORM\Table(name="shop_products")
*/
class Product
{
/**
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="bigint")
*/
protected $id;
/**
* #ORM\OneToMany(targetEntity="ProductItem", mappedBy="product")
*/
protected $productItem;
}
Related entity:
<?php
// SomeBundle/Entity/Shop/ProductItem.php
namespace SomeBundle\Entity\Shop;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity
* #ORM\Table(name="shop_products_items")
*/
class ProductItem
{
/**
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="bigint")
*/
protected $id;
/**
* #ORM\ManyToOne(targetEntity="Product", inversedBy="productItem")
* #ORM\JoinColumn(name="product_id", referencedColumnName="id")
*/
protected $product;
}
For reasons why your code does not work could be many (namespaces, folder structure, column names, etc.). This example works and tested. Give it a try : )

Symfony2 and doctrine: multiple relationships between two classes

I am new to Symfony2 and Doctrine.
I have two classes: User and Book. Each user can like very much, quite, or dislike every book in the database.
In order to describe this kind of association, I defined three different ManyToMany relationships between the two classes.
Here is an extract of my code:
Here is class User
<?php
namespace MyBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
/**
* User
*
* #ORM\Table(name="user")
* #ORM\Entity(repositoryClass="MyBundle\Entity\UserRepository")
*/
class User
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="username", type="string", length=64, unique=true)
*/
private $username;
/**
* #ORM\ManyToMany(targetEntity="Book", inversedBy="user_likemuch")
* #ORM\JoinColumn(name="book_id", referencedColumnName="id")
*/
protected $books_likemuch;
/**
* #ORM\ManyToMany(targetEntity="Book", inversedBy="user_likequite")
* #ORM\JoinColumn(name="book_id", referencedColumnName="id")
*/
protected $books_likequite;
/**
* #ORM\ManyToMany(targetEntity="Book", inversedBy="user_dislike")
* #ORM\JoinColumn(name="book_id", referencedColumnName="id")
*/
protected $books_dislike;
public function __construct()
{
$this->books_likemuch = new ArrayCollection();
$this->books_likequite = new ArrayCollection();
$this->books_dislike = new ArrayCollection();
}
}
And here is class Book:
<?php
namespace MyBundle\Entity;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping as ORM;
/**
* Book
*
* #ORM\Table(name="book")
* #ORM\Entity(repositoryClass="MyBundle\Entity\BookRepository")
*/
class Book
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="name", type="string", length=64)
*/
private $name;
/**
* #ORM\ManyToMany(targetEntity="User", mappedBy="books_likemuch")
*/
protected $user_likemuch;
/**
* #ORM\ManyToMany(targetEntity="User", mappedBy="books_likequite")
*/
protected $user_likequite;
/**
* #ORM\ManyToMany(targetEntity="User", mappedBy="books_dislike")
*/
protected $user_dislike;
public function __construct() {
$this->user_likemuch = new ArrayCollection();
$this->user_likequite = new ArrayCollection();
$this->user_dislike = new ArrayCollection();
}
}
However, when I try to update the schema of the database through
php app/console doctrine:schema:update --force
I get the following error:
[Doctrine\DBAL\Schema\SchemaException]
The table with name 'XXX/myproject/app/data/users.user_book' already exists.
Actually, I do not even understand if I an describing the relationship in a wrong way, or if doctrine cannot manage multiple relationships between two classes.
Does anyone have any suggestion?
Thanks in advance!
Doctrine can indeed manage multiple relationships. The classic example is between Users and Roles. The problem here is that you're trying to create multiple ManyToMany relationships between the same two entities. What Doctrine will do to manage these relationships is create a "join" table using the convention table1_table2, or in your case "user_book". So the reason you are getting the Doctrine error is ostensibly because it's attempting to create the same table 3 times.
I think what you want to do is have 3 separate ManyToMany "join" tables, one for likemuch, likequite, and dislike. So try specifying the join table for each association and see if that helps. Ex:
/**
* #ORM\ManyToMany(targetEntity="Book", inversedBy="user_likemuch")
* #ORM\JoinColumn(name="book_id", referencedColumnName="id")
* #ORM\JoinTable(name="user_book_likemuch")
*/
protected $books_likemuch;
See the section on bi-directional association mapping for ManyToMany relationships: Doctrine Association Mapping

Categories