所謂控制器是一個PHP函式,用來讀取Request物件中的資訊,並建立和回傳一個Response物件,這個回應(response)可以是HTML頁面、JSON、XML、供下載的檔案、重新定向(redirect)、404錯誤等等,控制器根據應用的需求,執行任何可能的邏輯去呈現畫面。
簡易的控制器
雖然控制器可以是任何可呼叫的(函式、物件中的方法或Closure),但大多會是一個控制器類內的方法(a method inside a controller class):// src/Controller/LuckyController.php
namespace App\Controller;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
class LuckyController
{
/**
* @Route("/lucky/number/{max}", name="app_lucky_number")
*/
public function number($max)
{
$number = random_int(0, $max);
return new Response(
'Lucky number: '.$number.''
);
}
}控制器就是方法number(),被放在名為LuckyController的控制器類。這個控制器非常容易理解:
- line 2: Symfony利用PHP的命名空間(namespace)功能來命名整個控制器類。
- line 4: Symfony也是利用PHP的命名空間功能來命名整個控制器類:關鍵字use輸入(import)一個控制器必要回傳的類(class)Response。
- line 7: 實作上,類名可隨便取,但按慣例會用Controller當作後綴字。
- line 12: 由於有個{max}的通配符在路由,操作方法(action method)會有一個$max的參數。
- line 16: 控制器建立和回傳一個Response物件
對應URL到控制器
想要看到控制器呈現的結果,你必須透過路由將URL與其對應,這邊的設定是透過註解式路由,寫成@Route("/lucky/number/{max}")來達成對應。想看頁面,請用瀏覽器到這個URL:
欲見更多關於路由資訊,請看路由。
基礎的控制器和服務
Symfony為了讓大家輕鬆點,有一個基礎控制器AbstractController可以選擇使用,你可以延伸了解一些輔助方式(helper methods)。在控制器類的上方加上use的語句,並修改LuckyController以擴展(extend)它:
// src/Controller/LuckyController.php namespace App\Controller; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; class LuckyController class LuckyController extends AbstractController { // ... }就是這樣!你現在已經接觸了像$this->render()這樣的方法,接下來還有很多其他的方法你會慢慢學到。
生成URL
generateUrl()方法是一個輔助方法,可以針對給定的路由產生URL:$url = $this->generateUrl('app_lucky_number', ['max' => 10]);重新導向(Redirecting)
如果你像要將使用者重新導向其他頁面,可以使用redirectToRoute()和redirect()兩個方法:use Symfony\Component\HttpFoundation\RedirectResponse;
// ...
public function index()
{
// redirects to the "homepage" route
return $this->redirectToRoute('homepage');
// redirectToRoute is a shortcut for:
// return new RedirectResponse($this->generateUrl('homepage'));
// does a permanent - 301 redirect
return $this->redirectToRoute('homepage', [], 301);
// redirect to a route with parameters
return $this->redirectToRoute('app_lucky_number', ['max' => 10]);
// redirects to a route and maintains the original query string parameters
return $this->redirectToRoute('blog_show', $request->query->all());
// redirects externally
return $this->redirect('http://symfony.com/doc');
}渲染模板(rendering template)
如果你要回傳HTML,你會想要用渲染模板,方法render()會渲染模板,並封裝成Response物件給你:// renders templates/lucky/number.html.twig
return $this->render('lucky/number.html.twig', ['number' => $number]);模板和Twig相關的說明,在文章『建立和使用模板』(原文)有更多的說明。獲取服務(fetching services)
Symfony包含相當多有用的物件,稱為服務(service)。可以用來渲染模板、發送信件、查詢資料庫和任何你想得到的工作。如果你要在控制器裡使用服務,請用它的類名(或介面(interface))宣告參數,Symfony會自動提供你所需的服務:
use Psr\Log\LoggerInterface;
// ...
/**
* @Route("/lucky/number/{max}")
*/
public function number($max, LoggerInterface $logger)
{
$logger->info('We are logging!');
// ...
}太棒了!其他還有哪些服務類型可以使用?如果想知道,請在控制台執行指令debug:autowiring:
php bin/console debug:autowiring
如果需要控制參數的確切值,可以使用其名稱綁定(bind)參數:
// 省略程式碼和其他服務一樣,你可以在控制器中用常規構造函數注入(regular constructor injection)。
想要知道更多關於服務的資訊,請看服務容器(Service Container)這篇文章。
產生控制器
為了節省時間,你可以安裝Symfony Maker,並藉由它產生一個新的控制器類:php bin/console make:controller BrandNewController created: src/Controller/BrandNewController.php如果想要產生針對Doctrine實體(entity)完整的CRUD,執行:
php bin/console make:crud Product
錯誤處理及404頁面
如果找不到東西,應該回傳一個404的回應,請拋(throw)一個特殊形態的例外(exception)來達成:use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
// ...
public function index()
{
// retrieve the object from database
$product = ...;
if (!$product) {
throw $this->createNotFoundException('The product does not exist');
// the above is just a shortcut for:
// throw new NotFoundHttpException('The product does not exist');
}
return $this->render(...);
}
方法createNotFoundException()只是一個NotFoundHttpException特殊物件的捷徑,最終會觸發Symfony內的一個404 HTTP回應。如果你拋一個延伸的例外或HttpException的實例(instance),Symfony會自帶適當的HTTP狀態碼(status code),否則回應就會是HTTP status code 500。
// this exception ultimately generates a 500 status error
throw new \Exception('Something went wrong!');每個情況下,錯誤頁面(error page)是顯示給一般使用者看,完整的除錯頁面(full debug error page)則是供開發者(i.e. 當你在除錯模式(Debug mode) - The parameters Key: Parameters (Variables))。若想要客製化顯示給使用者看的錯誤頁面,請參考如何將錯誤頁面客製化的文章(How to Customize Error Pages article)。
回應物件作為控制器參數
如果想要讀取查詢參數、取得請求的標頭(header)或取得上船的檔案?所有的資訊都存在Symfony的Request物件中,要在你的控制器內取得,只要用請求類名宣告成參數:use Symfony\Component\HttpFoundation\Request;
public function index(Request $request, $firstName, $lastName)
{
$page = $request->query->get('page', 1);
// ...
}
更多資訊請參考Request Object(原文)管理Session
Symfony提供session的服務,可以讓你從請求中取得更多關於使用者的資訊,預設狀態下就可以使用session,不過只有在讀取或寫入的時候才會啟動。Session儲存區和其他設定都是由config/packages/framework.yaml檔案內的framework.session configuration管控。
要取得session,請新增一個參數並宣告型態為SessionInterface:
use Symfony\Component\HttpFoundation\Session\SessionInterface;
public function index(SessionInterface $session)
{
// stores an attribute for reuse during a later user request
$session->set('foo', 'bar');
// gets the attribute set by another controller in another request
$foobar = $session->get('foobar');
// uses a default value if the attribute doesn't exist
$filters = $session->get('filters', []);
}儲存的屬性會放在session哩,跟使用者其他的session一起。想要了解更多,請看Session。
Flash Messages
你可以儲存一些特別的訊息在使用者的session,稱為"Flash",設計上,flash message只會使用一次: 一旦你檢索後就會自動從session裡面移除,這個特性讓flash message特別適合存放用戶通知。例如:想像你需要處理一個表單(form)的遞交:
use Symfony\Component\HttpFoundation\Request;
public function update(Request $request)
{
// ...
if ($form->isSubmitted() && $form->isValid()) {
// do some sort of processing
$this->addFlash(
'notice',
'Your changes were saved!'
);
// $this->addFlash() is equivalent to $request->getSession()->getFlashBag()->add()
return $this->redirectToRoute(...);
}
return $this->render(...);
}
在處理完這個請求後,控制器會設定flash message到session並重新導向,訊息的鍵(key)(這個例子裡,鍵就是notice)可以任意指定:你可以用這個key來檢索訊息。在下一頁的模板中(如果放在你的基礎模板中會更好),用app.flashes()來讀取session內任何flash message:
{# templates/base.html.twig #}
{# read and display just one flash message type #}
{% for message in app.flashes('notice') %}
{{ message }}
{% endfor %}
{# read and display several types of flash messages #}
{% for label, messages in app.flashes(['success', 'warning']) %}
{% for message in messages %}
{{ message }}
{% endfor %}
{% endfor %}
{# read and display all flash messages #}
{% for label, messages in app.flashes %}
{% for message in messages %}
{{ message }}
{% endfor %}
{% endfor %}常見使用notice、warning和error作為不同類型flash message的鍵(key),當然你也可以使用任何符合你需求的鍵。請求和回應
如同上述,Symfony會傳一個Request物件給任何有宣告參數Request類的控制器:use Symfony\Component\HttpFoundation\Request;
public function index(Request $request)
{
$request->isXmlHttpRequest(); // is it an Ajax request?
$request->getPreferredLanguage(['en', 'fr']);
// retrieves GET and POST variables respectively
$request->query->get('page');
$request->request->get('page');
// retrieves SERVER variables
$request->server->get('HTTP_HOST');
// retrieves an instance of UploadedFile identified by foo
$request->files->get('foo');
// retrieves a COOKIE value
$request->cookies->get('PHPSESSID');
// retrieves an HTTP request header, with normalized, lowercase keys
$request->headers->get('host');
$request->headers->get('content-type');
}Request類有幾個公開屬性(public properties)和方法,可返回有關請求的所有資訊。跟Request一樣,Response物件也有公開屬性headers,ResponseHeaderBag提供一些好用的方法來設定回應的標頭檔(header),標頭名稱(header names)一般標準使用Content-Type,等同於content-type和content_type。
控制器唯一的條件就是要回傳Response物件:
use Symfony\Component\HttpFoundation\Response;
// creates a simple Response with a 200 status code (the default)
$response = new Response('Hello '.$name, Response::HTTP_OK);
// creates a CSS-response with a 200 status code
$response = new Response('');
$response->headers->set('Content-Type', 'text/css');有個特殊的類可以讓部分的回應簡單一點,下面會列舉一部份。想要了解更多關於Request和Response(還有特殊類的Response)的資訊,請查看HttpFoundation component documentation。回傳JSON回應
如果要在控制器內回傳JSON,可以用輔助方法json(),這會回傳一個已經自動編碼(encode)的JsonResponse特殊物件。// ...
public function index()
{
// returns '{"username":"jane.doe"}' and sets the proper Content-Type header
return $this->json(['username' => 'jane.doe']);
// the shortcut defines three optional arguments
// return $this->json($data, $status = 200, $headers = [], $context = []);
}如果能在你的應用裡使用serializer service,可以幫你把資料連載(serialize)成JSON格式,不然也可以用函式json_encode。媒體文件回應(Streaming File Responses)
你可以在控制器內用輔助方式file()操作檔案:public function download()
{
// send the file contents and force the browser to download it
return $this->file('/path/to/some_file.pdf');
}
輔助方式file()提供一些參數設定來決定表現行為:use Symfony\Component\HttpFoundation\File\File;
use Symfony\Component\HttpFoundation\ResponseHeaderBag;
public function download()
{
// load the file from the filesystem
$file = new File('/path/to/some_file.pdf');
return $this->file($file);
// rename the downloaded file
return $this->file($file, 'custom_name.pdf');
// display the file contents in the browser instead of downloading it
return $this->file('invoice_3241.pdf', 'my_invoice.pdf', ResponseHeaderBag::DISPOSITION_INLINE);
}結語
當你建立頁面時,你最後會需要幫頁面寫一些邏輯的程式碼,在Symfony稱之為控制器,是一個PHP的函式能讓你執行任何操作,並回應一個Response物件給使用者。為了簡化一點,你可能會延伸基礎控制器AbstractController的類,因為這樣可以讓你快速取得方法render()和redirectToRoute()的捷徑。
在其他文章裡,你會學到如何在控制器裡使用特定的服務,幫助你從資料庫獲取物件、處理表單提交、處理緩存等等。
繼續前進!
接下來,來學習『Symfony4.2入門教學【第五篇】模板(Templates)』(rendering template with Twig)。文章原文:
https://symfony.com/doc/4.2/controller.html
系列文章:
- Symfony4.2入門教學【第一篇】安裝
- Symfony4.2入門教學【第二篇】建立第一個頁面(Routes)
- Symfony4.2入門教學【第三篇】路由(Routing)
- Symfony4.2入門教學【第五篇】模板(Templates)
- Symfony4.2入門教學【第六篇】設定(Configuration)
沒有留言:
張貼留言