The Model
‘M’或MVC系統的mdel部份用來查詢資料庫(或是其他的外部資源)並且提供資料給控制器,我們可以依據請求來載入適當的model,但我傾向model跟controller的這幾行模糊起來,所以控制器會使用DB抽象函式庫來直接查詢DB,而不使用個別的模式,這是依照個人的喜好,所以你也可以不這樣做。
我們必須做的一件事就是新增需要的程式碼來設定資料庫的連結,並且把它新增到我們的index首頁,有很多可用的DB抽象函式庫(包括我自己的AutoCRUD)但是PHP5已經有一個很棒的DB函式庫- PDO -所以不需要一個不同的函式庫。
將下面的程式碼放入index檔案中(在含括起始檔後):
$registry->set (‘db’, $db);
在上面的例子中,我們建構PDO函式庫一個新的實體,然後連結到我們的MySQL資料庫,然後我們使用Registry類別來製作$db全域資料。
現在我們很高興我們系統的model部份已經完成,讓我們繼續下個步驟:撰寫控制器。
撰寫控制器意味著我們必須寫一個 Router類別, Router類別依照請求(透過URL來傳遞$router 變數)來載入目前的控制器,讓我們先寫這個Router類別。
Router類別
我們的Router類別必須解析請求,然後載入目前的方法,第一個步驟就是建構Router類別的骨架:
private $registry;
private $path;
private $args = array();
function __construct($registry) {
$this->registry = $registry;
}
}
?>
然後將下面這幾行加到index.php中:
$registry->set (‘router’, $router);
現在我們已經將Router類別加到我們的MVC系統,但是現在它還是沒有做什麼事,所以讓我們再加些需要的方法到Router類別中。
我們第一個想要增加的方法是setPath()方法,這是用來設定我們掌握我們所有控制器的目錄setPath()方法看起來像這樣,而且需要加到Router類別中:
$path = trim($path, ‘/‘);$path .= DIRSEP;
if (is_dir($path) == false) {
throw new Exception (‘Invalid controller path: `’ . $path . ‘`’);
}
$this->path = $path;
}
然後將下一行加到index.php中:
現在我們已經設定了控制器的路徑,我們可以寫真正用來載入正確控制器的方法,這方法是elegate(),它會解析請求,這方法的第一部份看起來像這樣:
$this->getController($file, $controller, $action, $args);
就像你看到的,它使用了另一個方法getController() 來取得控制器名稱以及一些其他變數,這方法看起來像這樣:
if (empty($route)) { $route = ‘index’; }
// Get separate parts
$route = trim($route, ‘/‘);
$parts = explode(‘/’, $route);
// Find right controller
$cmd_path = $this->path;
foreach ($parts as $part) {
$fullpath = $cmd_path . $part;
// Is there a dir with this path?
if (is_dir($fullpath)) {
$cmd_path .= $part . DIRSEP;
array_shift($parts);
continue;
}
// Find the file
if (is_file($fullpath . ‘.php’)) {
$controller = $part;
array_shift($parts);
break;
}
}
if (empty($controller)) { $controller = ‘index’; };
// Get action
$action = array_shift($parts);
if (empty($action)) { $action = ‘index’; }
$file = $cmd_path . $controller . ‘.php’;
$args = $parts;
}
讓我們看一下這方法,首先它先取得$route查詢字串變數的值,然後使用explode()函式將它分成幾個部份,假如請求是’members/view’它就會分成array(‘members’, ‘view’)。
然後我們使用foreach迴圈來運作每一部份,首先確認這個部份是否是目錄,假如是就將它加入到filepath然後確認下一個部份,這樣可以允許我們將控制器放到子目錄,然後使用層級的控制器,假如這個部份不是目錄而是檔案,我們就將其存到$controller變數,因為我們找到了我們要的控制器我們就離開迴圈。
在迴圈之後我們首先是否有找到控制器,假如沒有我們就使用預設的’index’,接下來我們需要進行action的取得,控制器是一個含有不同方法的類別,而action會指到某一個方法,假如沒有指定action我們會使用預設的’index’。
最後我們會取得控制器連續的完整路徑、名稱及其擴充。
現在請求被解析就可以用delegate()方法來載入控制器及執行action,完整的delegate()方法如下所示:
$this->getController($file, $controller, $action, $args);
// File available?
if (is_readable($file) == false) {
die (‘404 Not Found’);
}
// Include the file
include ($file);
// Initiate the class
$class = ‘Controller_’ . $controller;
$controller = new $class($this->registry);
// Action available?
if (is_callable(array($controller, $action)) == false) {
die (‘404 Not Found’);
}
// Run action
$controller->$action();
}
在使用getController()方法解析請求後,我們先確認檔案是否真的存在,如果沒有我們會傳回一個簡單的錯誤訊息。
接下來我們要做的是含括控制器檔案,然後初始化類別,這個類別總是以Controller_[name]這樣的格式來命名,稍後我們會學習更多控制器相關的內容。
接下來我們使用 is_callable()函式來確認action是否存在而且可以執行,最後我們執行這個action,完成這個router的角色任務。
現在我們有一個可以完整運作的delegate()方法,我們新增下面這一行到index.php中:
假如你現在執行系統,而且尚未新增’controllers’ 目錄的話,你不是會得到下面的錯誤:
就是’404 Not Found’的錯誤,因為尚未有任何的控制器,但是我們接下來會馬上處理這個部份。