Doctrine2:继承类的高效映射

时间:2011-11-08 18:50:47

标签: orm doctrine-orm symfony

我在弄清楚如何使用Doctrine2配置我的类的映射时遇到了问题。

我想说这些表:

Address table 
--------------------- 
- id 
- civic_no 
- road 
- state 
- country 

PersonnalAddress table 
--------------------- 
- id 
- base_address_id 
- type 
- is_primary 

BusinessAddress table 
--------------------- 
- id 
- base_address_id 
- business_name 
- shipping_phone 
- is_primary 

并且使用PHP对象:

class Address{}
class BusinessAddress extends Address{}
class PersonalAddress extends Address{}

考虑以下要求:

  • 地址可以单独存在(Address类不是抽象的)
  • 个人地址和businessAddress可以拥有完全相同的地址数据
  • 如果我删除或编辑地址,则会对从中继承的所有商家或个人地址产生影响。
  • 我不希望数据库中存在任何数据重复(这是第二范式的要求)
  • 代理方法意味着代码重复,我不想拥有任何代码。
  • 魔术方法在可测性方面不好,我不喜欢任何。

为了更好地说明问题,我希望数据库中的数据看起来像:

Address table:
id | civic_no | road | state | country
 1        123   test      qc        ca

PersonnalAddress table:
id | base_address_id | type | is_primary 
 1                 1      A            0
 2                 1      B            1

BusinessAddress table:
id | base_address_id | business_name | shipping_phone | is_primary 
 1                 1       chic choc          1231234            1

实施符合这些要求的解决方案的最佳策略是什么?

2 个答案:

答案 0 :(得分:1)

好的,这有点长,但我认为它涵盖了你的所有基础,如果你有任何问题,那么随时可以问。

这有一个警告,我不知道你是否可以在MappedSuperclass上做Many-To-One。如果这不可能,那么您可以使用类表继承。试试看它是否有效。

请记住,我很快推出了这个代码,它未经测试,因此它可能不正确,但希望您能够了解它是如何工作的。

我们走了!

接口使得更容易说“什么是地址”而不制作抽象类然后重写方法并导致“糟糕的设计”感觉;)

interface Address {

    function getCivicNo();

    function getRoad();

    function getState();

    function getCountry();
}

抽象您不需要的实体,但如果您不使用它,则需要复制ID代码。

/**
 * @ORM\MappedSuperclass
 */
abstract class AbstractEntity {

    /**
     * Entity ID column.
     * 
     * @var integer
     * 
     * @ORM\Id
     * @ORM\Column(type="integer")
     * @ORM\GeneratedValue
     */
    private $id;

    public function getId() {
        return $id;
    }

}

您可以单独存储或链接到“ComplexAddress”

的BasicAddress
/**
 * @ORM\Entity
 */
class BasicAddress extends AbstractEntity implements Address {

    /** @ORM\Column() */
    private $road;

    public function getRoad() {
        return $this->road;
    }

    // etc etc

}

“ComplexAddress”就是让您重复使用代码将呼叫委托给基本地址。

/**
 * @ORM\MappedSuperclass
 */
abstract class ComplexAddress extends AbstractEntity implements Address {

    /** @ORM\Many-To-One(targetEntity="BasicAddress")
    private $basicAddress;

    public function __construct(BasicAddress $basicAddress) {
        $this->basicAddress = $basicAddress;
    }

    public function getRoad() {
        return $this->basicAddress->getRoad();
    }

    // other methods for implementing "Address" just delegate to BasicAddress

}

PublicAddress

/**
 * @ORM\Entity()
 */
class PersonalAddress extends ComplexAddress {

    /** @ORM\Column(type="boolean") */
    private $isPrimary;

    public function isPrimary() {
        return $isPrimary;
    }

    // other personal address methods here

}

BusinessAddress

/**
 * @ORM\Entity()
 */
class BusinessAddress extends ComplexAddress {

    /** @ORM\Column() */
    private $businessName;

    public function getBusinessName() {
        return $this->businessName;
    }

    // other business address methods here

}

编辑:刚注意到我忘了将级联参数删除,你可能需要直接处理 - 当删除BasicAddress时也会删除使用它的其他地址。

答案 1 :(得分:0)

您必须在地址 ManyToOne <上创建 OneToMany 关系,而不是扩展地址类。 / strong> PersonalAddress BusinessAddress 之间的关系。像这样:

<?php

// ...

use Doctrine\Common\Collections\ArrayCollection;

// ...
class Address
{
    // ...

    /**
     * @ORM\OneToMany(targetEntity="PersonalAddress", mappedBy="address")
     */
    private $personalAddresses;

    /**
     * @ORM\OneToMany(targetEntity="BusinessAddress", mappedBy="address")
     */
    private $businessAddresses;

    public function __construct()
    {
        $this->personalAddresses = new ArrayCollection();
        $this->businessAddresses = new ArrayCollection();
    }

    // ...
}

而且,对于儿童班:

<?php

// ...

// ...
class PersonalAddress
{
    // ...

    /**
     * @ORM\ManyToOne(targetEntity="Address", inversedBy="personalAddresses")
     * @ORM\JoinColumn(name="base_address_id", referencedColumnName="id")
     */
    private $address;

    // ...
}