I think you are trying to make a trait work for which it is not intended.
Traits are not a form of multiple inheritance, but rather "horizontal reuse" - they are often called "copy and paste using the compiler." Thus, the task is to provide some code, so you do not need to manually copy it to the class. Its only relation is to the class in which the use statement is executed, where the code is "inserted". To help in this role, he can make some basic requirements of this target class, but after that the trait does not take part in inheritance .
In your example, you are worried that a subclass might try to access $primaryModel without running the constructor code that initializes it, and you are trying to use this flag to provide this; but in reality this is not the main responsibility.
The following Sub class definitions are completely equivalent:
trait Test { public function foo() { echo 'Hello, World!'; } } class ParentWithTrait { use Test; } class Sub inherits ParentWithTrait { }
against
class ParentWithMethodDefinition { public function foo() { echo 'Hello, World!'; } } class Sub inherits ParentWithMethodDefinition { }
In any case, the Sub class can have its own definition of foo() and bypass the logic written in the parent class.
The only contract that can prevent this is the final keyword, which in your case means marking your constructor as final . You can then specify an extension point that can be overridden for subclasses to add their own initialization:
class Base { final public function __construct() { important_things();
A distinguishing feature may also mark its constructor as final, but it is part of the inserted code, and not a requirement for the class using the attribute. You could use the trait with the constructor, but then write a new constructor, and also completely mask the version of the trait:
trait Test { final public function __construct() { echo "Trait Constructor"; } } class Noisy { use Test; } class Silent { use Test; public function __construct() {
As for this feature, it is like buying a bottle of beer and pouring it into the sink: you asked for its code and did not use it, but that is your problem.
However, you can also use the methods of this attribute by creating a new method with the same code, but with a different name and / or with a different visibility. This means that you can mix code with attributes declaring constructors and use this code in a more complex constructor or somewhere else in the class.
The target class may also use the final + hook pattern:
trait TestOne { final public function __construct() { echo "Trait TestOne Constructor\n"; } } trait TestTwo { final public function __construct() { echo "Trait TestTwo Constructor\n"; } } class Mixed { final public function __construct() { echo "Beginning\n"; $this->testOneConstructor(); echo "Middle\n"; $this->testTwoConstructor(); echo "After Traits\n"; $this->onConstruct(); echo "After Sub-Class Hook\n"; } use TestOne { __construct as private testOneConstructor; } use TestTwo { __construct as private testTwoConstructor; } protected function onConstruct() { echo "Default hook\n"; } } class ChildOfMixed extends Mixed { protected function onConstruct() { echo "Child hook\n"; } }
This feature did not force the Mixed class to implement this template, but it allowed it, in accordance with its purpose, to facilitate code reuse.
Interestingly, the following code does not work because the as keyword adds an alias rather than renaming a regular method, so it tries to override the final constructor from Mixed :
class ChildOfMixed extends Mixed { use TestTwo { __construct as private testTwoConstructor; } protected function onConstruct() { $this->testTwoConstructor(); echo "Child hook\n"; } }