在用yii2做大型网站的时候,尤其是做产品的时候,譬如做电商系统,在很多页面的侧栏,或者内容部分的底部等部分,我们希望通过配置的方式,很容易的把某个块加入,譬如在分类页面限制客户的浏览产品记录和newsletter,在产品页面,我们同样想显示这些,我们可能做出来很多这样的显示独立块,让客户在后台通过配置的方式就可以加入这些块,我们需要考虑的如下
- 这些块由两部分组成,数据提供者block部分,view html部分,通过block提供的动态数据,画出整个页面。
- 当前功能块可能包含其他的功能块,也就是多个功能块嵌套。
- 实现上述功能,我们就可以做出来很多独立功能块,在各个页面很容易的配置。下面是实现的方法
我的yii2 fec扩展已经实现这个功能,github地址:https://github.com/fancyecommerce/yii2-fec
下面讲述的是实现原理。
- 这个是实现的原理文件:
<?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);
}
}
}
- <?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);
- }
-
- }
- }
<?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;
}
}
- <?php
- namespace fec\block;
- use Yii;
- class TestMenu
- {
- public $terry1;
- public $terry2;
-
- public function getLastData()
- {
- $arr = [
- 'terry1' =>$this->terry1,
- 'terry2' =>$this->terry2,
- ];
- return $arr;
- }
- }
<?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>
- <div>My Name is
- <?php
- echo $terry1."_".$terry2;
- ?>
- </div>
<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);
?>
- <?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);
- ?>
<?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
- My Name is My111_My222
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',
],
]
],
],
];
- <?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',
- ],
- ]
- ],
- ],
- ];
<?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',
],
]
],
- 'params' => [
- 'water' => 'good',
- 'block' =>[
- 'menu' =>[
- # 必填
- 'class' => 'fec\block\TestMenu',
- 'view' => '@fec/views/testmenu/index.php',
- # 下面为选填
- 'method'=> 'getLastData',
- 'terry1'=> 'My1',
- 'terry2'=> 'My2',
- ],
- ]
- ],
'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')
?>
- <?php
- use fec\helpers\CView;
- echo CView::getConfigChildHtml('menu')
- ?>
<?php
use fec\helpers\CView;
echo CView::getConfigChildHtml('menu')
?>
My Name is My1_My2
- My Name is My1_My2
My Name is My1_My2
总结:这个功能是可以深度嵌套,譬如在view1.php调用了child -> menu,
在menu的view文件中又调用了child–>product ,…..,深入嵌套,这是魅力所在。
蛮好的。
我感觉用接口的方式也方便。VIEW通过AJAX请求控制器(或block)。
和你说的不是一种实现方式,我的这个方式类似于,吧一个view文件切成几个view文件,
就像侧栏的一些框,譬如newsletter,客户访问记录等, 很多页面都可以用到,这样直接配置上就可以了。
可是这个功能为什么不使用widget来实现呢,复用性广泛且需要写的代码比这个少很多
原理都是一样的,最终都是通过php 的 ob函数,使用自己写的,我是想以后添加多模板,可以在多个模板路径中,通过模板路径的优先级,加载对应的view文件
如果我view文件中,放一个块,也就是你说的widget,但是如果我这个快中,还要放一个块呢? 在放一个块呢?这种深度嵌套,还是自己写比较好。