yii開發中,用到了大量的widget,咱們也能本身創建一個widget,今天爲你講解widget的運行機理,讓你遊刃有餘的操做它。php
ActiveForm、Breadcrumbs、DetailView、LinkPager、ListView 等等,這些內置的掛件讓咱們開發變的如此簡單。數組
本節教你自定義一個widget,順便說下widget的運行機理和幾大重要函數。瀏覽器
老規矩,先說目錄。app
何時用widgetyii
新建一個widget編輯器
begin & end 方法函數
beforeRun & afterRun 方法佈局
begin...this
場景仍是蠻簡單的、當咱們發現頁面有的區塊重複出現了,好比TOP十、好比內容編輯器等、就要考慮是否要作成一個widget了。spa
咱們仍是舉例把,新建一個Top10的區塊。首先咱們要在應用程序的components下新建一個Top10Widget的類,根據psr-4規則,佈局應該是這樣的
components
componentsTop10Widget.php
Top10Widget.php的內容
namespace app\components; use yii\base\Widget; class Top10Widget extends Widget { public $catId; public function init(){ parent::init(); if(empty($this->catId)){ $this->catId = 0; } } public function run(){ parent::run(); } }
做爲一個widget,你必需要繼承於yiibaseWidget才能夠,就像咱們的Top10Widget同樣。類的變量表明未來視圖使用該掛件時對其傳遞的參數,除此以外咱們要重載Widget的init和run兩個方法。
要注意的一點是,在方法裏須要先調用父類的對應方法,如上面代碼同樣,這個要記住。
init() 方法旨在對傳遞過來的變量進行驗證、篩選及處理,因此對於init而言,若是參數出現異常表明前臺並無傳遞參數正確,這個時候你可使用throw new Exception("xxx");來拋出異常,頁面會報錯,固然爲了美觀,咱們可使用try ... catch....將這個異常接住而後更友好的顯示。
小提示:對於throw異常的方法,在run中也同樣,我建議try .... catch ....它們。
偷偷的小廣告 北哥工兵連 每1-2天一篇乾貨 http://nai8.me
好的,上面說了init的初衷,接下來講下run函數(有些掛件並無使用init,而是將全部數據都放到run裏處理也是能夠的,不過我通常不這麼幹)。
run() 方法表明該關鍵的執行邏輯,針對於init處理過的變量,進行邏輯處理,最後顯示給客戶想要的東西。就和action同樣,這裏面能夠執行不少不少。
最後咱們能夠經過return 或 echo 將結果返回給瀏覽器
namespace app\components; use yii\base\Widget; class Top10Widget extends Widget { public $catId; // 省略的代碼.... public function run(){ parent::run(); return $this->catId; // 不徹底等價於 echo $this>-catId } }
那麼一個問題出現了,若是我僅僅是想像是一些數字結果,而是但願它的樣子更豐富,我要如何作那?
我想你也想到了,咱們能夠對這個掛件加載一個視圖,而後在視圖裏處理。
namespace app\components; use yii\base\Widget; class Top10Widget extends Widget { public $catId; // 省略的代碼.... public function run(){ parent::run(); return $this->render('top10',[ 'catId'=>$this->catId ]); } }
top10.php文件在哪創建?按照約定,咱們須要在appcomponentsviews下創建這個視圖。
use app\components\Top10Widget; <?= Top10Widget::widget(['catId'=>98]);?>
看到了吧,和控制的視圖同樣,先use,在使用。
到這裏我想你們經過yii的手冊都已經知道了,新建一個widget是如此簡單。
從上面咱們知道,咱們經過使用Top10Widget::widget()方法將參數傳遞給掛件並處理,可是有的時候可能數組不少,用傳參方法不太適合,那麼咱們要如何去作那?見下段。
咱們可使用begin和end方法將要傳遞的內容總體傳遞給掛件作處理。
以下視圖
<?php use app\components\Top10Widget; ?> <?php Top10Widget::begin();?> <b>hello widget</b> <?php Top10Widget::end();?>
在掛件裏,咱們以下處理
namespace app\components; use yii\base\Widget; class Top10Widget extends Widget { public $catId; public function init(){ parent::init(); ob_start(); } public function run(){ parent::run(); $content = ob_get_clean(); return "<div style='color:red'>{$content}</div>"; } }
經過 ob_start和ob_get_clean方法,咱們得到關鍵包圍的內容,更多關於緩衝區的內容能夠看php文檔 http://php.net/manual/zh/ref....
到此爲止,我想你對掛件的創建已經沒有任何懼怕了,它們如此簡單,各類狀況也都有對策。可是若是你是一個技術發燒友,你必定發現yiibaseWidget下還有兩個函數beforeRun和afterRun,彷佛他們在run函數先後進行了什麼?接下來給你說說它們倆。
若是用一句話歸納就是咱們能夠經過beforeRun的返回值是真是假來決定掛件是否執行run函數,而afterRun則能對run的處理結果再一次處理後返給瀏覽器,看下widget實現源碼就知道了
public static function widget($config = []) { ob_start(); ob_implicit_flush(false); try { /* @var $widget Widget */ $config['class'] = get_called_class(); $widget = Yii::createObject($config); $out = ''; if ($widget->beforeRun()) { $result = $widget->run(); $out = $widget->afterRun($result); } } catch (\Exception $e) { // close the output buffer opened above if it has not been closed already if (ob_get_level() > 0) { ob_end_clean(); } throw $e; } return ob_get_clean() . $out; }
你必定看到了這段代碼
if ($widget->beforeRun()) { $result = $widget->run(); $out = $widget->afterRun($result); }
聰明的你明白了,換句話說,咱們能夠在自定義widget時候經過重載beforeRun來進一步控制run是否處理,進而讓咱們的掛件更加靈活。
那麼回過頭來咱們思考,爲什麼要插入這樣一個beforeRun開關函數那,緣由在於init函數並無返回值,它控制不了run是否執行,而run函數的功能是接收數據進行數據處理,所以在它以前加一個函數來進行再一次判斷決定是否往下走是有必要的。
此篇講完,但願對你有用。