I have the two tables :
table A with id as primary key
table B with id as primary key and foreign key
Explanation on short:
I need to have in table B a primary key that also to be a foreign key that points to table A's primary key.
Can anybody explain me how to map this by annotations in Doctrine 2?
Note:
I tried it By this :
class A
{
/**
* @var bigint $id
*
* @Column(name="id", type="bigint", nullable=false)
* @Id
* @GeneratedValue(strategy="IDENTITY")
*/
private $a_id;
...
and B table:
class B
{
/**
* @var bigint $id
* @Id
* @OneToOne(targetEntity="A", fetch="LAZY")
* @JoinColumn(name="id", referencedColumnName="id")
*/
private $b_id;
...
But it gives me this error:
Uncaught exception
'Doctrine\ORM\Mapping\MappingException'
with message 'No identifier/primary
key specified for Entity 'B'. Every
Entity must have an identifier/primary
key.' in
/var/www/agr-reg-php/Doctrine/ORM/Mapping/MappingException.php:37
Stack trace:
N.B: I must not have composite primary key.
This is possible since Doctrine 2.1:
Identity through Foreign Entities or derived entities: You can now use a foreign key as identifier of an entity. This translates to using @Id on a @ManyToOne or @OneToOne association. You can read up on this feature in the tutorial.
I could solve the problem, creating a pk field with the same name to the foreign field
class B
{
/**
* @var bigint $a_id
* @Id @Column(name="a_id", type="bigint", nullable="false")
* @ORM\Id
* @ORM\GeneratedValue(strategy="IDENTITY")
*/
private $a_id;
/**
* @OneToOne(targetEntity="A", fetch="LAZY")
* @JoinColumn(name="id", referencedColumnName="id")
*/
private $a;
.
.
.
/**
* @var A
*
* @ORM\OneToOne(targetEntity="A")
* @ORM\JoinColumns({
* @ORM\JoinColumn(name="a_id", referencedColumnName="id", unique=true)
* })
*/
private $a;
//...
Finally I resolved my problem by specifying two fields in my entity class for the same column from real table. The changes are made only in class B (look at the question for class A):
class B
{
/**
* @var bigint $id
* @Id @Column(name="id", type="bigint", nullable="false")
*/
private $b_id;
/**
* @OneToOne(targetEntity="A", fetch="LAZY")
* @JoinColumn(name="id", referencedColumnName="id")
*/
private $a;
...
In fact all what I have done is write two fields in my entity for the same primary key and foreign key.
Well this is other temporal solution for insert:
/**
* @Entity
* @Table(name="types")
*/
class Type {
/**
* @Id
* @Column(type="integer")
* @GeneratedValue
*/
private $id;
/**
* @OneToMany(targetEntity="type\models\Language", mappedBy="type")
*/
private $languages;
/**
* @Column(type="string", length = 45,nullable = true)
*/
private $category;
/**
* @Column(type="string", length = 1)
*/
private $status;
/**
* @Column(type="string", length = 45)
*/
private $name;
/**
* @Column(type="datetime", nullable = true)
*/
private $datedeleted;
/**
* @Column(type="datetime", nullable = true)
*/
private $datemodificated;
/**
* @Column(type="datetime")
*/
private $dateregistered;
public function __construct() {
$this->languages = new \Doctrine\Common\Collections\ArrayCollection;
$this->status = 1;
$this->dateregistered = new \DateTime("now");
}
public function id() {
return $this->id;
}
public function category($value = NULL) {
if (is_null($value))
return $this->category;
else
$this->category = $value;
}
public function name($value = NULL) {
if (is_null($value))
return $this->name;
else
$this->name = $value;
}
public function status($value = NULL) {
if (is_null($value))
return $this->status;
else
$this->status = $value;
}
public function datedeleted($value = NULL) {
if (is_null($value))
return $this->datedeleted;
else
$this->datedeleted = $value;
}
public function datemodificated($value = NULL) {
if (is_null($value))
return $this->datemodificated;
else
$this->datemodificated = $value;
}
public function dateregistered($value = NULL) {
if (is_null($value))
return $this->dateregistered;
else
$this->dateregistered = $value;
}
}
/**
* @Entity
* @Table(name="languages")
*/
class Language {
/**
* @Id
* @Column(name="type_id", type="integer", nullable="false")
*/
private $language_id;
/**
* @Id
* @ManyToOne(targetEntity="type\models\Type",inversedBy="languages")
* @JoinColumn(name="type_id", referencedColumnName="id")
*/
private $type;
/**
* @Column(type="string", length = 100, nullable = true)
*/
private $description;
/**
* @Id
* @Column(type="string", length = 20)
*/
private $language;
public function language_id($value) {
$this->language_id = $value;
}
public function description($value = NULL) {
if (is_null($value))
return $this->description;
else
$this->description = $value;
}
public function language($value = NULL) {
if (is_null($value))
return $this->language;
else
$this->language = $value;
}
public function type($value = NULL) {
if (is_null($value))
return $this->type;
else
$this->type = $value;
}
}
$language = new Language;
$xtype_id = $this->em->find("Type",1);
$language->language_id($xtype_id->id());
$language->type($xtype_id);
$language->description($xdescription);
$language->language($xlanguage);
$this->em->persist($this);
$this->em->flush();
this insert in a Class With the foreign key and primary key in language
I had the same task and experimentaly found this solution:
class A {
/**
* @Id @Column(type="integer", nullable=false)
* @GeneratedValue
*/
protected $id;
/**
* @OneToOne(targetEntity="B", inversedBy="a", orphanRemoval=true, cascade={"persist", "remove"})
* @JoinColumn(name="id", referencedColumnName="id")
*/
protected $b;
}
class B {
/**
* @OneToOne(targetEntity="A", mappedBy="b" )
*/
protected $user;
/**
* @Id
* @Column(type="integer", nullable=false)
* @GeneratedValue
*/
protected $id;
}