Let's say I have a configuration like this (snippet from official guide ):
$config = [
// ...
'container' => [
'definitions' => [
'yii\widgets\LinkPager' => ['maxButtonCount' => 5],
],
],
// ...
];
I create a class named FancyLinkPager:
class FancyLinkPager extends \yii\widgets\LinkPager
{
// ...
}
When I create an object of class FancyLinkPager like so (please ignore the $pagination object, it's here for correctness sake):
$pagination = \Yii::createObject(Pagination::class);
$linkPager = \Yii::createObject(['class' => LinkPager::class, 'pagination' => $pagination]);
$fancyLinkPager = \Yii::createObject(['class' => FancyLinkPager::class, 'pagination' => $pagination]);
$linkPager->maxButtonCount; // 5 as configured
$fancyLinkPager->maxButtonCount; // 10 as LinkPager's default
My problem is that I wished $fancyLinkPager->maxButtonCount to be 5 as configured. I know I can add another line in the configuration or adjust it to specify my custom class, but it's not solution for me because:
My question is: is there any framework-supported way of achieving this? The solutions I came up with are:
Maybe the only real solution would be to create my own implementation of yii\\di\\Container that allows for inheriting configuration from parent classes but before I dive into this I would like to know if I haven't overlooked something.
Finally I came up with my own implementation of DI container introducing new configurable property:
public $inheritableDefinitions = [];
Full class code:
<?php
namespace app\di;
/**
* @inheritdoc
*/
class Container extends \yii\di\Container
{
/**
* @var array inheritable object configurations indexed by class name
*/
public $inheritableDefinitions = [];
/**
* @inheritdoc
*
* @param string $class
* @param array $params
* @param array $config
*
* @return object the newly created instance of the specified class
*/
protected function build($class, $params, $config) {
$config = $this->mergeInheritedConfiguration($class, $config);
return parent::build($class, $params, $config);
}
/**
* Merges configuration arrays of parent classes into configuration of newly created instance of the specified class.
* Properties defined in child class (via configuration or property declarations) will not get overridden.
*
* @param string $class
* @param array $config
*
* @return array
*/
protected function mergeInheritedConfiguration($class, $config) {
if (empty($this->inheritableDefinitions)) {
return $config;
}
$inheritedConfig = [];
/** @var \ReflectionClass $reflection */
list($reflection) = $this->getDependencies($class);
foreach ($this->inheritableDefinitions as $parentClass => $parentConfig) {
if ($class === $parentClass) {
$inheritedConfig = array_merge($inheritedConfig, $parentConfig);
} else if (is_subclass_of($class, $parentClass)) {
/** @var \ReflectionClass $parentReflection */
list($parentReflection) = $this->getDependencies($parentClass);
// The "@" is necessary because of possible (and wanted) array to string conversions
$notInheritableProperties = @array_diff_assoc($reflection->getDefaultProperties(),
$parentReflection->getDefaultProperties());
// We don't want to override properties defined specifically in child class
$parentConfig = array_diff_key($parentConfig, $notInheritableProperties);
$inheritedConfig = array_merge($inheritedConfig, $parentConfig);
}
}
return array_merge($inheritedConfig, $config);
}
}
This is how it can be used to accomplish customization of LinkPager described in the question:
'container' => [
'inheritableDefinitions' => [
'yii\widgets\LinkPager' => ['maxButtonCount' => 5],
],
],
Now if I create a class FancyLinkPager
that extends yii\\widgets\\LinkPager
the DI container will merge the default configuration:
$pagination = \Yii::createObject(Pagination::class);
$linkPager = \Yii::createObject(['class' => LinkPager::class, 'pagination' => $pagination]);
$fancyLinkPager = \Yii::createObject(['class' => FancyLinkPager::class, 'pagination' => $pagination]);
$linkPager->maxButtonCount; // 5 as configured
$fancyLinkPager->maxButtonCount; // 5 as configured - hurrah!
I have also taken into account qiangxue's comment about explicit setting of default property values in class definition, so if we declare the FancyLinkPager
class as such:
class FancyLinkPager extends LinkPager
{
public $maxButtonCount = 18;
}
the property setting will be respected:
$linkPager->maxButtonCount; // 5 as configured
$fancyLinkPager->maxButtonCount; // 18 as declared
To swap default DI container in your application you have to explicitly set Yii::$container
somewhere in an entry script:
Yii::$container = new \app\di\Container();
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.