web页面的整体布局,大体相似的时候,可以做成layout。有时候页面上的一些东西,并不是所有的页面上都有,但是根据具体页面的上下环境的不同,而略有区别。例如菜单,导航等等。我们可以通过YII提供的widget来实现此功能。
官方的解释是:
E文版
A widget is an instance of CWidget or a child class of CWidget. It is a component that is mainly for presentational purposes. A widget is usually embedded in a view script to generate a complex, yet self-contained user interface. For example, a calendar widget can be used to render a complex calendar user interface. Widgets facilitate better reusability in user interface code.
中文版
小物件是 CWidget 或其子类的实例.它是一个主要用于表现数据的组件.小物件通常内嵌于一个视图来产生一些复杂而独立的用户界面.例如,一个日历小物件可用于渲染一个复杂的日历界面.小物件使用户界面更加可复用.
1.widget的在view中的使用方法。
下面以框架自带的,比较常用的wiget来举例说明。
/yii_dev/testwebap/protected/views/layouts/main.php中有:
<div id="mainmenu">
<?php $this->widget('zii.widgets.CMenu',array(
'items'=>array(
array('label'=>'Home', 'url'=>array('/site/index')),
array('label'=>'About', 'url'=>array('/site/page', 'view'=>'about')),
array('label'=>'Contact', 'url'=>array('/site/contact')),
array('label'=>'Login', 'url'=>array('/site/login'), 'visible'=>Yii::app()->user->isGuest),
array('label'=>'Logout ('.Yii::app()->user->name.')', 'url'=>array('/site/logout'), 'visible'=>!Yii::app()->user->isGuest)
),
)); ?>
</div><!-- mainmenu -->
<?php if(isset($this->breadcrumbs)):?>
<?php $this->widget('zii.widgets.CBreadcrumbs', array(
'links'=>$this->breadcrumbs,
)); ?><!-- breadcrumbs -->
<?php endif?>
可以找到这些框架定义的widget原型看看。存放位置在/yii_dev/yii/framework/zii/widgets目录下面
例如Cmenu
<?php
/**
* CMenu class file.
*
* @author Jonah Turnquist <poppitypop@gmail.com>
* @author Qiang Xue <qiang.xue@gmail.com>
* @link http://www.yiiframework.com/
* @copyright Copyright © 2008-2011 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
/**
* CMenu displays a multi-level menu using nested HTML lists.
*
* The main property of CMenu is {@link items}, which specifies the possible items in the menu.
* A menu item has three main properties: visible, active and items. The "visible" property
* specifies whether the menu item is currently visible. The "active" property specifies whether
* the menu item is currently selected. And the "items" property specifies the child menu items.
*
* The following example shows how to use CMenu:
* <pre>
* $this->widget('zii.widgets.CMenu', array(
* 'items'=>array(
* // Important: you need to specify url as 'controller/action',
* // not just as 'controller' even if default acion is used.
* array('label'=>'Home', 'url'=>array('site/index')),
* array('label'=>'Products', 'url'=>array('product/index'), 'items'=>array(
* array('label'=>'New Arrivals', 'url'=>array('product/new', 'tag'=>'new')),
* array('label'=>'Most Popular', 'url'=>array('product/index', 'tag'=>'popular')),
* )),
* array('label'=>'Login', 'url'=>array('site/login'), 'visible'=>Yii::app()->user->isGuest),
* ),
* ));
* </pre>
*
*
* @author Jonah Turnquist <poppitypop@gmail.com>
* @author Qiang Xue <qiang.xue@gmail.com>
* @version $Id: CMenu.php 3204 2011-05-05 21:36:32Z alexander.makarow $
* @package zii.widgets
* @since 1.1
*/
class CMenu extends CWidget
{
/**
* @var array list of menu items. Each menu item is specified as an array of name-value pairs.
* Possible option names include the following:
* <ul>
* <li>label: string, optional, specifies the menu item label. When {@link encodeLabel} is true, the label
* will be HTML-encoded. If the label is not specified, it defaults to an empty string.</li>
* <li>url: string or array, optional, specifies the URL of the menu item. It is passed to {@link CHtml::normalizeUrl}
* to generate a valid URL. If this is not set, the menu item will be rendered as a span text.</li>
* <li>visible: boolean, optional, whether this menu item is visible. Defaults to true.
* This can be used to control the visibility of menu items based on user permissions.</li>
* <li>items: array, optional, specifies the sub-menu items. Its format is the same as the parent items.</li>
* <li>active: boolean, optional, whether this menu item is in active state (currently selected).
* If a menu item is active and {@link activeClass} is not empty, its CSS class will be appended with {@link activeClass}.
* If this option is not set, the menu item will be set active automatically when the current request
* is triggered by {@link url}. Note that the GET parameters not specified in the 'url' option will be ignored.</li>
* <li>template: string, optional, the template used to render this menu item.
* When this option is set, it will override the global setting {@link itemTemplate}.
* Please see {@link itemTemplate} for more details. This option has been available since version 1.1.1.</li>
* <li>linkOptions: array, optional, additional HTML attributes to be rendered for the link or span tag of the menu item.</li>
* <li>itemOptions: array, optional, additional HTML attributes to be rendered for the container tag of the menu item.</li>
* <li>submenuOptions: array, optional, additional HTML attributes to be rendered for the container of the submenu if this menu item has one.
* When this option is set, the {@link submenuHtmlOptions} property will be ignored for this particular submenu.
* This option has been available since version 1.1.6.</li>
* </ul>
*/
public $items=array();
/**
* @var string the template used to render an individual menu item. In this template,
* the token "{menu}" will be replaced with the corresponding menu link or text.
* If this property is not set, each menu will be rendered without any decoration.
* This property will be overridden by the 'template' option set in individual menu items via {@items}.
* @since 1.1.1
*/
public $itemTemplate;
/**
* @var boolean whether the labels for menu items should be HTML-encoded. Defaults to true.
*/
public $encodeLabel=true;
/**
* @var string the CSS class to be appended to the active menu item. Defaults to 'active'.
* If empty, the CSS class of menu items will not be changed.
*/
public $activeCssClass='active';
/**
* @var boolean whether to automatically activate items according to whether their route setting
* matches the currently requested route. Defaults to true.
* @since 1.1.3
*/
public $activateItems=true;
/**
* @var boolean whether to activate parent menu items when one of the corresponding child menu items is active.
* The activated parent menu items will also have its CSS classes appended with {@link activeCssClass}.
* Defaults to false.
*/
public $activateParents=false;
/**
* @var boolean whether to hide empty menu items. An empty menu item is one whose 'url' option is not
* set and which doesn't contain visible child menu items. Defaults to true.
*/
public $hideEmptyItems=true;
/**
* @var array HTML attributes for the menu's root container tag
*/
public $htmlOptions=array();
/**
* @var array HTML attributes for the submenu's container tag.
*/
public $submenuHtmlOptions=array();
/**
* @var string the HTML element name that will be used to wrap the label of all menu links.
* For example, if this property is set as 'span', a menu item may be rendered as
* <li><a href="url"><span>label</span></a></li>
* This is useful when implementing menu items using the sliding window technique.
* Defaults to null, meaning no wrapper tag will be generated.
* @since 1.1.4
*/
public $linkLabelWrapper;
/**
* @var string the CSS class that will be assigned to the first item in the main menu or each submenu.
* Defaults to null, meaning no such CSS class will be assigned.
* @since 1.1.4
*/
public $firstItemCssClass;
/**
* @var string the CSS class that will be assigned to the last item in the main menu or each submenu.
* Defaults to null, meaning no such CSS class will be assigned.
* @since 1.1.4
*/
public $lastItemCssClass;
/**
* Initializes the menu widget.
* This method mainly normalizes the {@link items} property.
* If this method is overridden, make sure the parent implementation is invoked.
*/
public function init()
{
$this->htmlOptions['id']=$this->getId();
$route=$this->getController()->getRoute();
$this->items=$this->normalizeItems($this->items,$route,$hasActiveChild);
}
/**
* Calls {@link renderMenu} to render the menu.
*/
public function run()
{
$this->renderMenu($this->items);
}
/**
* Renders the menu items.
* @param array $items menu items. Each menu item will be an array with at least two elements: 'label' and 'active'.
* It may have three other optional elements: 'items', 'linkOptions' and 'itemOptions'.
*/
protected function renderMenu($items)
{
if(count($items))
{
echo CHtml::openTag('ul',$this->htmlOptions)."\n";
$this->renderMenuRecursive($items);
echo CHtml::closeTag('ul');
}
}
/**
* Recursively renders the menu items.
* @param array $items the menu items to be rendered recursively
*/
protected function renderMenuRecursive($items)
{
$count=0;
$n=count($items);
foreach($items as $item)
{
$count++;
$options=isset($item['itemOptions']) ? $item['itemOptions'] : array();
$class=array();
if($item['active'] && $this->activeCssClass!='')
$class[]=$this->activeCssClass;
if($count===1 && $this->firstItemCssClass!='')
$class[]=$this->firstItemCssClass;
if($count===$n && $this->lastItemCssClass!='')
$class[]=$this->lastItemCssClass;
if($class!==array())
{
if(empty($options['class']))
$options['class']=implode(' ',$class);
else
$options['class'].=' '.implode(' ',$class);
}
echo CHtml::openTag('li', $options);
$menu=$this->renderMenuItem($item);
if(isset($this->itemTemplate) || isset($item['template']))
{
$template=isset($item['template']) ? $item['template'] : $this->itemTemplate;
echo strtr($template,array('{menu}'=>$menu));
}
else
echo $menu;
if(isset($item['items']) && count($item['items']))
{
echo "\n".CHtml::openTag('ul',isset($item['submenuOptions']) ? $item['submenuOptions'] : $this->submenuHtmlOptions)."\n";
$this->renderMenuRecursive($item['items']);
echo CHtml::closeTag('ul')."\n";
}
echo CHtml::closeTag('li')."\n";
}
}
/**
* Renders the content of a menu item.
* Note that the container and the sub-menus are not rendered here.
* @param array $item the menu item to be rendered. Please see {@link items} on what data might be in the item.
* @return string
* @since 1.1.6
*/
protected function renderMenuItem($item)
{
if(isset($item['url']))
{
$label=$this->linkLabelWrapper===null ? $item['label'] : '<'.$this->linkLabelWrapper.'>'.$item['label'].'</'.$this->linkLabelWrapper.'>';
return CHtml::link($label,$item['url'],isset($item['linkOptions']) ? $item['linkOptions'] : array());
}
else
return CHtml::tag('span',isset($item['linkOptions']) ? $item['linkOptions'] : array(), $item['label']);
}
/**
* Normalizes the {@link items} property so that the 'active' state is properly identified for every menu item.
* @param array $items the items to be normalized.
* @param string $route the route of the current request.
* @param boolean $active whether there is an active child menu item.
* @return array the normalized menu items
*/
protected function normalizeItems($items,$route,&$active)
{
foreach($items as $i=>$item)
{
if(isset($item['visible']) && !$item['visible'])
{
unset($items[$i]);
continue;
}
if(!isset($item['label']))
$item['label']='';
if($this->encodeLabel)
$items[$i]['label']=CHtml::encode($item['label']);
$hasActiveChild=false;
if(isset($item['items']))
{
$items[$i]['items']=$this->normalizeItems($item['items'],$route,$hasActiveChild);
if(empty($items[$i]['items']) && $this->hideEmptyItems)
unset($items[$i]['items']);
}
if(!isset($item['active']))
{
if($this->activateParents && $hasActiveChild || $this->activateItems && $this->isItemActive($item,$route))
$active=$items[$i]['active']=true;
else
$items[$i]['active']=false;
}
else if($item['active'])
$active=true;
}
return array_values($items);
}
/**
* Checks whether a menu item is active.
* This is done by checking if the currently requested URL is generated by the 'url' option
* of the menu item. Note that the GET parameters not specified in the 'url' option will be ignored.
* @param array $item the menu item to be checked
* @param string $route the route of the current request
* @return boolean whether the menu item is active
*/
protected function isItemActive($item,$route)
{
if(isset($item['url']) && is_array($item['url']) && !strcasecmp(trim($item['url'][0],'/'),$route))
{
if(count($item['url'])>1)
{
foreach(array_splice($item['url'],1) as $name=>$value)
{
if(!isset($_GET[$name]) || $_GET[$name]!=$value)
return false;
}
}
return true;
}
return false;
}
}
类的注释中给出了,此widget的功能和使用方法。
CMenu displays a multi-level menu using nested HTML lists.
*
* The main property of CMenu is {@link items}, which specifies the possible items in the menu.
* A menu item has three main properties: visible, active and items. The "visible" property
* specifies whether the menu item is currently visible. The "active" property specifies whether
* the menu item is currently selected. And the "items" property specifies the child menu items.
*
* The following example shows how to use CMenu:
* <pre>
* $this->widget('zii.widgets.CMenu', array(
* 'items'=>array(
* // Important: you need to specify url as 'controller/action',
* // not just as 'controller' even if default acion is used.
* array('label'=>'Home', 'url'=>array('site/index')),
* array('label'=>'Products', 'url'=>array('product/index'), 'items'=>array(
* array('label'=>'New Arrivals', 'url'=>array('product/new', 'tag'=>'new')),
* array('label'=>'Most Popular', 'url'=>array('product/index', 'tag'=>'popular')),
* )),
* array('label'=>'Login', 'url'=>array('site/login'), 'visible'=>Yii::app()->user->isGuest),
* ),
* ));
* </pre>
/yii_dev/yii/framework/zii/widgets/CBreadcrumbs.php
<?php
/**
* CBreadcrumbs class file.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @link http://www.yiiframework.com/
* @copyright Copyright © 2008-2011 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
/**
* CBreadcrumbs displays a list of links indicating the position of the current page in the whole website.
*
* For example, breadcrumbs like "Home > Sample Post > Edit" means the user is viewing an edit page
* for the "Sample Post". He can click on "Sample Post" to view that page, or he can click on "Home"
* to return to the homepage.
*
* To use CBreadcrumbs, one usually needs to configure its {@link links} property, which specifies
* the links to be displayed. For example,
*
* <pre>
* $this->widget('zii.widgets.CBreadcrumbs', array(
* 'links'=>array(
* 'Sample post'=>array('post/view', 'id'=>12),
* 'Edit',
* ),
* ));
* </pre>
*
* Because breadcrumbs usually appears in nearly every page of a website, the widget is better to be placed
* in a layout view. One can define a property "breadcrumbs" in the base controller class and assign it to the widget
* in the layout, like the following:
*
* <pre>
* $this->widget('zii.widgets.CBreadcrumbs', array(
* 'links'=>$this->breadcrumbs,
* ));
* </pre>
*
* Then, in each view script, one only needs to assign the "breadcrumbs" property as needed.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @version $Id: CBreadcrumbs.php 2799 2011-01-01 19:31:13Z qiang.xue $
* @package zii.widgets
* @since 1.1
*/
class CBreadcrumbs extends CWidget
{
/**
* @var string the tag name for the breadcrumbs container tag. Defaults to 'div'.
*/
public $tagName='div';
/**
* @var array the HTML attributes for the breadcrumbs container tag.
*/
public $htmlOptions=array('class'=>'breadcrumbs');
/**
* @var boolean whether to HTML encode the link labels. Defaults to true.
*/
public $encodeLabel=true;
/**
* @var string the first hyperlink in the breadcrumbs (called home link).
* If this property is not set, it defaults to a link pointing to {@link CWebApplication::homeUrl} with label 'Home'.
* If this property is false, the home link will not be rendered.
*/
public $homeLink;
/**
* @var array list of hyperlinks to appear in the breadcrumbs. If this property is empty,
* the widget will not render anything. Each key-value pair in the array
* will be used to generate a hyperlink by calling CHtml::link(key, value). For this reason, the key
* refers to the label of the link while the value can be a string or an array (used to
* create a URL). For more details, please refer to {@link CHtml::link}.
* If an element's key is an integer, it means the element will be rendered as a label only (meaning the current page).
*
* The following example will generate breadcrumbs as "Home > Sample post > Edit", where "Home" points to the homepage,
* "Sample post" points to the "index.php?r=post/view&id=12" page, and "Edit" is a label. Note that the "Home" link
* is specified via {@link homeLink} separately.
*
* <pre>
* array(
* 'Sample post'=>array('post/view', 'id'=>12),
* 'Edit',
* )
* </pre>
*/
public $links=array();
/**
* @var string the separator between links in the breadcrumbs. Defaults to ' » '.
*/
public $separator=' » ';
/**
* Renders the content of the portlet.
*/
public function run()
{
if(empty($this->links))
return;
echo CHtml::openTag($this->tagName,$this->htmlOptions)."\n";
$links=array();
if($this->homeLink===null)
$links[]=CHtml::link(Yii::t('zii','Home'),Yii::app()->homeUrl);
else if($this->homeLink!==false)
$links[]=$this->homeLink;
foreach($this->links as $label=>$url)
{
if(is_string($label) || is_array($url))
$links[]=CHtml::link($this->encodeLabel ? CHtml::encode($label) : $label, $url);
else
$links[]='<span>'.($this->encodeLabel ? CHtml::encode($url) : $url).'</span>';
}
echo implode($this->separator,$links);
echo CHtml::closeTag($this->tagName);
}
}
文件见下面有很多,可以自己找找试用一下。
2.自定义你的widget
继承 CWidget 并覆盖其init() 和 run() 方法,可以定义一个新的小物件:
class MyWidget extends CWidget { public function init() { // 此方法会被 CController::beginWidget() 调用 } public function run() { // 此方法会被 CController::endWidget() 调用 } }
小物件可以像一个控制器一样拥有它自己的视图.默认情况下,小物件的视图文件位于包含了小物件类文件目录的views
子目录之下.这些视图可以通过调用 CWidget::render() 渲染,这一点和控制器很相似.唯一不同的是,小物件的视图没有布局文件支持。另外,小物件视图中的$this
指向小物件实例而不是控制器实例。
例如定义一个显示:当期日期是:日期格式。根据你指定的日期格式来显示日期。
<?php $this->beginWidget('zii.widgets.CShowDate',array(
'format'=>'Y-m-d H:i:s',
)); ?>
当前日期是:
<?php $this->endWidget(); ?>
/yii_dev/yii/framework/zii/widgets/CShowDate.php
<?php
/**
* <pre>
<?php $this->beginWidget('zii.widgets.CShowDate',array(
'format'=>'Y-m-d H:i:s',
)); ?>
当前日期是:
<?php $this->endWidget(); ?>
* </pre>
*/
class CShowDate extends CWidget
{
public $format='Y-m-d H:i:s';
/**
* Initializes the widget.
* This renders the open tags needed by the portlet.
* It also renders the decoration, if any.
*/
public function init()
{
}
/**
* Renders the content of the portlet.
*/
public function run()
{
echo date($this->format,mktime());
}
}
主要array()中的key必须是在widget class中定义的成员变量。不然将无法解析。
是不是你有很多widget等着定义,赶紧动手写自己的widget吧!