控制器是PHP函數,通過它,你可以根據HTTP請求建立任務資訊,并且建構和傳回HTTP響應(作為Symfony2的Response對象)。響應可以是HTML頁面、XML文檔、序列化的JSON數組、圖檔、重定向、404錯誤甚至是你可以想到的一切。控制器中包含了你應用程式需要建立響應的抽象邏輯。
你的控制器可能是從請求中讀取資訊、導引資料庫資源、發送電子郵件或者在使用者Session中設定資訊。但控制器的最終目的是傳回傳遞給用戶端的Response對象,不用擔心還會變出什麼戲法或有其它什麼要求。下面是一些常見的示例:
1、控制器A準備了一個顯示網站首面内容的Response對象
2、控制器B從請求中讀取slug參數,從資料庫中載入一條博文,并建立一個顯示該博文的Response對象。如果slug不能被資料庫中檢索到,那麼控制器将建立并傳回一個帶404狀态碼的Response對象。
3、控制器C處理關于聯系人的表單送出。它從請求中讀取表單資訊,将聯系人資訊存入資料庫并發送包含聯系人資訊的電子郵件給網站管理者。最後,它建立一個Response對象将使用者的浏覽器重定向到聯系人表單的“感謝”頁面。
“請求、控制器、響應” 生命周期
通過Symfony2項目處理的每個請求都會通過基本相同的生命周期。架構保管重複任務并最終執行有着你自定義應用程式代碼的控制器。
1、每個請求都被單個前端控制器(如app.php或index.php)檔案處理,前端控制器負責引導架構;
2、路由檢視并比對請求資訊,并将其指向一個特定的路由,該路由決定調用哪個控制器;
3、執行控制器,控制器中的代碼将建立并傳回一個Response對象;
4、HTTP頭和Response對象的内容将發回用戶端。
建立控制器與建立頁面一樣友善,同時映射一個URI到該控制器
雖然名稱相似,但前端控制器與我們在本章節所說的控制器是不同的,前端控制器是你web目錄中的一個PHP小檔案,所有的請求都直接經過它。一個典型的應用程式将有一個用于生産的前端控制器(如app.php)和一個用于開發的前端控制器(如app_dev.php)。你可以永遠不需要對前端控制器進行編輯、檢視和擔心。
一個簡單的控制器
控制器是一個可調用的PHP,負責傳回資源的表現(大多數時間是一個HTML表現)。雖然一個控制器可以是任何的可被調用的PHP(函數、對象的方法或Closure),在Symfony2,控制器通常是在控制器對象中的一個方法,控制器也常被稱為action。
注意:控制器是indexAction方法,它隸屬于一個控制器類(HelloController)。不要對名稱感到困惑:控制器類隻是簡單将幾個控制器集中在一起的。通常情況下,控制器類将放置多個控制器(如updateAction、deleteAction等),一個控制器有時也會被當作一個action被提及。
這個控制器非常簡單,但還是讓我們看看它:
1、第3行:Symfony2充分利用了PHP5.3的名稱空間的功能去為整個控制器類命名空間。use關鍵字導入Response類,是我們控制器必須傳回的;
2、第6行:類名是一個控制器名加上詞Controller。這是為控制器提供一緻的慣例,并且可以在路由配置中僅被指向前面部分(如Hello);
3、第8行:在控制器類中的每個action都有着字尾Action,并在路由配置中通過action名(index)被指定。在下一節中,我們将使用路由映射一個URI到該action,并展示如何将路由占位符({name})變成action的參數($name);
4、第10行:控制器建立并傳回一個Response對象。
将URI映射到控制器
我們的新控制器傳回一個簡單的HTML頁。為了能夠在指定URI中渲染該控制器,我們需要為它建立一個路由。
我們将在路由章節中讨論路由元件的細節,但讓我們為我們的控制器建立一個簡單路由:
/hello/ryan 現在執行HelloController::indexAction()控制器,并且将ryan賦給$name變量。建立一個“頁”意味着簡單地建立一個控制器方法并與路由關聯。這裡并沒有什麼隐蔽的層或藏在螢幕後的魔法。
注意,用于指向控制器的文法:AcmeHelloBundle:Hello:index。Symfony2使用一個靈活的字元串注釋來指向不同的控制器。這是最通用的文法并告訴Symfony2到名為AcmeHelloBundle内部去查找一個名為HelloController的控制器類,然後執行方法indexAction()。
關于指向不同控制器的字元串格式用法的更多細節,請參見控制器命名模式。
注意因為我們的控制器處于AcmeHellBundle中,是以我們将路由配置有序地放置在AcmeHelloBundle中。要引導Bundle中的路由配置,必須從你的應用程式的主路由資源中導入。詳見包含外部路由資源。
作為控制器參數的路由參數
我們現在已經知道_controller的參數 AcmeHelloBundle:hello:index 指向AcmeHelloBundle 中的HelloController::indexAction()方法,這裡更有趣的是發送給該方法的參數:
控制器有個參數$name,對應所比對路由的{name}參數(在本例中是ryan)。實際上當執行你的控制器時,Symfony2在所比對路由中比對帶參數控制器中的每個參數。以下所示:
是以控制器也需要多個參數:
注意兩個占位符(`first_name`、`last_name`)和預設color變量做為控制器的參數。當路由比對時,占位符變量與 defaults 合并成一個數組,并且用于你的控制器。
将路由參數映射到控制器參數是十分容易和靈活的。在你開發時請遵循以下思路:
控制器參數的順序無關緊要
Symfony2可以根據路由參數名比對控制器方法參數的特征。換句話說,它可以實作last_name參數與$last_name參數的比對。控制器可以在随意排列參數的情況下正常工作。
控制器所需參數必須比對路由參數
下面會抛出一個運作時異常(RuntimeException),因為在路由定義中沒有foo參數
然而如果設定該參數可選是完全可行的。下面的例子不會抛出異常:
不是所有的路由參數都需要在控制器上有相應參數的
如果,舉個例子,last_name對你控制器不是很重要的話,你可以完全忽略掉它:
實際上,_controller路由參數本身就可以作為控制器參數,因為它也在路由的defaults中。當然它通常是沒用的,是以我們在控制器中忽略它
每條路由也都有一個特殊的_route參數,該參數比對路由的名字(如hello)。雖然不常用,但它一樣也可以作為控制器的參數。
(譯者:這麼幾段下來,其實隻需要記住一點,那就是有控制器的參數,而沒有相比對的路由參數,而反之則無妨!)
Controller基類
出于友善的考慮,Symfony2提供了一個Controller基類,以幫助實作常用的一些控制器任務,并站你的控制器類能夠通路所需的資源。通過繼承該類,你可以利用其中的一些幫手方法。
在頂部使用use語句添加Controller類,然後修改HelloController去繼承它。如下所示:
到目前為止,繼承Controller類并沒有改變任何東西。在下一節中,我們将使用幾個該基類中的助手方法。這些方法隻是讓你可以友善地使用Symfony2的核心功能,而無論你是否使用Controller基類。其實檢視核心功能的最好方式就是看Controller類本身。
在Symfony2中是否繼承基類是可以選擇的;它包含了非常有用的便捷方式,但并不強制使用。你也可以繼承ContainerAwaer。這個服務容器對象然後可以通過container屬性通路,并且它隻是你需要建立任何控制器的對象。
你也可以将你的控制器定義成服務。
常用控制器任務
雖然控制器實際上可以做任何事情,但大多數控制器都是一遍又一遍地執行同一個基本任務。這些任務包括重定向、轉發、渲染模闆和通路核心服務,這一切在Symfony2中非常便于管理。
重定向
如果你想将使用者重定向到另一頁,使用特殊的RedirectResponse類,該類就是為将使用者重定向到另一URL而特别設計的:
generateUrl()方法隻是一個在路由服務中調用generate()方法的快捷方法。它需要路由名和一個數組來做為參數,并傳回相應的URL。更多詳情請參見路由一章。
預設情況下,redirect方法使用302(臨時)重定向。如果要執行301(永久)重定向,請修改第2個參數:
轉發
你也可以很容易地通過forward()方法内部轉發到另一個action。與重定向使用者浏覽器不同,它産生一個内部的子請求,并調用一個特殊的控制器。forward()方法傳回的Response對象在必要的情況下可以進一步修改。Response對象是内部子請求的最終産品:
注意forward()方法使用與路由配置中相同的控制器字元串表示。在本例中目标控制器類将是在AcmeHelloBundle中的Hellocontroller類,發送給控制器類方法的數組做為其參數。當内嵌的控制器進入模闆時使用相同的接口(參見内嵌控制器)。目标控制器通路如下所示:
就這樣,當為一個路由建立一個控制器時,fancyAction的參數順序不是問題。Symfony2檢索關鍵詞名(如name),并将其比對到方法參數名(如$name)。如果你改變參數的順序,Symfony2也會将正确的值賦給每個變量。
正如其它Controller基類的方法一樣,forward方法也隻是Symfony2核心功能是快捷方式。轉發可以通過http_kernel服務直接完成。轉發傳回一個Response對象。
渲染模闆
雖然沒做要求,但大多數控制器最終将渲染一個負責為控制器生成HTML(或其他格式)的模闆。renderView()方法渲染模闆并傳回它的内容。來自模闆的内容可以用來建立一個Response對象:
在上兩個示例中,AcmeHelloBundle中的Resources/views/Hello/index.html.twig模闆将被渲染。
Symfon模闆引擎的更多細節請參見模闆章節。
renderView方法是直接使用模闆服務的快捷方式。也可以直接使用模闆服務:
通路其他服務
當繼承controller基類後,你可以通過get()方法通路任何Symfony2的服務。你可以需要下列常見服務:
Symfony2可以有無數個其它的服務,也鼓勵你定義自己的服務。詳情請參見服務容器章節。
管理錯誤
如果沒找到,将傳回一個404響應,通過抛出一個内建的HTTP異常将很容易做到這一點:
NotFoundHttpException将傳回一個404HTTP響應到浏覽器。當在調試模式中檢視頁面時,堆棧跟蹤所有的異常将被顯示,是以異常的原因是很容易找出的。
當然,你也可以自由地抛出你控制器中的任何異常類,Symfony2将自動傳回HTTP響應代碼500。
在每個示例中,一個帶格式的錯誤頁被顯示給最終使用者,而一個全調試錯誤頁會被顯示給開發者(當在調試模式檢視該頁時)。這些錯誤頁都是可以自定義的。要想知道更多請閱讀“如何自定義錯誤頁”。
管理會話(Session)
即使HTTP協定是無狀态的,Symfony2提供了一個好的會話對象來代表用戶端(它可以是使用浏覽器的人、bot或Web服務)。在兩個請求之間,Symfony2通過使用PHP的本地會話來儲存cookie中的屬性。
這些屬性将會在該使用者會話的剩餘時間中保留。
Flash消息
你也可以為了一個額外的請求在使用者會話中儲存一些小消息。這在處理表單時很有用:你想重定向并在下個請求中顯示一個特定的消息。這些類型的消息被稱為“Flash”消息。
讓我們看看我們處理表單送出的示例:
在處理請求之後,控制器設定了一個名為notice的flash消息,然後重定向。在下一個action的模闆中,下列代碼用來渲染該消息:
按照設計,flash消息意味着僅存活一個請求(它們總是“稍縱即逝”)。在本例中它們被設計用來跨躍重定向。
響應對象
對于控制器,唯一的要求就是傳回一個Response對象。Response類是一個PHP對于HTTP響應的一個抽象,一個基于文本的消息填充HTTP頭,其内容發返用戶端:
headers屬性是有着許多有用方法的HeaderBag對象用來讀取和改變Response頭的。頭名稱的規範化使得 Content-Type和content-type甚至是content_type相同。
請求對象
如同Response對象一樣,請求頭被儲存在HeaderBag對象中,并可以輕易通路到。
概述
在Symfony中,控制器就是包含建立和傳回任意所需邏輯的Response對象的PHP函數。控制器允許我們對于擁有多個頁的應用程式保持每個頁面被組織進不同控制器類和action方法的邏輯。
Symfony2通過路由比對決定執行哪個控制器,并解析its_controller參數的文字格式到一個真實Symfony2的控制器。在控制器上的參數與路由上的參數相對應,允許你的控制器去通路來自請求的資訊。
控制器可以做任何事并且包含任何邏輯,隻要它傳回一個Response對象。如果你繼承了Controller類,你就有權力通路所有Symfony2的核心服務對象,以及執行大多數常見任務的快捷方法。
本文轉自 firehare 51CTO部落格,原文連結:http://blog.51cto.com/firehare/586468,如需轉載請自行聯系原作者