Let's say that we have a Dog
class and a Category
class that dogs can be assigned to.
class Dog {
function categories() {} // Return the categories of the dog.
}
class Category {
function dogs() {} // Return the dogs under this category.
}
Dogs can have 'pet' and 'shepherd' categories. When assigned to 'pet' category, it's a 'pet dog' and the same goes for 'shepherd'.
Pet dogs and shepherd dogs have different attributes and functions. However, a dog can be both a 'pet dog' and a 'shepherd dog'.
I can imagine having different interfaces for 'pet dog' and 'shepherd dog', e.g.
interface Huggable {
function hug();
}
interface Trainable {
function train();
}
Ideally, when a dog is assigned to 'pet' category, it implements the Huggable
interface, if it's assigned to 'shepherd' category, it implements the Trainable
category.
Is it possible?
As I commented, it is not possible to implement this with PHP natively.
But you could implement something using decorators, for example.
A silly decorator approach:
You'd have your to-be-decorated class:
class Animal {
protected $categories = [];
public function getCategories() {
return $this->categories;
}
public function addCategory( string $category ) {
// we should check the animal doesn't already belong to this category
$this->categories[] = $category;
}
}
Your interfaces, Trainable
and Huggable
:
interface Trainable {
function train();
}
interface Huggable {
// see https://github.com/php-fig/fig-standards/blob/master/proposed/psr-8-hug/psr-8-hug.md
function hug() : bool;
}
One decorator that implements Trainable, and adds the specific category to the decorated instance:
class PetDecorator extends Animal implements Trainable {
public function __construct( Animal $animal ) {
$this->categories = $animal->getCategories();
$this->addCategory('pet');
}
public function train() {
echo "I'm housebroken!\n";
}
}
And another FluffyDecorator
that implements Huggable
class FluffyDecorator extends Animal implements Huggable {
public function __construct( Animal $animal ) {
$this->categories = $animal->getCategories();
$this->addCategory('loveBear');
}
public function hug( ) :bool {
echo "Much hug!\n";
return true;
}
}
Finally, you'd use it thus:
$fido = new Animal();
$fidoPet = new PetDecorator($fido);
$fidoPet->train();
// I'm housebroken!
print_r($fidoPet->getCategories());
/*
Array
(
[0] => pet
)
*/
$fidoLove = new FluffyDecorator($fidoPet);
// Much hug!
$fidoLove->hug();
print_r($fidoLove->getCategories());
/*
Array
(
[0] => pet
[1] => loveBear
)
*/
The many-to-many relationship between "Dogs" and "Categories" I leave up to you. That's a separate issue and could be handled in many different ways.