55 votes

Cartographie de l'héritage Doctrine 2 avec association

REMARQUE : si ce que je veux n'est pas possible, un "pas possible" réponse sera acceptée

Dans la Doctrine 2 de la documentation à propos de l'héritage de la cartographie, il est dit il y a 2 façons :

  • Seul l'héritage de table (STI)
  • La classe de l'héritage de table (CTI)

Pour les deux, il y a l'avertissement :

Si vous utilisez un STI/CTI entité plusieurs-à-un ou un-à-un de l'entité , vous ne devez jamais utiliser l'une des classes dans les niveaux supérieurs de l'héritage de la hiérarchie "targetEntity", seuls ceux qui n'ont pas de sous-classes. Sinon Doctrine NE peut pas créer de proxy cas de cette entité et sera TOUJOURS à la charge de l'entité avec impatience.

Alors, comment puis-je procéder pour utiliser l'héritage, avec un lien vers la base (résumé) de la classe ? (et conserver les performances de cours)


Exemple

Un utilisateur a plusieurs Pet (classe abstraite prolongée par Dog ou Cat).

Ce que je veux faire :

class User {
    /**
     * @var array(Pet) (array of Dog or Cat)
     */
    private $pets;
}

En raison de l'avertissement dans la Doctrine de la documentation, je devrais le faire :

class User {
    /**
     * @var array(Dog)
     */
    private $dogs;
    /**
     * @var array(Cat)
     */
    private $cats;
}

C'est gênant, parce que je perds les avantages de l'héritage !

Note : je n'ai pas ajouter de la Doctrine des annotations pour la cartographie de base de données, mais vous pouvez comprendre ce que je veux dire

47voto

timdev Points 25910

Je suis fatigué, mais cela semble être beaucoup de bruit pour rien.

Vous avez manqué les bits importants de cet avertissement:

Si vous utilisez un STI/CTI entité plusieurs-à-un ou un-à-un entité

Ce n'est pas le cas dans ton exemple! Si vous n'avez pas omis la doctrine des annotations, vous pourriez avoir remarqué.

L'association de l'Utilisateur::animaux de compagnie est OneToMany, ce n'est pas [Un|Plusieurs]ToOne. Un utilisateur a plusieurs animaux de compagnie.

L'association inverse est OneToOne, mais c'est le ciblage de l'Utilisateur, qui n'a pas d'héritage.

Robin réponse aurait été une bonne idée -- vous pouvez vous connecter les requêtes sql et de voir ce que la doctrine ne fait à votre base de données!


La mauvaise performance scénario est quelque chose comme:

abstract class Pet { ... }

class Cat extends Pet { ... } 

class Dog extends Pet { ... }

class Collar {
   /**
    * @Column(length="16")
    */

   protected $color;
   /**
    * ManyToOne(targetEntity="Pet")
    */
   protected $owner;
}

Maintenant, si vous voulez parcourir toutes les cols bleus, la Doctrine heurte à quelques difficultés. Il ne sait pas quelle classe $propriétaire va être, donc il ne peut pas utiliser un Proxy. Au lieu de cela, il est forcé d'impatience charge $propriétaire pour savoir si c'est un Chat ou un Chien.

Ce n'est pas un problème pour OneToMany ou ManyToMany relations, parce que dans ce cas, le chargement paresseux fonctionne très bien. Au lieu d'une procuration, vous obtenez un PersistentCollection. Et un PersistentCollection est toujours un PersistentCollection. Il ne s'occupe pas de son propre contenu jusqu'à ce que vous demandez d'eux. Si paresseux chargement fonctionne très bien.

46voto

Robin Points 2615

Je pense que vous avez mal compris, la section de ce manuel, vous avez cité est intitulé "l'impact de la Performance", ils ne sont pas en vous disant que vous ne pouvez pas faire cela, seulement qu'il y a des implications sur les performances si vous le faites. Cela a un sens pour le chargement paresseux - pour des collections hétérogènes de la STI entités, vous devez aller à la base de données et la charge de l'entité avant que vous savez dans quelle classe il sera, donc, le chargement paresseux n'est pas possible / n'a pas de sens. Je suis en train d'apprendre la Doctrine de 2 moi en ce moment, donc j'ai fait la maquette de votre exemple, les travaux suivants OK pour plus d':

namespace Entities;

/**
 * @Entity
 * @Table(name="pets")
 * @InheritanceType("SINGLE_TABLE")
 * @DiscriminatorColumn(name="pet_type", type="string")
 * @DiscriminatorMap({"cat" = "Cat", "dog" = "Dog"})
 */
class Pet
{
    /** @Id @Column(type="integer") @generatedValue */
    private $id;

    /** @Column(type="string", length=300) */
    private $name;

    /** @ManyToOne(targetEntity="User", inversedBy="id") */
    private $owner;
}


/** @Entity */
class Dog extends Pet
{

    /** @Column(type="string", length=50) */
    private $kennels;
}

/** @Entity */
class Cat extends Pet
{
    /** @Column(type="string", length=50) */
    private $cattery;
}

/**
 * @Entity
 * @Table(name="users")
 */
class User
{

    /** @Id @Column(type="integer") @generatedValue */
    private $id;

    /** @Column(length=255, nullable=false) */
    private $name;


    /** @OneToMany(targetEntity="Pet", mappedBy="owner") */
    private $pets;
}

... et le script de test ....

if (false) {
    $u = new Entities\User;
    $u->setName("Robin");

    $p = new Entities\Cat($u, 'Socks');
    $p2 = new Entities\Dog($u, 'Rover');

    $em->persist($u);
    $em->persist($p);
    $em->persist($p2);
    $em->flush();
} else if (true) {
    $u = $em->find('Entities\User', 1);
    foreach ($u->getPets() as $p) {
        printf("User %s has a pet type %s called %s\n", $u->getName(), get_class($p), $p->getName());
    }
} else {
    echo "  [1]\n";
    $p = $em->find('Entities\Cat', 2);
    echo "  [2]\n";
    printf("Pet %s has an owner called %s\n", $p->getName(), $p->getOwner()->getName());
}

Tous mes chats et les chiens charge que le type correct:

Si vous regardez le code SQL généré, vous remarquerez que lorsque le OneToMany targetEntity est "animal de compagnie", vous obtenez SQL comme ceci:

SELECT t0.id AS id1, t0.name AS name2, t0.owner_id AS owner_id3, pet_type, 
t0.cattery AS cattery4, t0.kennels AS kennels5 FROM pets t0 
WHERE t0.owner_id = ? AND t0.pet_type IN ('cat', 'dog')

Mais quand il est réglé à Chat, vous obtenez ceci:

SELECT t0.id AS id1, t0.name AS name2, t0.cattery AS cattery3, t0.owner_id 
AS owner_id4, pet_type FROM pets t0 WHERE t0.owner_id = ? AND t0.pet_type IN ('cat')

HTH.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X