web-dev-qa-db-ja.com

Doctrine 2関連付けを伴う継承マッピング

注:私が望むものが可能でない場合、「不可能」の回答が受け入れられます

継承マッピング に関するDoctrine 2のドキュメントでは、2つの方法があると述べています:

  • 単一テーブル継承(STI)
  • クラステーブル継承(CTI)

どちらにも、警告があります:

STI/CTIエンティティを多対1または1対1のエンティティとして使用する場合上位レベルのクラスの1つを使用しないでください継承階層の「targetEntity」として、サブクラスを持たないもののみ。それ以外の場合Doctrineこのエンティティのプロキシインスタンスを作成できず、常にエンティティを積極的にロードします。

それで、どうすれば続行できますか基本(抽象)クラスへの関連付けで継承を使用する? (もちろん、パフォーマンスを維持します)


ユーザーには多くのPetDogまたはCatによって拡張された抽象クラス)があります。

私がやりたいこと :

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

Doctrineドキュメンテーションの警告のため、私はそれを行う必要があります:

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

継承のメリットを失うため、これは迷惑です!

注:DBへのマッピングにDoctrineアノテーションを追加しませんでしたが、私の意味は理解できます

55
Matthieu Napoli

私は疲れていますが、これは何もないことについて大いに苦労しているようです。

あなたはその警告の重要な部分を見逃しました:

STI/CTIエンティティを使用する場合多対1または1対1のエンティティとして

あなたの例ではそうではありません! doctrineアノテーションを省略していない場合は、気づいたかもしれません。

関連付けUser :: petsはOneToManyであり、[One | Many] ToOneではありません。 1人のユーザーが多くのペットを飼っています。

逆関連付けis OneToOneですが、継承を持たないUserをターゲットにしています。

ロビンの答えは良いヒントだったはずです-SQLクエリをログに記録して、doctrineが実際にデータベースに何をしているかを確認できます!


パフォーマンスが悪いシナリオは次のようなものです。

abstract class Pet { ... }

class Cat extends Pet { ... } 

class Dog extends Pet { ... }

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

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

さて、すべてのブルーカラーを反復処理したい場合、Doctrineは問題が発生します。$ ownerがどのクラスになるかがわからないため、プロキシを使用できません。代わりに、それが猫であるか犬であるかを見つけるために$ ownerを熱心にロードすることが強制されます。

これはOneToManyまたはManyToManyの関係では問題になりません。その場合、遅延読み込みは正常に機能するためです。プロキシの代わりに、PersistentCollectionを取得します。また、PersistentCollectionは常に単なるPersistentCollectionです。あなたが実際にそれらを要求するまで、それはそれ自身の内容について気にしません。したがって、遅延読み込みは正常に機能します。

47
timdev

あなたが誤解していると思います、あなたが引用したマニュアルのセクションは「パフォーマンスへの影響」と題されています、彼らはあなたにあなたに告げていませんできませんこれをしてください、パフォーマンスの影響がある場合のみあなたがやる。これは遅延読み込みに意味があります。STIエンティティの異種コレクションの場合は、データベースに移動して、どのクラスになるかがわかる前にエンティティを読み込む必要があるため、遅延読み込みは不可能です/意味がありません。私は現在Doctrine 2を自分で学習しているので、あなたの例をモックアップしました。以下は問題なく動作します:

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;
}

...そしてテストスクリプト....

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());
}

私のすべての猫と犬は正しいタイプとしてロードされます:

生成されたSQLを見ると、OneToMany targetEntityが "pet"の場合、SQLは次のようになります。

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')

しかし、Catに設定すると、次のようになります。

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。

46
Robin