使用 widget
yii 的小组件是为了实现视图的可重用,例如一些常用的日期组件、弹框插件等,yii 巧妙的使用了 widget
将其封装,在视图中可简单调用,这样良好的封装减少我们开发重复代码,是一种优秀的设计。下面我们做个示例。
例如面包屑组件的使用1
2
3
4
5
6
7
8
9
10// 在视图文件中引入 breadcrumb 组件
use yii\widgets\Breadcrumbs;
Breadcrumbs::widget([
'links' => [
['Home']
]
]);
// 只是简单的代码就讲 links 数组中的面包屑地址生成一段 html ,这样就减少我们代码量
所有的小组件都是继承 yii\base\Widget
类,里面定义了静态的方法 widget
,begin
,end
,在上面例子中使用了 widget
,在调用 yii\widgets\ActiveForm
组件的时候,由于有开始标签 form
和闭合标签 </form>
,在标签中间也有相应的输出,yii
也提供了便捷的实现方法,在开始调用 begin
,中间书写其他表单元素,最后调用 end
1 |
|
创建 widget
创建 widget
必须继承 yii\base\Widget
来实现,实现 init
和 run
方法
由于 yii\base\Widget
继承 yii\base\Component
,而 yii\base\Component
继承 yii\base\Object
而 yii\base\Object
在实例化方法中会调用 $this->init()
方法,所以在 widget
实例化的时候会调用 init
方法
run
方法会在 yii\base\Widget
继承的 widget
和 end
方法中调用
1 | #path common/widgets/Demo |
使用方式就想上面的例子一样,在视图文件中引入,调用 widget
方法1
2
3
4
5
6# view file
use common\widgets\Demo;
Demo::widget(); // 不传参数
Demo::widget(['message' => 'this is widget']); // 传递参数 message
上面由于继承 yii\base\Widget
,所以调用的静态方法 widget
也是这个类中,看下其的实现1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21public static function widget($config = [])
{
ob_start();
ob_implicit_flush(false);
try {
$config['class'] = get_called_class(); // 获取调用类名
// 调用 Yii 的 createObject 实例化,使用的 DI 容器的 get 方法创建
// 在实例化的时候会调用 init 方法
$widget = Yii::createObject($config);
$out = $widget->run(); // 执行 run 方法,也就是上面继承实现的方法
} catch (\Exception $e) {
if (ob_get_level() > 0) {
ob_end_clean();
}
throw $e;
}
return ob_get_clean() . $out; // 返回结果
}
Yii::createObject
在 vendor/yiisoft/yii2/BaseYii.php
中实现1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17public static function createObject($type, array $params = [])
{
if (is_string($type)) {
return static::$container->get($type, $params);
} elseif (is_array($type) && isset($type['class'])) {
// 由于上面传递的是数组,并且里面有 class 这个键值,所以走这里的判断
$class = $type['class'];
unset($type['class']);
return static::$container->get($class, $params, $type); // 这里调用的是 `yii\di\Container` 的 `get` 方法
} elseif (is_callable($type, true)) {
return static::$container->invoke($type, $params);
} elseif (is_array($type)) {
throw new InvalidConfigException('Object configuration must be an array containing a "class" element.');
} else {
throw new InvalidConfigException('Unsupported configuration type: ' . gettype($type));
}
}
begin
和 end
这一对的实现
主要在 ActionForm
组件中有良好的体现,其具体实现如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25# begin
public static function begin($config = [])
{
$config['class'] = get_called_class();
$widget = Yii::createObject($config);
static::$stack[] = $widget; // 利用栈的后进先出的特性,实现嵌套
return $widget;
}
# end
public static function end()
{
if (!empty(static::$stack)) {
$widget = array_pop(static::$stack); // 栈的特性,后进先出,最后 begin 的先 end
if (get_class($widget) === get_called_class()) {
echo $widget->run(); // 调用定义的 run 方法
return $widget;
} else {
throw new InvalidCallException('Expecting end() of ' . get_class($widget) . ', found ' . get_called_class());
}
} else {
throw new InvalidCallException('Unexpected ' . get_called_class() . '::end() call. A matching begin() is not found.');
}
}
看了上面,就可以自己对组件进行封装,便于开发中重复使用。
©版权声明:原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 & 作者信息。
End