Doctrine2: эффективное отображение унаследованного класса - PullRequest
1 голос
/ 08 ноября 2011

У меня проблема с выяснением того, как настроить сопоставление моих классов с 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 не является абстрактным)
  • У PersonalAddress и businessAddress могут бытьте же самые адресные данные
  • Если я удалю или отредактирую адрес, это повлияет на все служебные или личные адреса, которые унаследованы от него.
  • Я не хочу дублирования данных вбаза данных (это требование 2-й нормальной формы)
  • Прокси-методы означают дублирование кода, я предпочитаю не иметь.
  • Магические методы не годятся с точки зрения тестируемости, я предпочитаюне иметь их.

Чтобы лучше проиллюстрировать проблему, я ожидаю, что данные в базе данных будут выглядеть следующим образом:

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 ]

1 голос
/ 11 ноября 2011

Хорошо, это немного долго, но я думаю, что оно охватывает все ваши базы, если у вас есть какие-либо вопросы, не стесняйтесь спрашивать.

Это сопровождается предупреждением, которое я не знаю, можете ли вы сделать Many-To-One на MappedSuperclass. Если это невозможно, вы можете вместо этого использовать наследование таблиц классов. Попробуйте и скажите нам, работает ли он.

Имейте в виду, я довольно быстро выдвинул этот код, он не проверен, поэтому он может быть неверным, но, надеюсь, вы поймете, как он работает.

Вот и мы!

Интерфейс, позволяющий легче сказать "что такое адрес", не создавая абстрактные классы, не переопределяя методы и не вызывая чувства "плохого дизайна";)

interface Address {

    function getCivicNo();

    function getRoad();

    function getState();

    function getCountry();
}

Абстрактная сущность, которая вам на самом деле не нужна, но если вы ею не пользуетесь, вам необходимо продублировать идентификационный код.

/**
 * @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;
    }

}

BasicAddress, который вы можете хранить отдельно или связать с «ComplexAddress»

/**
 * @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 также удаляются другие адреса, которые его используют.

0 голосов
/ 09 ноября 2011

Вместо расширения класса Address вам нужно создать отношения OneToMany для отношений Address и ManyToOne на 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;

    // ...
}
...