yii2 页面功能块配置实现原理(前端+后端提供数据类),以及Views深度嵌套

在用yii2做大型网站的时候,尤其是做产品的时候,譬如做电商系统,在很多页面的侧栏,或者内容部分的底部等部分,我们希望通过配置的方式,很容易的把某个块加入,譬如在分类页面限制客户的浏览产品记录和newsletter,在产品页面,我们同样想显示这些,我们可能做出来很多这样的显示独立块,让客户在后台通过配置的方式就可以加入这些块,我们需要考虑的如下

  1. 这些块由两部分组成,数据提供者block部分,view html部分,通过block提供的动态数据,画出整个页面。
  2. 当前功能块可能包含其他的功能块,也就是多个功能块嵌套。
  3. 实现上述功能,我们就可以做出来很多独立功能块,在各个页面很容易的配置。下面是实现的方法

我的yii2 fec扩展已经实现这个功能,github地址:https://github.com/fancyecommerce/yii2-fec

下面讲述的是实现原理。

  1. 这个是实现的原理文件:
<?php
namespace fec\helpers;
use Yii;
use yii\base\View;
use yii\base\InvalidConfigException;
class CView
{	
  # 功能块:
  # 本功能的作用是通过一个类文件,和一个view 文件,生成一个html块,而且在这个html中还可以嵌套其他的块
  # 这样设计的好处:譬如在前端,我们在很多url中都会有一些公用的侧栏块,我们很希望我们的块可以通过配置的方式很容易的加入到侧栏
  # 譬如电商网站侧栏的:客户的浏览记录,我们在多个页面都想加入这个功能,我们就可以很方便的做加入。
  
  # 默认的方法,功能块的提供数据的默认方法。可以在配置中配置method方法自定义。
  const DATA_METHOD = 'getLastData';
  
  /*  你可以在view 文件中通过下面的方式使用
    <?php

      use fec\helpers\CView;
      $config = [
        # 必填
        'class' => 'fec\block\TestMenu',
        'view'  => '@fec/views/testmenu/index.php',
        # 下面为选填
        'method'=> 'getLastData',
        'terry1'=> 'My1',
        'terry2'=> 'My2',
      ];
      echo CView::getChildHtml($config)
      ?>
  */
    public static function getChildHtml($config)
    {
    if(!isset($config['class']) || empty($config['class'])
    || !isset($config['view']) || empty($config['view'])
    ){
      throw new InvalidConfigException('view and class must exist in array config!');
    }
    $method = self::DATA_METHOD;
    if(isset($config['method']) && !empty($config['method'])){
      $method = $config['method'];
      unset($config['method']);
    }
    $view = $config['view'];
    unset($config['view']);
    $ob = Yii::createObject($config);
    $params = $ob->$method();
    return Yii::$app->view->render($view, $params);
    
    }
  # 通过配置
  /*
  1.add config param to modules params or application params.
  params.php
  [
    'params' => [
      'block' =>[
        'menu' =>[
          # 必填
          'class' => 'fec\block\TestMenu',
          'view'  => '@fec/views/testmenu/index.php',
          # 下面为选填
          'method'=> 'getLastData',
          'terry1'=> 'My1',
          'terry2'=> 'My2',
        ],
      ]
    ]
  ]
  2.
  use fec\helpers\CView;
  CView::getConfigChildHtml('menu');
  */
  public static function getConfigChildHtml($configKey){
    $config = [];
    # get config from module param 
    if($module = Yii::$app->controller->module){
      $module_config = CModule::param("block");
      if(isset($module_config[$configKey])){
        $config = $module_config[$configKey];
      }
    }
    # if module config param is empty or not exist,
    # get config from application
    if(empty($config)){
      $app_config = CConfig::param("block");
      if(isset($app_config[$configKey])){
        $config = $app_config[$configKey];
      }
    }
    
    if(!isset($config['class']) || empty($config['class'])
    || !isset($config['view']) || empty($config['view'])
    ){
      throw new InvalidConfigException('view and class must exist in array config!');
    }else{
      return self::getChildHtml($config);
    }
    
  }
}

2.新建功能块的block文件:

<?php
namespace fec\block;
use Yii;

class TestMenu
{
  public $terry1;
  public $terry2;
  
    public function getLastData()
    {
        $arr = [
      'terry1' =>$this->terry1,
      'terry2' =>$this->terry2,
    ];
        return $arr;
    }
}

3.新建view显示部分:

<div>My Name is
<?php

  echo $terry1."_".$terry2;
?>

</div>

4.在其他的view文件中调用:

<?php

use fec\helpers\CView;
$config = [
  'class' => 'fec\block\TestMenu',
  'view'  => '@fec/views/testmenu/index.php',
  # 下面为选填
  'method'=> 'getLastData',
  'terry1'=> 'My111',
  'terry2'=> 'My222',
];
echo CView::getChildHtml($config);
?>

 

也就是通过上面的配置数组指定block的提供者, view文件的实现文件,也就是上面的步骤 2 和3,method为选填,如果不填写,默认为getLastData函数,terry1,terry2为初始化提供变量,然后我们就可以看到输出了

My Name is My111_My222

5.我们通过配置的方式调用CView::getConfigChildHtml()函数

5.1 加入module配置:(在report 配置中)

<?php
return [
  'report' => [
    'class' => 'appadmin\code\Report\Module',
    
    'components'=>[
      'mycomponent' => [
        'class' => 'appadmin\component\MyComponent',
        'terry' => 'xxxx',
      ],
    ],
    
    'params' => [
      'water' => 'good',
      'block' =>[
        'menu' =>[
          # 必填
          'class' => 'fec\block\TestMenu',
          'view'  => '@fec/views/testmenu/index.php',
          # 下面为选填
          'method'=> 'getLastData',
          'terry1'=> 'My1',
          'terry2'=> 'My2',
        ],
      ]
    ],
  ],
];

当然,您如果不使用模块,可以在params.php中直接加入block的配置部分:

'params' => [
      'water' => 'good',
      'block' =>[
        'menu' =>[
          # 必填
          'class' => 'fec\block\TestMenu',
          'view'  => '@fec/views/testmenu/index.php',
          # 下面为选填
          'method'=> 'getLastData',
          'terry1'=> 'My1',
          'terry2'=> 'My2',
        ],
      ]
    ],

如果application 和module同时配置,那么在当前module下面,module下面的配置有限,

使用:

<?php

use fec\helpers\CView;

echo CView::getConfigChildHtml('menu')
?>
My Name is My1_My2

总结:这个功能是可以深度嵌套,譬如在view1.php调用了child -> menu,

在menu的view文件中又调用了child–>product ,…..,深入嵌套,这是魅力所在。

 

 

《yii2 页面功能块配置实现原理(前端+后端提供数据类),以及Views深度嵌套》有5个想法

    1. 和你说的不是一种实现方式,我的这个方式类似于,吧一个view文件切成几个view文件,
      就像侧栏的一些框,譬如newsletter,客户访问记录等, 很多页面都可以用到,这样直接配置上就可以了。

    1. 原理都是一样的,最终都是通过php 的 ob函数,使用自己写的,我是想以后添加多模板,可以在多个模板路径中,通过模板路径的优先级,加载对应的view文件

    2. 如果我view文件中,放一个块,也就是你说的widget,但是如果我这个快中,还要放一个块呢? 在放一个块呢?这种深度嵌套,还是自己写比较好。

发表评论

电子邮件地址不会被公开。 必填项已用*标注