Reputation: 11295
I've a problem figuring out how to configure the mapping of my classes with Doctrine2.
Let say I've these tables:
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
And thoses PHP objects:
class Address{}
class BusinessAddress extends Address{}
class PersonalAddress extends Address{}
Considering the following requirements:
To better illustrate the problem, I expect the data in the data base to look like:
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
What would be the best strategy to implement a solution that match theses requirements ?
Upvotes: 1
Views: 593
Reputation: 5411
Ok this is a bit of a long one but I think it covers all your bases, if you have any questions then feel free to ask.
This comes with a caveat that I don't know if you can do Many-To-One
on a MappedSuperclass. If that isn't possible then you may be able to use Class Table Inheritance instead. Give it a try and tell us if it works.
Keep in mind I pushed this code out pretty quickly, it is untested so it may not be correct but hopefully you'll get the idea of how it works.
Here we go!
Interface to make it easier to say "what is an address" without making abstract classes then overriding methods and causing the "bad design" feeling ;)
interface Address {
function getCivicNo();
function getRoad();
function getState();
function getCountry();
}
Abstract Entity which you don't really need but if you dont use it you need to duplicate the ID code.
/**
* @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 which you can store alone or have linked to a "ComplexAddress"
/**
* @ORM\Entity
*/
class BasicAddress extends AbstractEntity implements Address {
/** @ORM\Column() */
private $road;
public function getRoad() {
return $this->road;
}
// etc etc
}
"ComplexAddress" is just here to let you re-use the code for delegating calls to a basic address.
/**
* @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
}
Edit: Just noticed I forgot to put cascade parameters for deletion, you might need to handle this directly though - when a BasicAddress
is deleted also delete the other addresses that use it.
Upvotes: 1
Reputation: 14343
Instead of extending the Address class, you'll have to create a OneToMany relationship on the Address and a ManyToOne relationship on the PersonalAddress and BusinessAddress. Something like this:
<?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();
}
// ...
}
And, for the child classes:
<?php
// ...
// ...
class PersonalAddress
{
// ...
/**
* @ORM\ManyToOne(targetEntity="Address", inversedBy="personalAddresses")
* @ORM\JoinColumn(name="base_address_id", referencedColumnName="id")
*/
private $address;
// ...
}
Upvotes: 0