2

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?

yivi
  • 42,438
  • 18
  • 116
  • 138
deko
  • 350
  • 4
  • 15
  • PHP doesn't allow you to mix classes dynamically. This might be a good use for [traits](https://www.sitepoint.com/using-traits-in-php-5-4/) – Barmar Sep 11 '18 at 15:46

1 Answers1

3

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.

yivi
  • 42,438
  • 18
  • 116
  • 138
  • Thank you! I am recently new to oop and I understand the power and cleanliness of oop. This post helped me a bit to understand how extends work. Good work sir! – Kaloy Dec 11 '20 at 19:15