程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> 網頁編程 >> PHP編程 >> PHP綜合 >> Symfony2學習筆記之控制器用法詳解

Symfony2學習筆記之控制器用法詳解

編輯:PHP綜合

本文實例講述了Symfony2控制器用法。分享給大家供大家參考,具體如下:

一個controller是你創建的一個PHP函數,它接收HTTP請求(request)並創建和返回一個HTTP回復(Response)。回復對象(Response)可以是一個HTML頁面,一個XML文檔,一個序列化的JSON數組,一個圖片,一個重定向,一個404錯誤或者任何你想要的內容。controller中可以包含任何渲染你頁面內容的所需要的邏輯。

下面是一個controller最簡單的例子,僅僅打印一個Hello world!

use Symfony\Component\HttpFoundation\Response;
public function helloAction()
{
 return new Response('Hello world!');
}

Controller的終極目標都是相同的那就是創建並返回一個Response對象。按照這個思路,你可以從request對象讀取信息,加載數據庫資源,發送email,或者在用戶的Session中寫入信息。但是所有情況下,Controller將最終都會返回一個Response對象並被分發會客戶端。

比如如下情況:

Controller A 准備一個Response對象來表現網站homepage內容。
Controller B 從Request中讀取slug參數從數據庫中加載一個blog內容並創建一個Response對象來顯示這個blog。如果slug在數據庫中不存在,它將創建並返回一個帶有404狀態碼的Response對象.

Controller C 處理一個從聯系表單,它從Request對象中讀取表單信息,保存聯系信息到數據庫並發郵件給管理員。最後,它創建一個Response對象重定向客戶端浏覽器到聯系表單感謝頁面。

Requests,Controller, Response的生命周期

Symfony2項目中處理的每一個Request都是經過了相同的簡單生命周期。框架負責重復的任務,最終執行一個controller,該controller會包含你的應用程序代碼:

1.每個Request都會被一個統一的前端控制器文件(比如,app.php,或者app_dev.php)處理,它會啟動應用程序。
2.Router從Request中讀取URI信息,並找到匹配它的Route,從該Route中讀取_controller參數。
3.匹配成功的route的controller被執行,controller中的代碼創建並返回一個Response對象。
4.HTTP頭和生成的Response對象內容將會被發回客戶端。

創建一個頁面跟創建一個controller一樣容易,創建一個路由來映射一個URL到該controller。

注意:盡管從名字上來看,前端控制器和controller差不多,其實它們是不同的。
一個前端控制器是一個存放於web目錄下的PHP文件,多有的Request都會通過它被重定向。每一個應用程序都會有一個產品前端控制器app.php和一個開發用的前端控制器app_dev.php。你不需要編輯,查看或者擔心它們。

看一個簡單的Controller: 任何的PHP可調用內容(比如函數,對象方法或者一個Closure)都可以成為一個controller。Symfongy2中,一個controller通常為controller對象中一個單一的方法。Controllers通常也被稱為actions。

// src/Acme/HelloBundle/Controller/HelloController.php
namespace Acme\HelloBundle\Controller;
use Symfony\Component\HttpFoundation\Response;
class HelloController
{
 public function indexAction($name)
 {
  return new Response('<html><body>Hello '.$name.'!</body></html>');
 }
}

注意在這個例子中controller是indexAction方法,它存在於controller類(HelloController)中。不要混淆,之所以定義一個controller類(HelloController)只是為了方便組織多個controllers/actions在一起。一般情況下,一個controller類會有多個controllers/actions。

上面例子中的controller相當簡單:

Namespace行是symfony2使用了PHP5.3的命名空間功能來為整個controller類指定命名空間。
use關鍵字導入了Response類,這是我們的controller必須返回的內容。

Controller類名字都是由其名字後面加Controller來定義,但是只有前面的部分才是其真正名字,為了統一起見,在後面統一添加Controller。 在路由配置時只會取前面部分。

Controller類中每個被用於真正controller的方法都會被添加一個統一的後綴Action,同樣我們在配置其路由時也只會取前面部分而忽略掉Action。把它映射到某個URL。

每個controller方法的最後必然會創建一個Response對象並返回它。

映射一個URL到一個Controller方法:

上面例子中的controller方法返回一個簡單的HTML頁面。如果要在浏覽器中訪問到該頁面,那麼你需要為它創建一個route,把它映射到一個特定模式的URL上。

# app/config/routing.yml
hello:
 pattern:  /hello/{name}
 defaults:  { _controller: AcmeHelloBundle:Hello:index }

XML格式:

<!-- app/config/routing.xml -->
<route id="hello" pattern="/hello/{name}">
 <default key="_controller">AcmeHelloBundle:Hello:index</default>
</route>

PHP代碼格式:

// app/config/routing.php
$collection->add('hello', new Route('/hello/{name}', array(
 '_controller' => 'AcmeHelloBundle:Hello:index',
)));

現在想URL /hello/ryan 將被映射到HelloController::indexAction() controller並將ryan傳遞給$name變量。

創建一個所謂的頁面,其實就是創建一個controller方法和一個相關的route。

注意我們使用的指向controller方法的表示語法:AcmeHelloBundle:Hello:index

Symfony2使用了一個非常靈活的字符串聲明來指向不同的controller。它告訴Symfony2在一個名叫AcmeHelloBundle的bundle中去查找一個叫HelloController的類,並執行它的indexAction()方法。在這個例子中,我們的路由配置直接寫在了app/config/ 目錄下,一個更好的組織方式是把你的路由放到各自的bundle中。

路由參數作為Controller方法參變量

你已經了_controller參數 AcmeHelloBundle:Hello:index指向一個位於AcmeHelloBundle中名叫HelloController::indexAction()的方法。有趣的是路由中參數都會被傳遞給該方法。

<?php
// src/Acme/HelloBundle/Controller/HelloController.php
namespace Acme\HelloBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
class HelloController extends Controller
{
 public function indexAction($name)
 {
  // ...
 }
}

上例中controller方法有一個唯一參數,$name, 它對應著route中定義的{name}占位符名稱。事實上,等你執行你的controller時,Symfony2會匹配controller和route中每一個參數。

如果我修改一下Hello的路由定義:

YAML格式:

# app/config/routing.yml
hello:
 pattern:  /hello/{first_name}/{last_name}
 defaults:  { _controller: AcmeHelloBundle:Hello:index, color: green }

XML格式:

<!-- app/config/routing.xml -->
<route id="hello" pattern="/hello/{first_name}/{last_name}">
 <default key="_controller">AcmeHelloBundle:Hello:index</default>
 <default key="color">green</default>
</route>

PHP代碼格式:

// app/config/routing.php
$collection->add('hello', new Route('/hello/{first_name}/{last_name}', array(
 '_controller' => 'AcmeHelloBundle:Hello:index',
 'color'  => 'green',
)));

這時候controller中可以獲取這些參變量了:

public function indexAction($first_name, $last_name, $color)
{
 // ...
}

注意route定義中無論是占位符變量還是默認值變量都會被轉化為controller方法的輸入變量。當一個route匹配成功時,它會合並占位符和defaults到一個數組傳遞給controller。映射route參數到controller參數非常簡單和靈活。它們從route到controller不匹配順序。Symfony能夠把route中參變量的名字映射到controller方法簽名中的變量名字。比如{last_name} => $last_name,跟排列順序無關。

Controller方法中的參數必須匹配route中定義的參數下面為hello route定義的controller方法將會拋出異常:

public function indexAction($last_name, $color, $first_name)
{
 // ..
}

如果我們把$foo變量變為可選變量,那麼就不會拋異常了。

public function indexAction($first_name, $last_name, $color, $foo)
{
 // ..
}

並不是每一個在route中定義的參數都需要在controller中有與之對應的簽名參變量的,比如hello route中定義的{$last_name} 如果對你沒什麼意義的話可以在controller中省略掉它。

public function indexAction($first_name, $color)
{
 // ..
}

反之,如果你在Controller簽名中定義了變量,並且不是可選變量,那麼必須在route中有與之對應的參數被定義。

在route定義中有一個特殊參數 _route, 它匹配route的名稱(如上例中的hello)。雖然不常用,但是它也可以作為controller方法的一個參變量使用。

Request作為一個Controller方法簽名變量

為了方便,你可能會讓symfony傳遞你的Request對象作為參數到你的controller方法。這在你處理表單時尤為方便。

use Symfony\Component\HttpFoundation\Request;
public function updateAction(Request $request)
{
 $form = $this->createForm(...);
 $form->bindRequest($request);
 // ...
}

Controller基類

為了方便,Symfony2定義了一個Controller基類,包含了一些常用的controller任務並給了你的controller類訪問任何你需要的資源的途徑。通過繼承該類,你可以獲得許多幫助方法。

// src/Acme/HelloBundle/Controller/HelloController.php
namespace Acme\HelloBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Response;
class HelloController extends Controller
{
 public function indexAction($name)
 {
  return new Response('<html><body>Hello '.$name.'!</body></html>');
 }
}

在Symfony中controller並不一定非得繼承Controller基類,因為它內部的幫助方法等都不是必須的。你也可以繼承 Symfony\Component\DependencyInjection\ContainerAware 服務容器對象可以通過container屬性來訪問。同時你也可以把controller定義成service。

通用的Controller任務:

盡管Controller可以干任何事情,但是大部分的controller還是要重復的干一些基礎的任務。比如 重定向,跳轉,渲染模板和訪問核心服務等。

重定向(redirecting)

如果你想重定向你的用戶到另一個頁面,可以使用redirect()方法。

public function indexAction()
{
 return $this->redirect($this->generateUrl('homepage'));
}

這裡generateUrl()方法是一個幫助函數,用於根據給定的route生成相應的URL。默認情況下,redirect()方法執行一個302重定向。如果要執行301重定向,那麼需要修改第二個參數如下:

public function indexAction()
{
 return $this->redirect($this->generateUrl('homepage'), 301);
}

redirect()方法其實是一個簡化寫法,真正的代碼如下:

use Symfony\Component\HttpFoundation\RedirectResponse;
return new RedirectResponse($this->generateUrl('homepage'));

跳轉(Forwarding)

你可以使用forward()方法很容易從一個controller到另一個controller內部。它執行的是一個內部子請求,來調用指定的controller,所以不會產生用戶客戶端浏覽器的重定向。forward()方法返回的Response對象還將從原controller返回。

public function indexAction($name)
{
 $response = $this->forward('AcmeHelloBundle:Hello:fancy', array(
  'name' => $name,
  'color' => 'green'
 ));
 // further modify the response or return it directly
 return $response;
}

這裡forward()方法使用了跟route配置中相同的字符串參數。這裡傳入數組參數會作為目標調用controller的參數。當將controller嵌入到模板時,也會使用同樣的接口。目標調用的controller方法應該是如下定義:

public function fancyAction($name, $color)
{
 // ... create and return a Response object
}

就像為一個route創建一個controller一樣,跟參數的順序沒關系。symfony2 會匹配索引鍵名稱name到方法參數名稱$name,即使順序打亂也沒關系。跟其它Controller基類方法一樣,forward方法也僅僅是一個symfony2核心函數的快捷寫法。一個跳轉可以直接通過http_kernel服務來完成,返回一個Response對象。

$httpKernel = $this->container->get('http_kernel');
$response = $httpKernel->forward('AcmeHelloBundle:Hello:fancy', array(
 'name' => $name,
 'color' => 'green',
));

渲染模板:

雖然不是必須的,但是大部分controller將最終渲染一個負責生成為controller負責生成HTML的模板。renderView()方法會渲染一個模板並返回它的內容。這個返回內容可以用作創建Response對象,以供controller返回使用。

$content = $this->renderView('AcmeHelloBundle:Hello:index.html.twig', array('name' => $name));
return new Response($content);

上面的代碼完全可以更進一步的使用下面的代碼形式來寫:
復制代碼 代碼如下:return $this->render('AcmeHelloBundle:Hello:index.html.twig', array('name' => $name));

這兩種情況下,AcmeHelloBundle中的模板Resources/views/Hello/index.html.twig都會被渲染。

renderview()方法是如下代碼的快捷寫法:

$templating = $this->get('templating');
$content = $templating->render('AcmeHelloBundle:Hello:index.html.twig', array('name' => $name));

當然也可以在子目錄中渲染模板

$templating->render('AcmeHelloBundle:Hello/Greetings:index.html.twig', array('name' => $name));
// index.html.twig 存放於 Resources/views/Hello/Greetings 目錄.

訪問其它服務

只要是繼承了Controller基類,你就可以通過get()方法訪問symfony2的服務了。比如:

$request = $this->getRequest();
$templating = $this->get('templating');
$router = $this->get('router');
$mailer = $this->get('mailer');

Symfony2中還有無數的可用服務,同時也鼓勵你定義自己的服務。要查看所有的服務,可以使用container:debug 命令行工具

$ php app/console container:debug

管理錯誤和404頁面

當一些東西沒有找到,你應該重置HTTP協議返回一個404 回復。要做到這個,你將拋出一個特殊類型的異常。如果你是繼承了Controller基類,則:

public function indexAction()
{
 $product = // retrieve the object from database
 if (!$product) {
  throw $this->createNotFoundException('The product does not exist');
 }
 return $this->render(...);
}

createNotFoundException()方法創建一個特定的NotFoundHttpException對象,它最終觸發404 HTTP回復。當然你從你的controller方法中可以拋出任何類型的Exception 類,Symfony2會自動返回一個500 HTTP回復代碼。

throw new \Exception('Something went wrong!');

管理Session

Symfony2 提供了一個非常好的Session對象,你可以用它來在請求之間存貯有關用戶的信息。默認情況下,Symfony2 通過PHP本身的Session保存屬性到cookie。在任何controller中存儲和獲取Session信息將非常容易:

$session = $this->getRequest()->getSession();
// 為用戶的後一個請求使用存儲一個屬性
$session->set('foo', 'bar');
// 在另一個controller中為另一個請求獲取該屬性
$foo = $session->get('foo');
// 設置用戶的本地化語言
$session->setLocale('fr');

Flash 消息

你可以為特定的請求存儲少量的消息到用戶的Session。這在處理一個表單時非常有用,你想重定向和一個特定的信息顯示在下一個請求中。這種類型的消息被稱為Flash消息。比如,假設你處理一個表單提交:

public function updateAction()
{
 $form = $this->createForm(...);
 $form->bindRequest($this->getRequest());
 if ($form->isValid()) {
  // 做些排序處理
  $this->get('session')->setFlash('notice', 'Your changes were saved!');
  return $this->redirect($this->generateUrl(...));
 }
 return $this->render(...);
}

此例中,在處理完請求後,controller設置了一個notice flash消息並作了重定向。名字notice沒什麼意義,只是用於標識該消息。在下一個活動的模板中,下面的代碼能夠渲染這個notic消息:

Twig

{% if app.session.hasFlash('notice') %}
 <div class="flash-notice">
  {{ app.session.flash('notice') }}
 </div>
{% endif %}

PHP代碼:

<?php if ($view['session']->hasFlash('notice')): ?>
 <div class="flash-notice">
  <?php echo $view['session']->getFlash('notice') ?>
 </div>
<?php endif; ?>

這樣設計,flash消息就能夠為准確的某個請求存在了。他們一般被設計出來就是用於重定向的。

Response對象

作為一個Controller來說,唯一必須做到的是返回一個Response對象。

Response對象是一個PHP代碼對HTTP Response的抽象。
HTTP Response是一個基於文本的消息有HTTP headers和 返回給客戶端的內容組成。

//創建一個簡單的Response對象,默認狀態碼為200
$response = new Response('Hello ' .$name, 200);
//創建一個基於JSON的Response對象,狀態碼也為200
$response = new Response(json_encode(array('name'=>$name)));
$response->headers->set('content-type','application/json');

其中headers屬性是一個HeaderBag對象,內部包含許多有用的方法來讀取和改變Response的頭信息。頭名字被標准化使用Content-Type 與content-type或者content_type效果等同。

請求對象Request

除了路由占位符的值以外,如果繼承了Controller基類那麼該controller還可以訪問Request對象。

$request = $this->getRequest();
$request->isXmlHttpRequest(); // 判斷是不是Ajax請求
$request->getPreferredLanguage(array('en','fr'));
$request->query->get('page'); // 獲取$_GET 參數
$request->request->get('page'); //獲取$_POST參數

跟Response對象一樣,Request對象的頭也保存在HeaderBag對象中,可以很方便的被訪問。

總結思考:

無論何時,你創建一個頁面,你最終需要為它寫一些包含邏輯的代碼。在Symfony中,這叫一個controller, 它是一個PHP的函數,它可以為了最後返回一個Response對象給用戶可以做需要的任何事情。簡單的說,你可以選擇繼承一個Controller基類,它包含了許多執行controller通用任務的快捷方法。比如,你不想把HTML代碼寫入你的controller, 你可以使用render()方法來渲染並返回一個模板內容。

希望本文所述對大家基於Symfony框架的PHP程序設計有所幫助。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved