Symfony2 - 实体继承 - Common BaseEntity?

时间:2014-07-29 16:18:52

标签: symfony inheritance doctrine-orm

存在许多实体:Sheep,Cow,SheepVistedFarm,CowVisitedFarm和Farm。

绵羊和奶牛可以随意访问任意数量的农场。

如果马匹,农夫和拖拉机等需要跟踪,这种设置不能很好地扩展。

您将如何为每个农场创建一个统一的访问者列表?

我一直在研究继承映射http://docs.doctrine-project.org/en/2.0.x/reference/inheritance-mapping.html,但无法确定最佳方法是什么。

拖拉机和农民与羊,牛和马的实体非常不同。

单表和类表继承被排除,因为我希望每个实体由一个表表示。

即使使用单表或类表继​​承,如何避免使用SheepVisited,CowVisited,TractorVisited等来支持单个访问实体?

还有哪些其他选择?

class Sheep {

    /**
     * @ORM\OneToMany(targetEntity="SheepVisitedFarm", mappedBy="sheep")
     */
    protected $sheepvisited_farms;    
}

class Cow {

    /**
     * @ORM\OneToMany(targetEntity="CowVisitedFarm", mappedBy="cow")
     */
    protected $cow_visited_farms;    
}

class SheepVisitedFarm {
   /**
     * @ORM\ManyToOne(targetEntity="Sheep", inversedBy="sheep_visited_farms", cascade={"persist"})
     */
    protected $sheep;       

    /**
     * @ORM\ManyToOne(targetEntity="Farm", inversedBy="sheep_visited_farms", cascade={"persist"})
     */
    protected $farm;    

}

class CowVisitedFarm {
   /**
     * @ORM\ManyToOne(targetEntity="Cow", inversedBy="cow_visited_farms", cascade={"persist"})
     */
    protected $cow;       

    /**
     * @ORM\ManyToOne(targetEntity="Farm", inversedBy="cow_visited_farms", cascade={"persist"})
     */
    protected $farm;    

}

class Farm {

    /**
     * @ORM\OneToMany(targetEntity="SheepVisitedFarm", mappedBy="farm")
     */
    protected $sheep_visited_farms;        

    /**
     * @ORM\OneToMany(targetEntity="CowVisitedFarm", mappedBy="farm")
     */
    protected $cow_visited_farms;        

}

更新于2013-07-30

此问题已根据为VisitedFarm使用单表继承的想法进行了更新。已经为SheepVisitedFarm等扩展的VisitedFarm设置了DiscriminatorMap。但是,如何设置VisitedFarm与Sheep,Cow等之间的关系?

我所得到的东西显然不会起作用,因为SheepVisitedFarm等都向VisitedFarm引入了一个新的列。如何更新关系以纠正这个问题?

使用这种新结构,农场实体也是正确的吗?

<?php
/**
 * @ORM\Entity
 * @ORM\Table(name="sheep") 
 */
class Sheep {

    /**
     * @ORM\OneToMany(targetEntity="SheepVisitedFarm", mappedBy="sheep")
     */
    protected $sheepvisited_farms;    
}

/**
 * @ORM\Entity
 * @ORM\Table(name="cow") 
 */
class Cow {

    /**
     * @ORM\OneToMany(targetEntity="CowVisitedFarm", mappedBy="cow")
     */
    protected $cow_visited_farms;    
}

/**
 * @Entity
 * @ORM\Table(name="visited_farm") 
 * @InheritanceType("SINGLE_TABLE")
 * @DiscriminatorColumn(name="type", type="string")
 * @DiscriminatorMap({"sheep" = "SheepVisited", "cow" = "CowVisited"})
 */
class VisitedFarm
{
    /**
     * @ORM\ManyToOne(targetEntity="Farm", inversedBy="sheep_visited_farms", cascade={"persist"})
     */
    protected $farm;    
}

class SheepVisitedFarm extends VisitedFarm{
   /**
     * @ORM\ManyToOne(targetEntity="Sheep", inversedBy="sheep_visited_farms", cascade={"persist"})
     */
    protected $sheep;       

}

class CowVisitedFarm extends VisitedFarm{
   /**
     * @ORM\ManyToOne(targetEntity="Cow", inversedBy="cow_visited_farms", cascade={"persist"})
     */
    protected $cow;       

}

class Farm {

    /**
     * @ORM\OneToMany(targetEntity="SheepVisitedFarm", mappedBy="farm")
     */
    protected $sheep_visited_farms;        

    /**
     * @ORM\OneToMany(targetEntity="CowVisitedFarm", mappedBy="farm")
     */
    protected $cow_visited_farms;        

}

2 个答案:

答案 0 :(得分:1)

我认为没有办法做到这一点,两者都很容易扩展,并允许您对每种类型的访问者都有一个实体的细粒度控制。我可以想出两种模拟方法,两种方式都是妥协:

  1. 为每种类型提供CowVisitedFarmSheepVisitedFarm等类。您可能必须添加许多类,但这些类都将非常小,继承自基类。所涉及的开销实际上只是创建类:您可以在父类中放置任何常用功能。然后,根据文档,您只需设置一个鉴别器地图。

  2. 对您的实体进行概括,以便您只有一个(或几个)具有属性的不同实体来区分它们。例如一个名为Visitor的类可以在其上有一个entityType字段,可以确定它是拖拉机,绵羊等。如果需要更精细的控制,您可以拥有Animal类型和Machinery类型,因此您可以更加区分不属于的东西。

  3. 这取决于每种类型的访问者作为一个独特类的实例对于您选择的选项有多重要:如果您对每种类型的访问者都绝对需要不同的功能(例如您&# 39;重新定义诸如public function makeNoise() { echo 'Moo'; }public function makeNoise() { echo 'Mechanical whirring'; }之类的内容,然后值得接受这样一个事实:您需要添加所有这些实体类,然后为每个类添加Visit类。如果您更关心单个访问并且只需要知道实体的基本属性(例如它是什么)而无需为每个实体定义特定行为,那么您可以考虑第二个选项。如果您不确定采用哪种方法,我会选择第一种方法,因为从长远来看它更灵活。

答案 1 :(得分:1)

怎么样:

class Farm {
    ...
}

//Single table inhertance
abstract class Visitor {
    //many-to-many with Farm
    protected $farms;

    abstract public function visitorInfo();
}

interface visitorInterface {
    /**
     * @return Visitor
     */
    public function getVisitor();
}

class Sheep {
    protected $number;

    //one-to-one with SheepVisitor
    protected $visitor;
}

class SheepVisitor extends Visitor {
    //one-to-one with Sheep
    protected $sheep;

    public function visitorInfo() {
       return printf("Sheep number %d was here!", $sheep->getNumber());
    } 
}

class Cow {
    protected $number;

    //one-to-one EAGER with CowVisitor
    protected $visitor;
}

class CowVisitor extends Visitor {
   //one-to-one EAGER with Cow
   protected $cow;

   public function visitorInfo() {
       return printf("Cow number %d was here!", $cow->getNumber());
   }
}

//whenever you want to add a new entity that can visit the farm as well just create it's visitor class

class Farmer {
    protected $name;

    //one-to-one EAGER with HorseVisitor
    protected $visitor;
}

class FarmerVisitor extends Visitor {
   //one-to-one EAGER with Farmer
   protected $farmer;

   public function visitorInfo() {
       return printf("Farmer %s was here!", $farmer->getName());
   }
}

<强>注意

请记住FarmVisitor之间的关系,因为Visitor将使用表格继承,您可能需要从教条文档中考虑这一点:

http://doctrine-orm.readthedocs.org/en/latest/reference/inheritance-mapping.html

  

此策略对于查询所有类型的查询非常有效   层次结构或特定类型。不需要表连接,只需要一个   WHERE子句列出了类型标识符。特别是,   涉及采用此映射策略的类型的关系是   非常高效。

     

单表有一般的性能考虑因素   继承:如果是多对一或一对一的目标实体   association是STI实体,出于性能原因,它更可取   它是继承层次结构中的叶子实体,(即没有   子类)。否则,Doctrine无法创建此代理实例   实体,并将始终热切地加载实体。

这意味着建议在FarmVisitor之间保持单向关系,否则加载任何服务器场将导致学说加载所有访问者 EAGERLY ,除非您的应用程序赢了“使用Farm而不调用它Visitors但我会保持单向并做这样的事情:

$farm       = findOneFarm();
$visitors   = findVisitorsForFarm($farm);
foreach ($visitors as $visitor) {
    echo $visitor->visitorInfo();
}

代码写在这里,所以期待一些语法问题