最近更新: 2013-01-31

CommonGateway 初步

CommonGateway (以下簡稱 CG) 概念請參閱「CommonGateway 介紹」。源碼託管於 github : common-gateway ,主要程式碼只有一個 index.php 。或者參考下列命令列操作下載。但建議使用 git 管理。

wget https://raw.githubusercontent.com/shirock/rocksources/master/web/common-gateway/index.php

CG (也就是那份 index.php) 放在網頁根目錄下,然後以瀏覽器開啟網頁。本文範例的網頁根目錄是 /home/rock/web/cg ,瀏覽器開啟的 URL 是 http://localhost/cg/index.php 。開啟後,會看到下列的提示內容:

index.php/{control_name}/{object_id}.

You may put your controller class in controllers/{class_name}.php.

看到提示了嗎?根據提示,建立 controllers 子目錄,再於 controllers 子目錄下建立 book.php ,用以撰寫本文示範用的 Book 控制項。 controllers/book.php 最初內容如下:

<?php
class Book {
}
?>

再用瀏覽器開啟 URL http://localhost/cg/index.php/book (以下提到 URL 時,會省略 index.php 前的部份),CG 就會根據 URL 後面的路徑 "book" 去找 book.php 並執行它。因為我還沒在 Book 類別中實作任何東西,所以 CG 會顯示下列內容:

501 This service does not implement get or index method.

以瀏覽器直接開啟 URL 時,預設的請求方法(REQUEST_METHOD)就是 GET ,所以 CG 會去調用 get()index() 方法。而 get()index() 的差異表現在語意上。 index() 用於列出 Book 記錄的索引列表,所以它不需給予參數。而 get() 至少要一個參數指示取回哪一筆 Book 記錄。

我先寫 index()。就簡單列出兩筆書名記錄吧。

<?php
class Book {
    function index() {
        // $books = $query->from('book')->select();
        $index_model = array(
            'books' => array(
                new BookModel('123', 'book 123'),
                new BookModel('456', 'Book XYZ')
            ),
            'time'  => date('Y-m-d H:i:s')
        );
        return $index_model;
    }
}

class BookModel {
    var $isbn;
    var $title;
    function __construct($isbn, $title) {
        $this->isbn = $isbn;
        $this->title = $title;
    }
}

?>

再次用瀏覽器讀取 index.php/book ,會看到下列內容:

Template is missing. Missing views/book/index.phtml.

根據提示,建立子目錄 views/book 。這裡將放置 Book 控制項所需要的視圖(View)。繼續建立視圖文件 index.phtml ,內容如下:

<html>
<ul>
    <?php foreach ($books as $book): ?>
    <li><?=$book->isbn?> : <?=$book->title?>
    </li>
    <?php endforeach ?>
</ul>
<p>
Index date: <?=$time?>
</p>
</html>

我的 index() 回傳了一個陣列 $index_modelCG 就會把它的內容展開變成視圖 index.phtml 中的區域變數內容,變數名稱就是陣列的鍵。所以在 index.phtml 會出現 $books$time 兩個變數。然後用 PHP 的樣板語法印出 $books$time 的內容。

再次讀取 idnex.php/book ,終於出現能看的內容了。


    123 : book 123
    456 : Book XYZ

Index date: 2013-01-30 06:33:05

繼續實作 get() 方法,它需要一個 ISBN 參數以便查詢。這個參數內容會由 CG 根據 URL 路徑第二節的內容指派過來,我的 Book 不需要處理這件事。

class Book {
    function index() {
        // 省略...
    }

    var $book;
    function get($isbn) {
        /*
        $book = $query->from('book')->
                    ->where(array('isbn' => $isbn))
                    ->select();
        */
        if ($isbn != '123') {
            HttpResponse::exception(HttpResponse::NOT_FOUND);
        }
        $this->book = new BookModel('123', 'book 123');
        return;
    }
}

建立 GET 對應的 get.phtml 視圖。由於 get() 方法沒有回傳值,所以 CG 會直接將 Book 當成資料模型(Model),將它的公開屬性成員($this->book)變成視圖 get.phtml 內的區域變數 $book

<html>
<h2><?=$book->title?></h2>
<p>ISBN of this book: <?=$book->isbn?>
</p>
</html>

用瀏覽器開啟 index.php/book/123 ,會出現下列內容:

book 123

ISBN of this book: 123

到此目前,我已經建立了一個非常基本的 Book 查詢功能的 Web 服務了。

問答時間

本問答中沒提到的內容,可能在「CommonGateway 介紹」就說明了。也請參考。

等等,我的控制項的父類別是什麼?

使用過其他框架的使用者,應該會注意到本文的控制項類別 Book 沒有繼承任何父類別。

大部份框架確實需要使用者實作的控制項繼承它們指定的控制項父類別。但 CG 不需要。 CG 有很多把資源塞給控制項的途徑,所以你的控制項不需要繼承任何類別。 CG 把類別繼承關係的決定權還給你,

我一定要實作 index 方法嗎?

不需要。如果你的控制項原本就不打算提供索引服務,那就不需要實作 index 方法。 GET, POST, PUT, DELETE 或其它方法也一樣,如果你不需要,你就不必留個位子。

當客戶端請求的方法未實作時,CG 會按 HTTP 協定的標準作法,回應 501 Not implemented 訊息給客戶端。

我不用載入視圖與指派視圖的資料嗎?

CG 會根據使用方法自動載入對應的視圖(View)。

你只要在 views 子目錄建立符合預期名稱的目錄與視圖文件即可;也就是相同控制項名稱的子目錄,以及相同方法名稱的視圖文件。例如 CG 調用 Book 控制項的 get() 方法之後,就會自動載入 views/book/get.phtml 視圖。

視圖也是一份 PHP 文件,所以它的副檔名首字冠上 'p' 以便和一般文件區隔。而副檔名後的內容則表示回傳給客戶端的視圖的文件型態。瀏覽器預設要求的文件型態是 html ,所以本文建立的視圖副檔名都是 .phtml 。稍後的文章會說明如何回傳像 JSON, XML 的文件給客戶端。

最後,從 CG 的立場來看,它與控制項的關係是它調用了控制項的方法,所以控制項方法應該會把結果回傳給它。 因此 CG 會自己從控制項方法的回傳內容決定最後要呈現給客戶端的資料是什麼,並將資料塞到視圖中。故你也不需要指派視圖的資料。

這裡說明了控制項和視圖的處理策略,但好像沒有規劃資料模型?

因為我設計 CG 時,主要搭配資料庫處理函數 schema-database 使用。 而 schema-database 提供一個泛用型資料類別,可以映射資料庫的表格欄位成為資料模型(model)的屬性,也就不需要特地為了資料庫表格而建立一個關聯的資料模型類。 因此 CG 沒有設計資料模型的處理策略。

本文範例中的 BookModel 只是示範用。如果實際搭配 schema-database ,我不用定義 BookModel 類別。

相關文章
樂多舊網址: http://blog.roodo.com/rocksaying/archives/21320836.html