I'm working on a web app with Yii2 and PHP and am facing a typical multiple inheritance situation.
I have the classes A
and B
extend Yii2's ActiveRecord
class since they represent relational data stored in a DB. But then I have class C, which doesn't (and shouldn't) extend ActiveRecord
but shares common behaviors and member variables with A
and B
. In other words, the three classes genuinely share a common "is a" relation with a real-world entity, but only A
and B
are storable in a DB.
The way I've got things somewhat working until now is by using traits :
abstract class AbstractMotherClass extends ActiveRecord {
use MyTrait;
}
class A extends AbstractMotherClass {}
class B extends AbstractMotherClass {}
class C {
use MyTrait;
}
trait MyTrait {
public $someVariableInherentToAllThreeClasses;
public function someMethodInherentToAllThreeClasses() {
// do something
}
}
Then I have a method which can take any of the three classes (A
, B
or C
) and work with it. Until now, I only had to throw A
or B
at it so I just wrote
public fonction someMethod(AbstractMotherClass $entity) {}
so I could get type hinting and other things in my IDE. But now I have to pass C
as well and the app crashes since the method doesn't get its expected AbstractMotherClass
instance if I call someMethod(new C());
. To solve this, I would need a common class that all A
, B
, AND C
could extend, so that I could type hint that class in my method. But that would be multiple inheritance since A
and B
must also extend ActiveRecord
but C
can't.
I've found a lot of multiple inheritance problems, but they all have been solved by changing the object structure, splitting responsibilities, using composition over inheritance, and so on. I couldn't manage to apply those solutions here as they didn't seem suitable nor practical, but I might be wrong.
What would be the best way to do this ?
Also if anyone has a better title suggestion, I'd be happy to change it (I couldn't find a good one).
As Greg Schmidt mentioned as well, you could use interfaces
class ActiveRecord {
}
interface SameInterface {
public function someMethodInherentToAllThreeClasses();
}
abstract class AbstractMotherClass extends ActiveRecord implements SameInterface{
use MyTrait;
}
class A extends AbstractMotherClass {}
class B extends AbstractMotherClass {}
class C implements SameInterface{
use MyTrait;
}
trait MyTrait {
public $someVariableInherentToAllThreeClasses;
public function someMethodInherentToAllThreeClasses() {
return 'bar';
}
}
function foo(SameInterface $o) {
return $o->someMethodInherentToAllThreeClasses().PHP_EOL;
}
echo foo(new C());
Granted you have to copy paste someMethodInherentToAllThreeClasses in the interface. Interfaces are usually used for solving some multiple inheritance problem.