之前講了怎麼讓實現跳轉和請求的轉寄,當然,也只是很簡單的說了一下,更深的內容需要你自己去讀一下具體架構的實現。
現在跳轉和轉寄有了,對於模型的編寫可以後面再來,那我就先說一下怎麼講資料從控制器傳遞到視圖,之前我們的實現方式非常醜陋:
1 |
$viewPath = dirname(__FILE__) . '/../views/index.php'; |
2 |
if(file_exists($viewPath)) { |
5 |
echo 'view does not exists'; |
現在我準備用更優雅一點的方式將資料傳遞到視圖,現在先說一下怎麼調用:
02 |
class TestController extends Controller { |
03 |
public function test() { |
08 |
'str' => 'it is a str' |
10 |
$this->_display('test'); |
這就是今天我寫之後的調用方式,如果你學過smarty,那麼你會發現這和smarty很相似,的確,我寫toper的時候也是受了smarty的影響,最開始的想法是視圖這一塊就使用smarty,後面想了一下,既然號稱是自己的架構,那麼視圖這一塊兒肯定也要全部自己寫,雖然如此,但是我後面還是保留了assign,display這種寫法,並且只要設定檔修改,完全可以將視圖切換到smarty而不使用架構本身的實現。
好像扯得有點遠了,首先說一下,為了代碼的簡單,我將assign和display的實現簡化,大家理解思路就好,真實的架構的實現要複雜一些。
首先assign,它的功能是為變數賦值,這裡我假設傳遞的參數都是關聯陣列,也就是常說的HashTable,它不應該直接調用View的介面,這介面實現怎麼賦值:
1 |
protected function _assign(Array $arr) { |
為了處理視圖這一塊的資料存放區和展示,我定義了View.php這個檔案,View::assign這個方法是它的一個靜態方法,當然,實現的功能大家都懂的。。。
具體View.php中怎麼實現的呢?
1 |
private static $_data = array(); |
2 |
public static function assign($arr) { |
3 |
foreach($arr as $key => $val) { |
5 |
//過濾掉如array('test','test2')這種資料 |
6 |
self::$_data[$key] = $val; |
由於assign是靜態方法,資料又需要儲存到這個類,所以需要定義一個靜態成員變數$_data,這個變數儲存控制器傳遞的資料。因為之前在控制器的assign中已經進行了類型提示,所以可以保證在View中的assign傳遞的形參都是數組,現在只需要一個foreach然後依次儲存即可,那為什麼不直接使用self::$_data = $arr呢,這樣只有一句話呢?
首先,數組中的資料有些可能是不對的,比如array('test','test2')我就不懂資料test和test2到底代表了什麼,這樣的資料應該在assign中就被剔除掉,如果你要求比較嚴苛,也可以直接給使用者提示警告。
其次,有可能使用者多次調用assign,如果直接使用引用,那麼第二次調用assign就會把第一次的資料弄丟,這樣是不可容忍的。
好,賦值搞定,然後就是怎麼顯示的問題了,在控制器中,還是直接調用介面,而不負責內部實現,但是在調用介面之前需要進行一定的格式化:
01 |
protected function _display($str) { |
03 |
$str = str_replace(array( |
08 |
View::display(MODULES_PATH . View::VIEW_BASE_PATH . $str . '.php'); |
為了視覺上的美觀和其他一些XXX的原因,我們可以將表示路徑的/在作為實參傳遞的時候變成.,這樣假設要調用test/test2.php,那麼只需要傳遞test.test2.php即可。當然,這樣也存在一個問題,有時候需要使用.,就像剛才這個例子,實際上test.test2.php會被解析為test/test2/php,這樣實際上是有問題的,那麼怎麼解決呢,我使用一個#代表.,這樣剛才這個傳遞的時候就變成了test.test2#php。這樣還有一個問題,.php基本上每個頁面都有,那為什麼還要傳遞進去呢,直接架構幫你加上就好了嘛,所以使用者只需要輸入test.test2就好,這樣從邏輯上也容易理解,test模組下面的test2這個視圖檔案。如果你用過thinkphp,你會發現這和它的寫法很類似,實際上我在最開始寫的時候就是邊看thinkphp源碼邊寫的,所以很多東西都借鑒了它的思想。我個人比較討厭使用#,所以我基本上在視圖檔案中沒有使用過#,因為我覺得你自己不會這麼無聊,去寫類似於test.view.php這樣的無意義的檔案名稱,直接test.php就好了,這樣的名字架構又幫你解決了一部分,所以基本上不存在這個問題了。
這兒還出現了一個常量View::VIEW_BASE_PATH,這個常量代表的含義是視圖根目錄的路徑,這樣傳遞進去的實際上是一個實際的絕對路徑。
由於display是展示這個視圖檔案,那麼肯定會使用include某一個檔案,具體的實現如下:
1 |
public static function display($file) { |
2 |
if(file_exists($file)) { |
6 |
throw new ViewException(ViewException::NOT_EXISTS_TEMPLATE); |
extract就是講數組打散,更多的請查詢PHP手冊。
這裡由於可能這個視圖檔案不存在,所以需要判定一下,如果視圖檔案不存在,則直接拋出異常,注意,異常是使用了ViewException,這個類又是新定義的,傳遞的參數表示這個異常是因為視圖模板不存在而引起的。
那我麼來看看具體這個異常類的實現:
02 |
class ViewException extends BaseException { |
03 |
const NOT_EXISTS_TEMPLATE = 1; |
04 |
public function __construct($code = 0) { |
06 |
case ViewException::NOT_EXISTS_TEMPLATE: |
07 |
$msg = 'the template file is not exists'; |
10 |
$msg = 'unknown exception'; |
13 |
parent::__construct($msg,$code); |
這個類它繼承了BaseException,由於BaseException實現了debug模式開與關不同情況下展示內容不同,ViewException也具有這個特性,當debug關閉,拋出異常的時候,也只會跳轉到錯誤頁,不會直接展示異常,這樣的處理更易維護。