Before giving the code that can help you solve these problems, I want to note that the question you faced is the right signal that somewhere at the previous stage you made a number of incorrect architectural decisions.
You need to break the ancestor's encapsulation, while you are heavily dependent on its implementation. So much so that, in principle, they are ready to duplicate as many as two classes in order to correct a small part of this very implementation.
Inheritance is always a very strong connection between classes . In your case, I would advise using composition instead of inheritance and implementing the yii\data\DataProviderInterface .
But, if you think that this does not fit your case, here is the solution:
You can reach the $_models private property using closures, specifying a new scope ( http://php.net/manual/ru/closure.bindto.php ).
Consider a test case:
abstract class TestGrandPa { private $_models; public function __construct($m) { $this->_models = $m; } /** * @return mixed */ public function getModels() { return $this->_models; } /** * @param mixed $models */ public function setModels($models) { $this->_models = $models; } } class TestPa extends TestGrandPa { public function greet() { echo 'Hello ' . $this->getModels(); } protected function prepareGreet($m) { return $m . '!'; } } class TestYouth extends TestPa { public function prepare() { $closure = function() { return $this->_models; }; $binded = $closure->bindTo($this, 'TestGrandPa'); $this->setModels($this->prepareGreet($binded())); } } $y = new TestYouth('Denis'); $y->prepare(); echo $y->greet(); die();
This code will output Hello Denis! .
Here I create a closure that should return the value of an object property:
$closure = function() { return $this->_models; };
Here I tie the closure to the current instance and specify a new scope - the TestGrandPa class:
$binded = $closure->bindTo($this, 'TestGrandPa');
If I described the method simply:
public function prepare() { // $closure = function() { // return $this->_models; // }; // $binded = $closure->bindTo($this, 'TestGrandPa'); $this->setModels($this->prepareGreet($this->_models)); }
I would get Hello ! .
Those. for your case, to reach BaseDataProvider::$_models , you need to rewrite the method like this:
public function prepare($forcePrepare = false) { parent::prepare($forcePrepare); if ($forcePrepare || $this->_tree === null) { $closure = function() { return $this->_models; }; $binded = $closure->bindTo($this, 'yii\data\BaseDataProvider'); $this->_tree = $this->prepareTree($binded()); } }
I note once again that it is preferable (it will be easier to support later) to review a number of architectural solutions that led to the emergence of this problem.