Проблема Symfony2 Doctrine2 с опциональным отношением один к одному
У меня проблема с Doctrine2 в Symfony2 и двумя связанными объектами.
Существует пользовательский объект, который может (не должен) иметь ссылку на usermeta, которая содержит информацию, такую как биография и т.д.
Пользователь usermeta не является обязательным, поскольку пользователь импортируется другой системой, а usermeta - в моем приложении.
Конечно, я хочу сохранить оба вместе, так что сохранение пользователя должно создать или обновить объект usermeta.
Оба соединены столбцом с именем aduserid (одно имя в обеих таблицах).
Я признал, что если usermeta является необязательной ссылкой, то владельцем в этом случае должна быть usermeta, иначе доктрина загружает пользователя и нуждается в объекте usermeta - но это не всегда есть.
Обратите внимание на комментарии в User- > setMeta..
/**
* User
*
* @ORM\Table(name="user")
* @ORM\Entity
*/
class User
{
/**
* @var Usermeta
* @ORM\OneToOne(targetEntity="Usermeta", mappedBy="user", cascade={"persist"})
*/
protected $meta;
public function getMeta()
{
return $this->meta;
}
/**
*
* @param Usermeta $metaValue
*/
public function setMeta($metaValue)
{
// I've tried setting the join-column-value here
// - but it not getting persisted
// $metaValue->setAduserid($this->getAduserid());
// Then I've tried to set the user-object in Usermeta - but then
// it seems like Doctrine wants to update Usermeta and searches
// for ValId names aduserid (in BasicEntityPersister->_prepareUpdateData)
// but only id is given - so not luck here
// $metaValue->setUser($this);
$this->meta = $metaValue;
}
/**
* @var integer
*
* @ORM\Column(name="rowid", type="integer", nullable=false)
* @ORM\Id
* @ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id;
/**
* Get rowid
*
* @return integer
*/
public function getId()
{
return $this->id;
}
/**
* @var integer
*
* @ORM\Column(name="ADuserid", type="integer", nullable=false)
*/
private $aduserid;
/**
* Set aduserid
*
* @param integer $aduserid
* @return User
*/
public function setAduserid($aduserid)
{
$this->aduserid = $aduserid;
return $this;
}
/**
* Get aduserid
*
* @return integer
*/
public function getAduserid()
{
return $this->aduserid;
}
// some mor fields....
}
И класс Usermeta:
/**
* Usermeta
*
* @ORM\Table(name="userMeta")
* @ORM\Entity
*/
class Usermeta
{
/**
* @ORM\OneToOne(targetEntity="User", inversedBy="meta")
* @ORM\JoinColumn(name="ADuserid", referencedColumnName="ADuserid")
*/
protected $user;
public function getUser()
{
return $this->$user;
}
public function setUser($userObj)
{
$this->user = $userObj;
}
/**
* @var integer
*
* @ORM\Column(name="id", type="integer", nullable=false)
* @ORM\Id
* @ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id;
/**
* @var integer
*
* @ORM\Column(name="ADuserid", type="integer", nullable=false)
*/
private $aduserid;
/**
* Set aduserid
*
* @param integer $aduserid
* @return User
*/
public function setAduserid($aduserid)
{
$this->aduserid = $aduserid;
return $this;
}
/**
* Get aduserid
*
* @return integer
*/
public function getAduserid()
{
return $this->aduserid;
}
}
код контроллера выглядит следующим образом:
...
$userForm->bind($request);
if($userForm->isValid()) {
$em->persist($user);
$em->flush();
}
...
Ответы
Ответ 1
Чтение моего собственного старого вопроса довольно забавно, так как я вижу проблему на первый взгляд сейчас.
Когда дело дошло до решения, я подумал, что доктрина может обрабатывать только идентификаторы с именем "id", но... aduserid просто не помечен как идентификатор, он пропускает аннотацию и доктрину Id, которые не могут использовать поля для соединения столбец.
Во-вторых, Зденек Машек был прав: он должен быть отмечен как обнуляемый.
Ответ 2
Комментарий Zdenek Machek почти правильный. Как вы можете видеть из документации Doctrine2, параметр с нулевым должен быть в аннотации присоединения (@JoinColumn), а не в сопоставлении (@OneToOne).
@JoinColumn doc:
Эта аннотация используется в контексте отношений в поля @ManyToOne, @OneToOne и в контексте @JoinTable, вложенном внутри @ManyToMany. Эта аннотация не требуется. Если его не указано, имя атрибута и referColumnName выведены из имен таблиц и первичных ключей.
Обязательные атрибуты:
name: имя столбца, которое содержит идентификатор внешнего ключа для этого отношения. В контексте @JoinTable он указывает имя столбца в таблице соединений.
referatedColumnName: имя идентификатора первичного ключа, используемого для объединения этого отношения.
Дополнительные атрибуты:
уникальный. Определяет, является ли это отношение эксклюзивным между затронутыми объектами и должно быть принудительно соблюдено на уровне ограничения базы данных. По умолчанию false.
nullable: определить, требуется ли связанная сущность, или если null является допустимым состоянием для отношения. По умолчанию используется значение true.
onDelete: действие Cascade (уровень базы данных)
onUpdate: действие Cascade (уровень базы данных)
columnDefinition: фрагмент DDL SQL, который начинается после имени столбца и определяет полное (не переносное!) определение столбца. Этот атрибут позволяет использовать расширенные функции RMDBS. Использование этого атрибута в @JoinColumn необходимо, если вам нужны несколько разные определения столбцов для объединения столбцов, например, в отношении значений NULL/NOT NULL по умолчанию. Однако по умолчанию атрибут "columnDefinition" на @Column также устанавливает соответствующее значение столбца @JoinColumns columnDefinition. Это необходимо для работы внешних ключей.
http://doctrine-orm.readthedocs.org/en/latest/reference/annotations-reference.html#annref-joincolumn
@OneToOne doc:
Аннотации @OneToOne работают почти так же, как @ManyToOne, с одной дополнительной опцией, которая может быть указана. Конфигурация по умолчанию для @JoinColumn с использованием таблицы целевых сущностей и имен столбцов первичного ключа также применяется здесь.
Обязательные атрибуты:
targetEntity: FQCN ссылочного целевого объекта. Может быть неквалифицированным именем класса, если оба класса находятся в одном и том же пространстве имен. ВАЖНО: Никакой ведущей обратной косой черты!
Дополнительные атрибуты:
каскад: опция каскада
выборка: один из LAZY или EAGER
orphanRemoval: логическое значение, указывающее, должны ли быть удалены из Doctrine сироты, обратные объекты OneToOne, которые не связаны с каким-либо собственным экземпляром. По умолчанию false.
inversedBy: атрибут inverseedBy обозначает поле в сущности, которая является обратной стороной отношения.
http://doctrine-orm.readthedocs.org/en/latest/reference/annotations-reference.html#onetoone
Ответ 3
Вы используете неправильный тип отношений для своей проблемы.
Что вам нужно, это однонаправленный один к одному от Usermetastrong > до Пользователь.
Двунаправленное отношение "один к одному" означает следующее:
- Пользователь ДОЛЖЕН иметь объект Usermeta.
- Объект Usermeta ДОЛЖЕН иметь пользователя.
В вашем случае вы только пытаетесь потребовать второе условие.
Это означает, что вы можете только увлажнять пользователя из Usermeta, а не наоборот.
К сожалению доктрина не поддерживает отношения нуля или одного ко многим.
Ответ 4
Я получил сообщение об ошибке . Функция spl_object_hash() ожидает, что параметр 1 будет объектом, null указывается в... " при попытке выполнить одно и то же. Я попытался определить двунаправленную связь One to One
, в то время как обратное значение могло бы быть null
. Это дало сообщение об ошибке. Убрав обратную сторону отношений, проблема решена.
Жаль, что отношения Zero or One to One
не поддерживаются.
Ответ 5
Надеюсь, я никого не потревожаю, представив этот очень поздний ответ, но вот как я решил эту проблему:
/**
* @var Takeabyte\GripBundle\Entity\PDF
* @ORM\OneToOne(targetEntity="Takeabyte\GripBundle\Entity\PDF", inversedBy="element", fetch="EAGER", orphanRemoval=true)
*/
protected $pdf = null;
Я добавил = null;
в объявление атрибута. Я надеюсь, что это поможет любой, кто читает это.