CakePHP 2.x CookBook 中文版 第六章 視圖

 

視圖

視圖是 MVC 中的 V。 視圖負責爲請求生成指定的輸出。常用的格式有 HTML、XML 或者 JSON,不過用戶可以下載的流文件和 PDF 文件也可以由視圖層提供。

CakePHP 爲多數常用的渲染情況提供了一些內置的視圖類: :

  • 可以使用 JSON 和 XML 視圖. 建立 XML 或者 JSON webservices
  • 可以使用 媒體視圖 提供受保護的文件,或者動態生成文件
  • 可以使用 主題 生成多主題視圖

視圖模板

CakePHP 的視圖層決定你如何向用戶展示。 多數時候,視圖在瀏覽器中顯示 (X)HTML 文檔,也可能會需要給 Flash 對象提供 AMF 數據,以 SOAP 形式回覆遠程應用程序,或者爲用戶輸出一個 CSV 文件。

CakePHP 的默認視圖文件是以 .ctp(CakePHP TempLate)爲擴展名的純 PHP 文件。這些文件包含了所有的從控制器獲取的準備以某種格式提供給你的觀衆的數據的呈獻邏輯。如果你喜歡使用類似 Twig 或者 Smarty 這樣的模板語言,可以使用 View 的一個子類來橋接 CakePHP 和你的模板語言。

視圖存儲在 /app/View/ ,所有文件都放在以控制器命名的子文件夾中,文件名則是以動作命名的。例如,Products 控制器的 “view()” 動作的視圖通常是 /app/View/Products/view.ctp

CakePHP 的視圖層可以由不同的部分構成。每一部分有不同的用途,將在下面的章節中介紹:

  • views:視圖是動作運行的唯一的頁面部分。它們構成了應用程序的響應。
  • elements:小的可重用的視圖代碼。元件通常在視圖內部渲染。
  • layouts: 應用程序中打包了呈獻邏輯的一些視圖接口文件。多數視圖在佈局內部渲染。
  • helpers:這些類包裝了視圖層的許多地方都使用的視圖邏輯。除了其它事項,CakePHP 的助手幫助你建立表單、構建 AJAX 功能、分頁模型數據,或者提供 RSS feed。

繼承視圖

2.1 新版功能.

視圖繼承允許你在一個視圖中包含另一個視圖。使用 視圖塊 進行綁定提供了保持視圖 DRY 的強勁手段。例如,應用程序中有一個邊欄,它在特定的視圖中需要呈獻不同的內容。通過繼承一個共用的視圖文件,你無需重複邊欄中共有的標記,只需要定義改變的部分:

// app/View/Common/view.ctp
<h1><?php echo $this->fetch('title'); ?></h1>
<?php echo $this->fetch('content'); ?>

<div class="actions">
    <h3>Related actions</h3>
    <ul>
<?php echo $this->fetch('sidebar'); ?>
    </ul>
</div>

上面的視圖文件可以作爲父視圖。它需要繼承它的視圖定義 sidebar 和 title 塊。它將包含繼承視圖中所有未捕獲的內容。假設我們的視圖文件有一個帶有帖子數據的 $posts 變量,我們的視圖看起來將是這個樣子的:

<?php
// app/View/Posts/view.ctp
$this->extend('/Common/view');

$this->assign('title', $post);

$this->start('sidebar');
?>
<li>
<?php
echo $this->Html->link('edit', array(
    'action' => 'edit',
    $post['Post']['id']
)); ?>
</li>
<?php $this->end(); ?>

// 剩餘的內容將以父視圖中的 'content' 塊的形式生效
<?php echo h($post['Post']['body']);

上面顯示的這個帖子視圖展示瞭如何繼承一個視圖,並且植入一個塊集。塊中未定義的內容將被捕捉並放入一個叫做content 的特殊塊。當一個視圖包含一個 extend() 調用,其將一直執行到當前文件的結尾。一旦它完成,被繼承的視圖被渲染。一個視圖調用 extend() 超過一次, 將用後一個父視圖覆蓋前一個父視圖:

$this->extend('/Common/view');
$this->extend('/Common/index');

上面的代碼中, `` /Common/index.ctp`` 將作爲當前視圖的父視圖。

必要時,你可以嵌套繼承視圖。如果需要,每個視圖都能被另一個視圖繼承。每個父視圖將獲得前一個視圖的內容,當作自己的 content 塊。

註解

你應該在應用程序中避免使用 content 作爲塊名。CakePHP 用它作爲繼承視圖中未捕獲的內容塊。

使用視圖塊

2.1 新版功能.

視圖塊放在 $scripts_for_layout,並提供一個允許你在視圖/佈局中任意位置定義插槽或者塊的靈活的 API。 塊是實現類似邊欄這樣的東東的理想方法,或者是在佈局的頭/尾加載資源的好地方。塊有兩種定義方式:作爲捕獲塊,或者通過直接賦值。start()、 append() 和 end() 方法是和捕獲塊一同工作的:

// 建立一個邊欄塊
$this->start('sidebar');
echo $this->element('sidebar/recent_topics');
echo $this->element('sidebar/recent_comments');
$this->end();


// 隨後添加一個邊欄
$this->append('sidebar');
echo $this->element('sidebar/popular_topics');
$this->end();

也可以多次使用 start() 添加進一個塊。 任何時候都可以使用 assign() 清除或者覆蓋一個塊:

// 清除之前定義的邊欄塊的內容。
$this->assign('sidebar', '');

在 2.3 版本中,新加了幾個與塊一同工作的方法。prepend() 預置一個已存在的塊的內容:

// 預置到邊欄。
$this->prepend('sidebar', 'this content goes on top of sidebar');

startIfEmpty() 方法在一個塊爲空或者未定義時生成一個塊。如果塊已經存在,則 startIfEmpty() 定義的內容被忽略。當你想要在塊不存在時爲其定義默認內容時,可以使用這一方法::

// 在視圖文件中。
// 創建一個導航欄塊。
$this->startIfEmpty('navbar');
echo $this->element('navbar');
echo $this->element('notifications');
$this->end();

// 在父視圖/佈局中。
$this->startIfEmpty('navbar');
Default content
$this->end();

echo $this->fetch('navbar');

上面的例子中,navbar 塊包含在第一部分中添加的內容。一旦在子視圖中定義了這個塊,其默認內容將被忽略。

註解

應該避免使用 content 作塊名。它被用於 CakePHP 的內部視圖和繼承視圖,以及佈局視圖內容中。

顯示塊

2.1 新版功能.

可以使用 fetch() 方法顯示塊。 fetch 將安全地輸出一個塊,如果塊不存在,就返回 ‘’。

echo $this->fetch('sidebar');

還可以根據一個塊是否存在來決定是否顯示其內容。要想在佈局、繼承視圖文件中有條件的顯示頭或者其它標籤時,這種方法非常有用:

// 在 app/View/Layouts/default.ctp 中
<?php if ($this->fetch('menu')): ?>
<div class="menu">
    <h3>Menu options</h3>
<?php echo $this->fetch('menu'); ?>
</div>
<?php endif; ?>

在 2.3.0 版,還可以在塊沒有內容時爲其提供默認值。這使爲空狀態添加佔位符變得更容易。可以使用兩個參數提供默認值:

<div class="shopping-cart">
    <h3>Your Cart</h3>
<?php echo $this->fetch('cart', 'Your cart is empty');
</div>

在 2.3 版更改: $default 添加於 2.3 版。

使用 script 和 CSS 文件塊

2.1 新版功能.

塊替代了被廢棄的 $scripts_for_layout 佈局變量。HtmlHelper 關聯到視圖塊,它的 script() 、 css() 和 meta()方法在與 inline = false 選項共同使用時使用相同的相同的名字更新一個塊。

<?php
// 在視圖文件中。
$this->Html->script('carousel', array('inline' => false));
$this->Html->css('carousel', null, array('inline' => false));
?>

// 在佈局文件中。
<!DOCTYPE html>
<html lang="en">
    <head>
    <title><?php echo $this->fetch('title'); ?></title>
<?php echo $this->fetch('script'); ?>
<?php echo $this->fetch('css'); ?>
    </head>
    // 下面是剩餘的佈局尊容...

HtmlHelper 還允許你控制使用哪個 scripts 和 CSS 塊:

// 在視圖文件中。
$this->Html->script('carousel', array('block' => 'scriptBottom'));

// 在佈局文件中。
echo $this->fetch('scriptBottom');

佈局

佈局包含圍繞視圖的展示代碼。想要在視圖中看到的所有內容都將被放在佈局中。

佈局文件放在 /app/View/Layouts 中。CakePHP 的默認佈局可以通過在 /app/View/Layouts/default.ctp 中建立一個新的默認佈局來覆蓋。 一旦一個新的默認佈局被建立,當頁被渲染時,控制器渲染的視圖代碼就被放在默認視圖中。

當你創建一個佈局,你需要告訴 CakePHP 你的視圖代碼放在哪兒了。要做到這一點,確保你的佈局中包含了 $this->fetch('content')。下面是一個默認佈局的示例:

<!DOCTYPE html>
<html lang="en">
<head>
<title><?php echo $title_for_layout?></title>
<link rel="shortcut icon" href="favicon.ico" type="p_w_picpath/x-icon">
<!-- Include external files and scripts here (See HTML helper for more info.) -->
<?php
echo $this->fetch('meta');
echo $this->fetch('css');
echo $this->fetch('script');
?>
</head>
<body>

<!-- If you'd like some sort of menu to
show up on all of your views, include it here -->
<div id="header">
    <div id="menu">...</div>
</div>

<!-- Here's where I want my views to be displayed -->
<?php echo $this->fetch('content'); ?>

<!-- Add a footer to each displayed page -->
<div id="footer">...</div>

</body>
</html>

註解

在 2.1 版之前,fetch() 方法無效。fetch('content') 是 2.0 版中 $scripts+for_layout 變量裏包含的$content_for_layout 和 fetch('meta')fetch('css')fetch('script') 行的替代品。

script、 css 和 meta 塊包含所有使用內置 HTML 助手定義在定義在視圖中的內容。用於包含來自視圖的 javascript 和 CSS 文件。

註解

當在視圖文件中使用 HtmlHelper::css() 或 HtmlHelper::script() 時,將 ‘inline’ 設置爲 ‘false’,將在一個帶有相同名字的塊中放置 html 源代碼。(更多的詳細信息和用法請參見 API)

content 塊包含渲染視圖的內容。

$title_for_layout 包含頁面標題。這個變量是自動生成的,不過你能夠通過在控制器/視圖中設置它的值來覆蓋其自動生成的默認值。

要設置佈局的標題,最簡單的辦法是在控制器中設置 $title_for_layout 變量:

class UsersController extends AppController {
    public function view_active() {
        $this->set('title_for_layout', 'View Active Users');
    }
}

也可以在視圖文件中設置 title_for_layout 變量:

$this->set('title_for_layout', $titleContent);

可以根據自己的願望建立許多佈局:把它們放進 app/View/Layouts 目錄, 然後在控制器動作中使用控制器或者視圖的$layout 屬性在不同佈局間切換:

// 通過控制器的佈局屬性
public function admin_view() {
    // stuff
    $this->layout = 'admin';
}

// 通過視圖的佈局屬性
$this->layout = 'loggedin';

舉例來說,假設我的站點包含一個小的廣告條,我可能會創建一個帶有小型廣告空間的新佈局,並把它指定爲所有的控制器方案都使用的一部分:

class UsersController extends AppController {
    public function view_active() {
        $this->set('title_for_layout', 'View Active Users');
        $this->layout = 'default_small_ad';
    }

    public function view_p_w_picpath() {
        $this->layout = 'p_w_picpath';
        //output user p_w_picpath
    }
}

除了 CakePHP 的默認佈局,CakePHP 還包含兩可以在你的應用程序中使用的核心佈局: ‘ajax’ 和 ‘flash’。 Ajax 佈局便於生成 Ajax 響應 - 它是一個空佈局(很多 ajax 調用的返回中僅僅包含那麼幾個標籤,不需要完整的渲染接口)。 Flash 佈局用於藉助 Controller::flash() 方法顯示消息。

內核中的另外三個視圖,xml,js 和 rss,是提供非 text/html 內容的簡捷方法。

使用插件中的佈局

2.1 新版功能.

如果你想使用插件中的佈局,可以用 插件語法。 例如,使用 Contacts 插件中的 contact 佈局:

class UsersController extends AppController {
    public function view_active() {
        $this->layout = 'Contacts.contact';
    }
}

元素

很多應用程序都有需要在多個頁面重複使用的處理顯示的代碼塊,有時是在佈局的不同地方。 CakePHP 能夠幫助你在站點中重用這些重複部分。這些可重用的部分被稱爲 元素。廣告、幫助、導航控制、擴展菜單、登錄表單和標註,在 CakePHP 中常常被實現爲元素。 一個元素基本上是一個能夠被包含在其它視圖、佈局,甚至其它元素中的迷你視圖。 元素可以使視圖的可讀性更好,重複的元素被放在自己的文件中。它們能幫助你在應用程序中重用內容片斷。

元素放在 /app/View/Elements/ 文件夾,使用 .ctp 作爲文件擴展名。 在視圖中使用 element 方法輸出元素:

echo $this->element('helpbox');

向元素傳遞變量

可以通過 element 方法的第二個參數向元素傳遞數據:

echo $this->element('helpbox', array(
    "helptext" => "Oh, this text is very helpful."
));

在元素文件中,所有傳遞過來的變量被當作參數數組的成員變量(與和視圖文件一起工作的控制器中的Controller::set() 方法設置的變量相同)。在上例中, /app/View/Elements/helpbox.ctp 文件可以使用 $helptext變量:

// 在 app/View/Elements/helpbox.ctp 中
echo $helptext; //輸出 "Oh, this text is very helpful."

View::element() 方法也支持元素的選項。這些選項支持 ‘緩存’ 和 ‘回調’。例如:

echo $this->element('helpbox', array(
        "helptext" => "This is passed to the element as $helptext",
        "foobar" => "This is passed to the element as $foobar",
    ),
    array(
        "cache" => "long_view", // 使用 "long_view" 緩存配置
        "callbacks" => true // 將此元素的 before/afterRender 回調設置爲 true
    )
);

元素緩存很容易通過 Cache 類實現。可以讓元素以你設置的任何緩存配置進行存儲。 這爲你決定在哪兒和如何長期存儲元素提供了極爲靈活地方法。 爲達到在應用程序中緩存同一元素的不同版本的目的,可以通過使用如下格式爲每個版本提供一個唯一的緩存鍵的方式:

$this->element('helpbox', array(), array(
        "cache" => array('config' => 'short', 'key' => 'unique value')
    )
);

可以通過使用 requestAction() 來獲得元素的全部優點。 requestAction() 函數從控制器動作中獲取視圖變量,並以數組形式返回它們。 這會使你的元素能夠以真正的 MVC 風格運行。 建立一個爲元素準備視圖數據的控制器動作,然後在 element() 的第二個參數中調用 requestAction(),自控制器向元素提供視圖變量。

要做到這一點,在控制器中加入類似於下面的 Post 例子中的內容:

class PostsController extends AppController {
    // ...
    public function index() {
        $posts = $this->paginate();
        if ($this->request->is('requested')) {
            return $posts;
        } else {
            $this->set('posts', $posts);
        }
    }
}

然後我們就能夠通過元素訪問分頁的 posts 模型。我們可以通過如下方式獲取最後五個帖子的有序列表:

<h2>Latest Posts</h2>
<?php $posts = $this->requestAction('posts/index/sort:created/direction:asc/limit:5'); ?>
<ol>
<?php foreach ($posts as $post): ?>
      <li><?php echo $post['Post']['title']; ?></li>
<?php endforeach; ?>
</ol>

緩存元素

如果你提供了緩存參數,你就可以獲得 CakePHP 視圖緩存的好處。將其設置爲 true,將會以 ‘默認’ 緩存配置緩存元素。此外,你也可以設置要使用的任意一個緩存配置。關於配置 Cache 的更多信息參見 緩存 。一個簡單的緩存元素的例子:

echo $this->element('helpbox', array(), array('cache' => true));

如果你在一個視圖中多次渲染了同一元素,只要確保每次手忙腳亂的 ‘key’ 參數的值是不同的即可。這將防止連續的調用會覆蓋前一個元素調用的緩存結果。例如:

echo $this->element(
    'helpbox',
    array('var' => $var),
    array('cache' => array('key' => 'first_use', 'config' => 'view_long')
);

echo $this->element(
    'helpbox',
    array('var' => $differenVar),
    array('cache' => array('key' => 'second_use', 'config' => 'view_long')
);

上面的例子保證了兩個元素的緩存結果是相互隔離的。 如果你想所有的元素緩存使用同一個緩存配置,你可以通過設置 View::$elementCache 給你想使用的緩存配置的方式來保存一些重複項。在什麼都不提供時,CakePHP 將使用這一配置。

從插件請求元素

2.0

要從插件載入元素,使用 plugin 選項(從 1.x 版的 data 選項中移出):

echo $this->element('helpbox', array(), array('plugin' => 'Contacts'));

2.1

如果你使用了一個插件並且希望使用其內置的元素,使用熟知的 插件語法。 如果視圖被插件 控制器/動作 渲染,插件的名字將自動添加到所有要用的元素的前面,直到另一個插件名稱出現。 如果插件內不存在這個元素,將會在主 APP 文件夾內尋找它:

echo $this->element('Contacts.helpbox');

如果視圖是插件的一部分,你可以活略插件名。例如,如果你在 Contacts 插件的 ContactsController 中工作:

echo $this->element('helpbox');
// 和
echo $this->element('Contacts.helpbox');

是等同的,其結果是渲染了同一個元素。

在 2.1 版更改: $options[plugin] 選項被廢棄,同時添加了 Plugin.element 支持。

建立你自己的視圖類

你可能需要建立自定義的新數據視圖類型的視圖類,或者嚮應用程序添加附加的自定義視圖渲染邏輯。就像多數 CakePHP 組件視圖類,有幾條約定:

  • 視圖文件要放在 App/View``文件夾中。例如 ``App/View/PdfView.php 。
  • 視圖文件必須以 View 做後綴。例如 PdfView 。
  • 在指定要渲染的視圖類名時,需要省略 View 後綴。例如 $this->viewClass = 'Pdf'; 。

你也許需要擴展 View 以使某些東西正常工作:

// 在 App/View/PdfView.php 中

App::uses('View', 'View');
class PdfView extends View {
    public function render($view = null, $layout = null) {
        // custom logic here.
    }
}

替換 render 方法會讓你對如果渲染內容有更大的控制權。

View API

class View

視圖方法在所有的視圖、元素和佈局文件中都可用。

使用 $this->method() 調用視圖方法。

View::set(string $varmixed $value)

視圖有一個與控制器對象中的 set() 方法作用相同的 set() 方法。在視圖文件中調用 set() 方法將向附後被渲染的佈局或元素中添加變量。關於使用 set() 方法的更多信息參見 控制器方法 。

可以在視圖文件中使用:

$this->set('activeMenuButton', 'posts');

之後 $activeMenuButton 變量就可以在佈局中使用了,並且它的值爲 ‘posts’ 。

View::getVar(string $var)

獲取視圖變量 $var 的值

View::getVars()

獲取當前渲染範圍內的所有有效的視圖變量的列表。返回變量名組成的數組。

View::element(string $elementPatharray $dataarray $options = array())

渲染元素或者局部視圖。更多的信息和示例參見 元素 一節。

View::uuid(string $objectmixed $url)

爲對象生成基於對象類型和 url 的唯一的非隨機的 DOM ID。這個方法常常被助手使用,爲每個元素生成唯一的 DOM ID,例如 JsHelper

$uuid = $this->uuid('form', array('controller' => 'posts', 'action' => 'index'));
//$uuid 包含 'form0425fe3bad'
View::addScript(string $namestring $content)

向內部腳本緩衝添加內容。這個緩衝在佈局中是 $scripts_for_layout。當用手需要直接向佈局添加 javascript 或者 css 時很有用。 謹記,從佈局或者佈局中的元素中添加的腳本不能補添加到 $scripts_for_layout。 此方法經常用在助手中,比如 Javascript 助手 和 Html助手 助手。

2.1 版後已移除: 被 使用視圖塊 特性取代。

View::blocks()

獲取所有已定義的塊的名字,返回結果爲所有名字構成的數組。

View::start($name)

開始捕獲視圖塊。參見 使用視圖塊 一節的示例。

2.1 新版功能.

View::end()

結束最近開放的捕捉塊。參見 使用視圖塊 一節的示例。

2.1 新版功能.

View::append($name$content)

以 $name 爲名稱向塊中追加內容。參見 使用視圖塊 一節的示例。

2.1 新版功能.

View::prepend($name$content)

以 $name 爲名稱向塊中預存內容。 參見 使用視圖塊 一節的示例。

2.3 新版功能.

View::startIfEmpty($name)

在塊爲空時開始一個塊。如果塊已經被定義,則其全部內容將被捕獲並拋棄。

2.3 新版功能.

View::assign($name$content)

爲塊賦值。這會覆蓋已存在的內容。參見 使用視圖塊 一節的示例。

2.1 新版功能.

View::fetch($name)

獲取塊的值。如果塊沒有定義,其值爲’‘(空字符串)。 參見 使用視圖塊 一節的示例。

2.1 新版功能.

View::extend($name)

繼承以 $name 命名的 視圖/元素/佈局。參見 使用視圖塊 一節的示例。

2.1 新版功能.

property View::$layout

設置包含當前視圖的佈局。

property View::$elementCache

這個緩存配置用於緩存元素。設置此屬性將改變默認的元素緩存配置。 此默認值可以使用 element 方法的 ‘cache’ 選項覆蓋。

property View::$request

CakeRequest 的實例。使用此實例方法有關當前請求的信息。

property View::$output

包含最後渲染的視圖內容(來自視圖文件或者佈局內容)。

2.1 版後已移除: 被 $view->Blocks->get('content'); 代替。

property View::$Blocks

ViewBlock 的實例。用於在視圖渲染中提供視圖塊功能。

2.1 新版功能.

關於視圖的更多內容

  • 主題
    • Theme 部件
    • 提高插件和主題部件的運行效率
  • 媒體視圖
    • 可設置的參數
  • JSON 和 XML 視圖
    • 在應用程序中允許使用數據視圖
    • 利用 serialize 鍵使用數據視圖
    • 利用視圖文件使用數據視圖
  • 助手
    • 使用和配置助手
    • 使用助手
    • 回調方法
    • 創建助手
      • 包含其它助手
      • 使用助手
    • 爲所有助手創建功能
    • 助手 API
      • 回調
    • 內核 Helpers
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章