Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/php/291.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Php Yii2依赖项注入、配置和继承_Php_Inheritance_Dependency Injection_Yii2 - Fatal编程技术网

Php Yii2依赖项注入、配置和继承

Php Yii2依赖项注入、配置和继承,php,inheritance,dependency-injection,yii2,Php,Inheritance,Dependency Injection,Yii2,假设我有这样一个配置(来自的代码片段): 我创建了一个名为FancyLinkPager的类: class FancyLinkPager extends \yii\widgets\LinkPager { // ... } 当我像这样创建FancyLinkPager类的对象时(请忽略$pagination对象,它在这里是为了正确性): 我的问题是,我希望$fancyLinkPager->maxButtonCount按配置为5。我知道我可以在配置中添加另一行或调整它以指定自定义类,但这不是我

假设我有这样一个配置(来自的代码片段):

我创建了一个名为FancyLinkPager的类:

class FancyLinkPager extends \yii\widgets\LinkPager
{
    // ...
}
当我像这样创建FancyLinkPager类的对象时(请忽略$pagination对象,它在这里是为了正确性):

我的问题是,我希望$fancyLinkPager->maxButtonCount按配置为5。我知道我可以在配置中添加另一行或调整它以指定自定义类,但这不是我的解决方案,因为:

  • 我想保持代码干燥
  • 这是我的需求的一个过于简单的例子——在现实世界中,您不希望有多个LinkPager的子类,但对于其他对象来说,这是非常可能的
  • 我的问题是:有没有框架支持的方法来实现这一点?我提出的解决方案是:

  • 在FancyLinkPager(或另一个中间类或trait)中破解一个自定义的_构造,这样它就可以查看应用程序的配置并在实例上调用Yii::configure,但我没有找到一种通用的方法
  • 使用“setup”对象(如LinkPagerSettings)将依赖项注入FancyLinkPager,并在我的配置的容器部分中配置该类,但使用vanilla LinkPager实例也会带来一些麻烦

  • 也许唯一真正的解决方案是创建我自己的yii\di\Container实现,允许从父类继承配置,但在深入研究之前,我想知道我是否忽略了什么。

    最后,我提出了我自己的di Container实现,引入了新的可配置属性:

    public $inheritableDefinitions = [];
    
    完整类别代码:

    <?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);
        }
    }
    
    现在,如果我创建一个扩展
    yii\widgets\LinkPager
    的类
    FancyLinkPager
    ,DI容器将合并默认配置:

    $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!
    
    我还考虑了类定义中默认属性值的显式设置,因此如果我们将
    FancyLinkPager
    类声明为:

    class FancyLinkPager extends LinkPager
    {
        public $maxButtonCount = 18;
    }
    
    将遵守以下属性设置:

    $linkPager->maxButtonCount; // 5 as configured
    $fancyLinkPager->maxButtonCount; // 18 as declared
    
    要在应用程序中交换默认DI容器,必须在条目脚本中的某个位置显式设置
    Yii::$container

    Yii::$container = new \app\di\Container();
    

    我也有同样的问题:我认为不可能配置父类。因此,我的配置有一些冗余设置。。。根据,重写容器可能会导致性能问题,因为。我认为你的构造方法是合理的。如果你想了解这一点,请发布一个答案。感谢@robsch为我指出Yii2的工作人员对此的评论,我自己找不到。这使我认识到,如果配置继承是可选的,并且是有意的,那就最好了。
    class FancyLinkPager extends LinkPager
    {
        public $maxButtonCount = 18;
    }
    
    $linkPager->maxButtonCount; // 5 as configured
    $fancyLinkPager->maxButtonCount; // 18 as declared
    
    Yii::$container = new \app\di\Container();