接上篇,為什麼index 模版裡沒有任何有關require,inculde型別關鍵字,卻任然有我們認為“多餘”的字元出現。因為在至少我的認知裡html的結構該是<html><head></head><body></body></html>這樣的,而index.php模版中存在這樣的結構,而如果那裡採用特殊技術的話會不會存在html標籤在包含html標籤,那就不是一個標準的html文件了。而我們檢視原始碼生成的html原始碼時也發現了該html原始碼內容為:
<div id="content"> <html> <head> </head> <body> <center>這是我自己的第一個頁面,雖然將原來的大段內容刪除了。不在是一個部落格</center> <a href="/admin/post/index/">指向admin/post/index.php的內容</a> </body> </html></div><!-- content --> <div class="clear"></div>
即現在生成的頁面是由一些其他頁面加上我們自己的index.php結合生成的!
但是,他是在那裡實現的呢?
先看我們的控制器:
class SiteController extends Controller { public function actions() { return array( // captcha action renders the CAPTCHA image displayed on the contact page 'captcha'=>array( 'class'=>'CCaptchaAction', 'backColor'=>0xFFFFFF, ), // page action renders "static" pages stored under 'protected/views/site/pages' // They can be accessed via: index.php?r=site/page&view=FileName 'page'=>array( 'class'=>'CViewAction', ), ); } /** * This is the default 'index' action that is invoked * when an action is not explicitly requested by users. */ public function actionIndex() { $this->render('index'); }
這裡的actionIndex就是我們訪問localhost/index.php要呼叫的方法。但是我們的除了自己編寫的那點程式碼外,其他的模版呢?
可以檢視父類,有沒有一種可能,在做控制器例項化或者其他操作時呼叫了那個其他模版的程式,然後,index.php的內容作為一個執行後的string引數傳入進去最終得到我們想要的頁面?
class Controller extends CController { /** * @var string the default layout for the controller view. Defaults to '//layouts/column1', * meaning using a single column layout. See 'protected/views/layouts/column1.php'. */ public $layout='//layouts/column1';
這裡可以注意到有$layout。
最終我們定位到layout資料夾,如下圖:
column1.php的內容有:
<?php $this->beginContent('//layouts/main'); ?> <div id="content"> <?php echo $content; ?> </div><!-- content --> <?php $this->endContent(); ?>
這裡已經有一些眉目了,可以看到<div id="content">了,因為我們知道id屬性在html中的單一頁面是唯一的,而我們剛才檢視html頁面也有一個<div id="content">,那麼肯定是在呼叫actionIndex這個控制器方法的時候會將返回的字串作為變數名為$content的變數賦值給另外一個作用模組,而這個作用模組主要是頁面佈局。由此,可以分析出protected/component/Controller.php的類我們也可以模擬編寫出來一個其他的頁面佈局,讓我們的邏輯控制器(如SiteController.php)來繼承,達到更換佈局,而展示內容不變的目的,此為推斷,後面我會給出示例。
由上面一些推斷,總結出yii的裝飾功能。
要完成只顯示我們自己的頁面。可以將column1.php檔案修改為
<?php //$this->beginContent('//layouts/main'); ?> <div id="content"> <?php echo $content; ?> </div><!-- content --> <?php //$this->endContent(); ?>
當然編碼問題也要注意,在index.php中加入<meta content="text/html;charset=utf-8" http-equiv="Content-Type"/>就會如願看到:
現在看$this->beginContent和$this->endContent()到底為我們做了什麼工作。
1. 首先需要明確這裡的$this指的是什麼,這個變數當然指的是控制器。
修改columns.php的內容為:
<?php //$this->beginContent('//layouts/main'); ?> <div id="content"> <?php print_r($this) ?> <?php echo $content; ?> </div><!-- content --> <?php //$this->endContent(); ?>
有圖為證:
也就是說,beginContent和endContent也是我們的SiteController的一個方法,檢視沒有,那麼一定是父類方法了。
2. 分析/frameworks/web/CBaseController.php的內容。
public function beginContent($view=null,$data=array()) { $this->beginWidget('CContentDecorator',array('view'=>$view, 'data'=>$data)); }
檢視到這個罪魁禍首了。追根溯源,通過一堆工廠實現類方法呼叫可以檢視到最終會彙集到/framework/web/CContentDecorator.php檔案。
CContentDecorator.php核心:
protected function decorate($content) { $owner=$this->getOwner(); if($this->view===null) $viewFile=Yii::app()->getController()->getLayoutFile(null); else $viewFile=$owner->getViewFile($this->view); if($viewFile!==false) { $data=$this->data; $data['content']=$content; return $owner->renderFile($viewFile,$data,true); } else return $content; }
即$this->beginContent("layout_path"),這裡可以看到一個關鍵點是'content',即為什麼我們要使用$content在模版中進行展示了。
那麼更重要的main.php中控制的資料該展示在何處?
原來的main.php中的內容:
<?php endif?> <?php echo $content; ?> <div class="clear"></div> <div id="footer"> Copyright © <?php echo date('Y'); ?> by My Company.<br/> All Rights Reserved.<br/> <?php echo Yii::powered(); ?> </div><!-- footer --> </div><!-- page --> </body> </html>
注意main.php中也存在這個$content
所以,將該內容移動到Copyright下,檢視頁面已經出現變化了。
即在main.php中也是通過$content來進行控制。
但是在這裡除了頁面的除錯佈局外,當我們想檢視某個類的某個方法是否存在時,有沒有更好的處理方法呢?下篇處理該問題吧。