控制器¶
控制器是你的應用程式的核心,它們決定了應該如何處理 HTTP 請求。
什麼是控制器?¶
控制器是一個簡單的類別檔案,它可以使用與 URI 有關的命名方式。
以這個 URI 為例:
example.com/index.php/helloworld/
在上面的例子中,CodeIgniter 會嘗試找到一個名為 Helloworld.php 的控制器並載入它。
當一個控制器的名稱與 URI 的第一個區段匹配時,它將會被加載。
讓我們試一下:Hello World!¶
讓我們創建一個簡單的控制器,這樣你就可以看到它的操作方式。使用你的文本編輯器,創建一個名為 Helloworld.php 的文件,然後把下面的程式碼放在裡面。
你會注意到 Helloworld 控制器繼承了 BaseController ,如果你不需要 BaseController 提供的功能,你也可以直接繼承 CodeIgniter\Controller
。
BaseController 替載入元件以及執行所有控制器都需要的功能提供了一個好地方,你可以在任何新的控制器中繼承這個類別。
為了安全起見,請以保護或私有宣告新的方法。
<?php
namespace App\Controllers;
class Helloworld extends BaseController
{
public function index()
{
echo 'Hello World!';
}
}
然後將文件保存到你的 /app/Controllers/ 目錄下。
重要
該文件必須命名為 「Helloworld.php」,大寫 「H」。
現在用類似於這樣的 URL 存取你的網站:
example.com/index.php/helloworld
如果你做對了,你應該會看到:
Hello World!
重要
控制器類別的名稱必須以大寫字母開頭,並且只有第一個字符可以是大寫。
這是有效的命名:
<?php
namespace App\Controllers;
class Helloworld extends BaseController
{
}
這是 無效 的命名:
<?php
namespace App\Controllers;
class helloworld extends BaseController
{
}
這是 無效 的命名:
<?php
namespace App\Controllers;
class HelloWorld extends BaseController
{
}
另外,一定要確保你的控制器繼承了父控制器類別,這樣它才可以繼承所有的方法。
備註
當未找到與你所宣告的路由相配對的項目時,系統將會嘗試透過將每個區段與 APPPATH/Controllers
中的資料夾/檔案進行配對來找到合適的控制器。這就是為什麼資料夾以及檔案必須大寫字母開頭,其餘部分皆是小寫的原因。如果你需要其他的命名方式,則需要使用 URI 路由 功能手動宣告。
以下是基於 PSR-4 的範例:自動載入器
\<NamespaceName>(\<SubNamespaceNames>)*\<ClassName>
$routes->get('helloworld', 'App\Controllers\HelloWorld::index');
方法¶
在上面的例子中,方法的名稱是 index()
。如果 URI 的第二個區段是空的,那麼 「index」 方法預設總會被載入。另一種顯示 「Hello World」 消息的方法是這樣的:
example.com/index.php/helloworld/index/
URI 的第二個區段決定了控制器中的哪個方法會被呼叫。
讓我們來試一試,為你的控制器中添加一個新方法:
<?php
namespace App\Controllers;
class Helloworld extends BaseController
{
public function index()
{
echo 'Hello World!';
}
public function comment()
{
echo 'I am not flat!';
}
}
現在載入下面的URL,查看 comment 方法:
example.com/index.php/helloworld/comment/
你應該看到你的新內容了。
將 URI 區段傳給你的方法¶
如果你的 URI 包含兩個以上的區段,它們將會作為參數傳遞給你的方法。
例如,假設你的 URI 長得是這樣的:
example.com/index.php/products/shoes/sandals/123
URI 的第三個區段和第四個區段(」sandals」 和 「123」)將傳入你的方法:
<?php
namespace App\Controllers;
class Products extends BaseController
{
public function shoes($sandals, $id)
{
echo $sandals;
echo $id;
}
}
重要
如果你使用了 URI 路由 功能,傳遞給你的方法的區段,將是重定向的區段。
定義一個預設控制器¶
CodeIgniter 可以在被告知沒有 URI 時、或是被請求你的網站根網址時,CodeIgniter 會加載一個預設的控制器。讓我們用 Helloworld 控制器來試一下。
要指定一個預設控制器,請打開你的 app/Config/Routes.php 檔案並設定這個變數:
$routes->setDefaultController('Helloworld');
其中 『Helloworld』 是你要使用的控制器類別的名稱。
在 Routes.php 下面的 「Route Definitions」 部分的幾行中,將下面這一行註解掉:
$routes->get('/', 'Home::index');
如果你現在瀏覽到你的網站時沒有指定任何的 URI 區段,你會看到 「Hello World」 訊息。
備註
這一行 $routes->get('/', 'Home::index');
是你在打造你的產品應用中會想要使用的最佳化寫法。但為了示範,我們不想使用這個功能。$routes->get()
在 URI Routing 中會進行解釋。
想了解的更加深入,可以參考 URI 路由文件 中的「路由設定選項」章節。
呼叫重映射方法¶
如上所述,URI 的第二段通常決定了要呼叫控制器中的哪個方法。 CodeIgniter 允許你透過使用 _remap()
方法來覆蓋這個行為:
public function _remap()
{
// Some code here...
}
重要
如果你的控制器包含一個名為 _remap() 的方法,無論你的 URI 包含什麼,它 都會被呼叫 。它覆蓋了 URI 正常判斷該呼叫哪個方法的行為,允許你定義自己的路由規則方法。
被覆蓋的呼叫方法(通常是 URI 的第二段)將作為參數傳遞給 _remap()
方法:
public function _remap($method)
{
if ($method === 'some_method') {
return $this->$method();
} else {
return $this->default_method();
}
}
方法名稱後的任何額外的字段都會被傳遞到 _remap()
中。這些參數可以被傳遞到方法中,來模擬CodeIgniter的預設行為。
範例:
public function _remap($method, ...$params)
{
$method = 'process_'.$method;
if (method_exists($this, $method)) {
return $this->$method(...$params);
}
throw \CodeIgniter\Exceptions\PageNotFoundException::forPageNotFound();
}
私有方法¶
在某些情況下,你可能希望某些方法不要被公開存取。為了達到這個目的,只需將方法聲明為私有或保護方法。這樣就可以防止它被 URL 請求送達。舉個例子,如果你為 Helloworld 控制器定義了一個像這樣的方法:
protected function utility()
{
// some code
}
然後嘗試使用以下 URL 來存取它,他將無法執行:
example.com/index.php/helloworld/utility/
將你的控制器組織成子目錄¶
如果你正在構建一個大型的應用程式,你可能會希望將控制器分層組織或結構化為子目錄。CodeIgniter 可以讓你達成這個目的。
只需在主要的 app/Controllers/ 目錄下創建子目錄,然後將你的控制器類別放進去。
重要
資料夾名稱必須以大寫字母開頭,並且只有第一個字元可以大寫。
備註
使用此功能時,URI的第一段必須要指定資料夾。例如,假設你有一個控制器位於這裡:
app/Controllers/products/Shoes.php
要呼叫上面的控制器,你的 URI 會看起來像這樣:
example.com/index.php/products/shoes/show/123
你的每個子目錄都可能包含一個預設控制器,如果 URL 裡面 只 包含子目錄,那麼這個控制器就會被呼叫。只需在那裡放一個與你的 app/Config/Routes.php 檔案中指定的 「default_controller」 名稱相匹配的控制器即可。
CodeIgniter 還允許你使用 URI Routing 功能重新映射你的URI。
擁有的屬性¶
你創建的每個控制器都應該繼承 CodeIgniter\Controller
類別。這個類別提供了幾個功能,所有的控制器都可以使用。
請求物件
應用程式的主要的 請求實體 總是可以作為一個類別屬性,$this->request
。
響應物件
應用程式的主要的 響應實體 總是可以作為一個類別屬性,$this->response
。
日誌物件
應用程式的主要的 日誌實體 總是可以作為一個類別屬性,$this->logger
。
強制 HTTPS
在所有的控制器中都有一個方便的方法,可以強制使用者透過 HTTPS 來存取一個方法:
if (! $this->request->isSecure()) {
$this->forceHTTPS();
}
預設情況下,在支持 HTTP 嚴格傳輸安全頭的現代瀏覽器中,這個呼叫應該強制瀏覽器將非 HTTPS 存取轉換為 HTTPS 存取一年。你可以透過傳入持續時間(秒)作為第一個參數來修改:
if (! $this->request->isSecure()){
$this->forceHTTPS(31536000); // 一年
}
備註
有數個 以時間定義的常數 可以讓你使用,像是 YEAR、MONTH 等。
驗證資料¶
為了簡化資料驗證,控制器還提供了一個方便的方法 validate()
。該方法在第一個參數中接受一個規則陣列,在可選的第二個參數中,接受一個自定義的錯誤信息陣列,如果物件無效,則顯示自定義的錯誤訊息。在內部,這個方法使用控制器的 $this->request 實體來獲取要驗證的資料。驗證函式庫文件 中有關於規則和訊息陣列格式的詳細資訊,以及可用的規則:
public function updateUser(int $userID)
{
if (! $this->validate([
'email' => "required|is_unique[users.email,id,{$userID}]",
'name' => 'required|alpha_numeric_spaces'
])) {
return view('users/update', [
'errors' => $this->validator->getErrors()
]);
}
// do something here if successful...
}
如果你覺得在設定文件中保留規則更簡單,你可以把 $rules 陣列替換成 Config\Validation.php
中定義的組名:
public function updateUser(int $userID)
{
if (! $this->validate('userRules')) {
return view('users/update', [
'errors' => $this->validator->getErrors()
]);
}
// do something here if successful...
}
備註
驗證也可以在模型中自動處理,但有時在控制器中進行驗證會更方便。具體在哪裡執行,由你自己決定.