web-dev-qa-db-fra.com

Problème de Symfony2 Doctrine2 avec une relation un à un facultative

J'ai un problème avec Doctrine2 dans Symfony2 et deux entités liées.

Il existe une entité utilisateur qui peut (ne doit pas) avoir une entité utilisateur référencée qui contient des informations telles que la biographie, etc.

L'usermeta est facultatif car l'utilisateur est importé par un autre système, tandis que l'usermeta est géré dans mon application.

Bien sûr, je veux enregistrer les deux ensemble, de sorte que l'enregistrement d'un utilisateur doit créer ou mettre à jour une entité utilisateur.

Les deux sont joints par une colonne nommée aduserid (même nom dans les deux tableaux).

J'ai reconnu que si usermeta est une référence facultative, le côté propriétaire dans ce cas devrait être usermeta, sinon doctrine charge l'utilisateur et a besoin de l'entité usermeta - mais il n'est pas toujours là.

Veuillez noter les commentaires dans 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's 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.... 
}

Et la classe 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;
}
}

le code du contrôleur ressemble à ceci:

...

$userForm->bind($request);

    if($userForm->isValid()) {
        $em->persist($user);
        $em->flush();
    }
...
23
daluq

Le commentaire Zdenek Machek est presque correct. Comme vous pouvez le voir dans la documentation Doctrine2, l'option nullable doit être dans l'annotation de jointure ( @ JoinColumn), pas dans le mappage ( @ OneToOne).

@ Doc JoinColumn:

Cette annotation est utilisée dans le contexte des relations dans les champs @ManyToOne, @OneToOne et dans le contexte de @JoinTable imbriqué dans un @ManyToMany. Cette annotation n'est pas obligatoire. S'il n'est pas spécifié, le nom des attributs et referencedColumnName sont déduits des noms de table et de clé primaire.

Attributs requis:

nom : nom de colonne qui contient l'identifiant de clé étrangère pour cette relation. Dans le contexte de @JoinTable, il spécifie le nom de la colonne dans la table de jointure.

referencedColumnName : nom de l'identificateur de clé primaire utilisé pour joindre cette relation.

Attributs facultatifs:

unique : détermine si cette relation est exclusive entre les entités affectées et doit être appliquée ainsi au niveau de la contrainte de base de données. Par défaut, false.

nullable: détermine si l'entité associée est requise ou si null est un état autorisé pour la relation. La valeur par défaut est true.

onDelete : Action en cascade (au niveau de la base de données)

onUpdate : Action en cascade (au niveau de la base de données)

columnDefinition : extrait DDL SQL qui commence après le nom de la colonne et spécifie la définition de colonne complète (non portable!). Cet attribut permet d'utiliser des fonctionnalités RMDBS avancées. L'utilisation de cet attribut sur @JoinColumn est nécessaire si vous avez besoin de définitions de colonne légèrement différentes pour joindre des colonnes, par exemple en ce qui concerne les valeurs par défaut NULL/NOT NULL. Cependant, par défaut, un attribut "columnDefinition" sur @Column définit également la columnDefinition @ JoinColumn associée. Cela est nécessaire pour faire fonctionner les clés étrangères.

http://doctrine-orm.readthedocs.org/en/latest/reference/annotations-reference.html#annref-joincolumn

@ OneToOne doc:

L'annotation @OneToOne fonctionne presque exactement comme @ManyToOne avec une option supplémentaire qui peut être spécifiée. Les valeurs par défaut de configuration pour @JoinColumn utilisant la table d'entité cible et les noms de colonne de clé primaire s'appliquent également ici.

Attributs requis:

targetEntity : FQCN de l'entité cible référencée. Peut être le nom de classe non qualifié si les deux classes sont dans le même espace de noms. IMPORTANT: pas de barre oblique inversée avant!

Attributs facultatifs:

cascade : Option Cascade

fetch : Un parmi LAZY ou EAGER

orphanRemoval : booléen qui spécifie si les orphelins, les entités OneToOne inverses qui ne sont connectées à aucune instance propriétaire, doivent être supprimés par Doctrine. Par défaut, false.

inversedBy : l'attribut inversedBy désigne le champ de l'entité qui est le côté inverse de la relation.

http://doctrine-orm.readthedocs.org/en/latest/reference/annotations-reference.html#onetoone

17
Francesco Casula

Vous utilisez le mauvais type de relation pour votre problème.

Ce que vous voulez, c'est un nidirectionnel un à un de Usermeta à Utilisateur .

Une relation bidirectionnelle un à un signifierait ce qui suit:

  1. Un utilisateur DOIT avoir un objet Usermeta.
  2. Un objet Usermeta DOIT avoir un utilisateur.

Dans votre cas, vous essayez uniquement d'exiger la deuxième condition.

Cela signifie que vous pouvez uniquement hydrater l'utilisateur d'Usermeta et non l'inverse.

Malheureusement doctrine ne prend pas en charge les relations zéro ou un à plusieurs .

8
james_t

J'espère que je ne dérange personne en soumettant cette réponse très tardive, mais voici comment j'ai résolu ce problème:

/**
 * @var Takeabyte\GripBundle\Entity\PDF
 * @ORM\OneToOne(targetEntity="Takeabyte\GripBundle\Entity\PDF", inversedBy="element", fetch="EAGER", orphanRemoval=true)
 */
protected $pdf = null;

J'ai ajouté = null; à la déclaration d'attribut. J'espère que cela sera d'une quelconque utilité pour quiconque lira ceci.

0
Rick Slinkman

J'ai reçu le message d'erreur "" spl_object_hash () s'attend à ce que le paramètre 1 soit un objet, null donné dans ... " en essayant la même chose. J'ai essayé de définir une relation bidirectionnelle One to One Alors que la valeur inversée pouvait être null. Cela a donné le message d'erreur. Enlever le côté inversé de la relation a résolu le problème. Il est dommage que les relations Zero or One to One Ne soient pas prises en charge.

0
Wilt