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

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

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

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

下面讲述的是实现原理。

  1. 这个是实现的原理文件:
  1. <?php
  2. namespace fec\helpers;
  3. use Yii;
  4. use yii\base\View;
  5. use yii\base\InvalidConfigException;
  6. class CView
  7. {
  8. # 功能块:
  9. # 本功能的作用是通过一个类文件,和一个view 文件,生成一个html块,而且在这个html中还可以嵌套其他的块
  10. # 这样设计的好处:譬如在前端,我们在很多url中都会有一些公用的侧栏块,我们很希望我们的块可以通过配置的方式很容易的加入到侧栏
  11. # 譬如电商网站侧栏的:客户的浏览记录,我们在多个页面都想加入这个功能,我们就可以很方便的做加入。
  12. # 默认的方法,功能块的提供数据的默认方法。可以在配置中配置method方法自定义。
  13. const DATA_METHOD = 'getLastData';
  14. /* 你可以在view 文件中通过下面的方式使用
  15. <?php
  16. use fec\helpers\CView;
  17. $config = [
  18. # 必填
  19. 'class' => 'fec\block\TestMenu',
  20. 'view' => '@fec/views/testmenu/index.php',
  21. # 下面为选填
  22. 'method'=> 'getLastData',
  23. 'terry1'=> 'My1',
  24. 'terry2'=> 'My2',
  25. ];
  26. echo CView::getChildHtml($config)
  27. ?>
  28. */
  29. public static function getChildHtml($config)
  30. {
  31. if(!isset($config['class']) || empty($config['class'])
  32. || !isset($config['view']) || empty($config['view'])
  33. ){
  34. throw new InvalidConfigException('view and class must exist in array config!');
  35. }
  36. $method = self::DATA_METHOD;
  37. if(isset($config['method']) && !empty($config['method'])){
  38. $method = $config['method'];
  39. unset($config['method']);
  40. }
  41. $view = $config['view'];
  42. unset($config['view']);
  43. $ob = Yii::createObject($config);
  44. $params = $ob->$method();
  45. return Yii::$app->view->render($view, $params);
  46. }
  47. # 通过配置
  48. /*
  49. 1.add config param to modules params or application params.
  50. params.php
  51. [
  52. 'params' => [
  53. 'block' =>[
  54. 'menu' =>[
  55. # 必填
  56. 'class' => 'fec\block\TestMenu',
  57. 'view' => '@fec/views/testmenu/index.php',
  58. # 下面为选填
  59. 'method'=> 'getLastData',
  60. 'terry1'=> 'My1',
  61. 'terry2'=> 'My2',
  62. ],
  63. ]
  64. ]
  65. ]
  66. 2.
  67. use fec\helpers\CView;
  68. CView::getConfigChildHtml('menu');
  69. */
  70. public static function getConfigChildHtml($configKey){
  71. $config = [];
  72. # get config from module param
  73. if($module = Yii::$app->controller->module){
  74. $module_config = CModule::param("block");
  75. if(isset($module_config[$configKey])){
  76. $config = $module_config[$configKey];
  77. }
  78. }
  79. # if module config param is empty or not exist,
  80. # get config from application
  81. if(empty($config)){
  82. $app_config = CConfig::param("block");
  83. if(isset($app_config[$configKey])){
  84. $config = $app_config[$configKey];
  85. }
  86. }
  87. if(!isset($config['class']) || empty($config['class'])
  88. || !isset($config['view']) || empty($config['view'])
  89. ){
  90. throw new InvalidConfigException('view and class must exist in array config!');
  91. }else{
  92. return self::getChildHtml($config);
  93. }
  94. }
  95. }
<?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文件:

  1. <?php
  2. namespace fec\block;
  3. use Yii;
  4. class TestMenu
  5. {
  6. public $terry1;
  7. public $terry2;
  8. public function getLastData()
  9. {
  10. $arr = [
  11. 'terry1' =>$this->terry1,
  12. 'terry2' =>$this->terry2,
  13. ];
  14. return $arr;
  15. }
  16. }
<?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显示部分:

  1. <div>My Name is
  2. <?php
  3. echo $terry1."_".$terry2;
  4. ?>
  5. </div>
<div>My Name is
<?php

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

</div>

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

  1. <?php
  2. use fec\helpers\CView;
  3. $config = [
  4. 'class' => 'fec\block\TestMenu',
  5. 'view' => '@fec/views/testmenu/index.php',
  6. # 下面为选填
  7. 'method'=> 'getLastData',
  8. 'terry1'=> 'My111',
  9. 'terry2'=> 'My222',
  10. ];
  11. echo CView::getChildHtml($config);
  12. ?>
<?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为初始化提供变量,然后我们就可以看到输出了

  1. My Name is My111_My222
My Name is My111_My222

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

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

  1. <?php
  2. return [
  3. 'report' => [
  4. 'class' => 'appadmin\code\Report\Module',
  5. 'components'=>[
  6. 'mycomponent' => [
  7. 'class' => 'appadmin\component\MyComponent',
  8. 'terry' => 'xxxx',
  9. ],
  10. ],
  11. 'params' => [
  12. 'water' => 'good',
  13. 'block' =>[
  14. 'menu' =>[
  15. # 必填
  16. 'class' => 'fec\block\TestMenu',
  17. 'view' => '@fec/views/testmenu/index.php',
  18. # 下面为选填
  19. 'method'=> 'getLastData',
  20. 'terry1'=> 'My1',
  21. 'terry2'=> 'My2',
  22. ],
  23. ]
  24. ],
  25. ],
  26. ];
<?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的配置部分:

  1. 'params' => [
  2. 'water' => 'good',
  3. 'block' =>[
  4. 'menu' =>[
  5. # 必填
  6. 'class' => 'fec\block\TestMenu',
  7. 'view' => '@fec/views/testmenu/index.php',
  8. # 下面为选填
  9. 'method'=> 'getLastData',
  10. 'terry1'=> 'My1',
  11. 'terry2'=> 'My2',
  12. ],
  13. ]
  14. ],
'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下面的配置有限,

使用:

  1. <?php
  2. use fec\helpers\CView;
  3. echo CView::getConfigChildHtml('menu')
  4. ?>
<?php

use fec\helpers\CView;

echo CView::getConfigChildHtml('menu')
?>
  1. My Name is My1_My2
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,但是如果我这个快中,还要放一个块呢? 在放一个块呢?这种深度嵌套,还是自己写比较好。

发表评论

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