# MVC應用程序和Presenters
**注意:Presenters就象通常我們所說的控制器。**
從這一節開始我們將學習如何使用Nette框架創建應用程序。
這一節將學到以下幾點。
1、MVC,Nette目錄結構和*bootstrap.php*文件
2、如何使用presenter和actions(方法)。
3、如何使用templates(模板)。
3、什么是持久參數。
# Model-View-Controller (MVC)
Model-View-Controller是一種軟件體系結構,用于將應用程序代碼(presenter)與應用程序邏輯代碼(model)分離,并與用于在具有圖形用戶界面的應用程序中顯示數據(view)的代碼分離。 通過這種方法,應用程序更容易理解,它將簡化未來的開發,并使我們能夠單獨測試應用程序的每個單元。
# Model
模型是數據,它是整個應用的功能基礎。 它包含應用程序邏輯。 任何用戶操作(登錄,將貨物插入到購物車,數據庫中的值的改變)表示模型動作。 模型正在管理其內部狀態,并為外部提供穩定的接口。 通過調用此接口上的方法,我們可以讀取或更新其狀態。模型的概念表示整個層,不僅僅是一個類。
# View
View是一個正在處理顯示的應用程序層。 它通常使用模板引擎,并知道如何通過從模型獲取數據來呈現每個組件。
# Controller
控制器處理來自用戶的請求,調用相關的應用程序邏輯(Model),然后要求View圖層顯示數據。 在Nette框架中,控制器由presenters表示。
# 目錄結構
~~~
sandbox/
├── app/ ← 目錄與應用程序
│ ├── config/ ← 配置文件
│ │ ├── config.neon ← 主配置文件
│ │ └── config.local.neon
│ │
│ ├── forms/ ← 表單類
│ ├── model/ ← 模型層及其類
│ ├── presenters/ ←控制器類
│ │ ├── HomepagePresenter.php ← Homepage控制器
│ │ └── templates/ ← 模板目錄
│ │ ├── @layout.latte ←模板的共享布局
│ │ └── Homepage/ ← Homepage模板目錄
│ │ └── default.latte ← default方法模板
│ ├── router/ ← 路由器類
│ │
│ └── bootstrap.php ← 應用啟動文件
│
├── log/ ← 包含日志,錯誤等
├── temp/ ←用于臨時文件,緩存,...
│
├── vendor/ ← 目錄與庫(例如第三方組件)
│ ├── nette/ ← 全部 Nette Framework 組件
│ │ └── nette/Nette ← Nette Framework本身由Composer安裝
│ ├── ...
│ │
│ └── autoload.php ← 腳本,用于處理已安裝軟件包中所有類的自動加載
│
└── www/ ← 公共目錄,項目的文檔根
├── .htaccess ← mod_rewrite的規則
├── index.php ← 觸發應用程序
└── images/ ← images, styles,文件夾 ..
~~~
此外,在一些目錄中,有.htaccess和web.config文件,它讓瀏覽器(對于Apache或IIS)不能直接訪問。 并且你不能從您的瀏覽器直接到達app /和libs /目錄。
請不要忘記向目錄log /和temp /授予寫權限(chmod 0777)。
# index.php & bootstrap.php
瀏覽器通過一個且只有一個文件發送所有請求,這個文件就位于公共目錄**www/**中的**index.php**。 它只傳遞控制到應用程序(即app /目錄),引導文件**bootstrap.php**。
在這里目錄結構只是一個建議。 你可以很容易地將其更改為任何你想要的目錄。 所以你需要做的是在bootstrap.php中重命名目錄和更改路徑。現在我們去了解一下bootstrap.php文件工作程序。
首先,文件加載Nette框架和庫:
~~~
require __DIR__ . '/../vendor/autoload.php';
~~~
Configurator配置創建所謂的DI上下文并處理應用程序初始化(DI配置后面會說到)。
~~~
$configurator = new Nette\Configurator;
~~~
在嚴格模式下激活調試器和日記記錄器:
~~~
//$configurator->setDebugMode(TRUE);
$configurator->enableDebugger(__DIR__ . '/../log');
~~~
設置臨時文件的目錄
~~~
$configurator->setTempDirectory(__DIR__ . '/../temp');
~~~
激活自動加載,將自動加載所有文件與相關類:
~~~
$configurator->createRobotLoader()
->addDirectory(__DIR__)
->addDirectory(__DIR__ . '/../vendor/others')
->register();
~~~
并根據配置文件生成一個DI容器。 一切都取決于它
作為調用app / bootstrap.php的結果,我們將返回這個DI容器。
~~~
$configurator->addConfig(__DIR__ . '/config/config.neon');
$configurator->addConfig(__DIR__ . '/config/config.local.neon');
return $configurator->createContainer();
~~~
我們將它作為局部變量存儲在www/index.php中并運行應用程序:
~~~
$container = require __DIR__ . '/../app/bootstrap.php';
$container->getService('application')->run();
~~~
以上就是bootstrap.php和index.php工作方式。
# 處理presenter和方法。
那么如何處理程序請求呢? 每一個對我們應用程序的請求都將通過index.php和bootstrap.php傳遞給$application對象。但是這個對象不理解http請求,所以它會要求路由器翻譯請求。路由器將查看請求,并告訴需要什么樣presenter ,以及應該執行什么樣action。
例如,路由器回應,用戶想要presenter里Product類方法為show(這是一個好習慣,就像Product:show),并傳遞參數id = 123。
這是最終可以理解的$application,它將繼續完成請求。它將創建一個ProductPresenter類的對象。表示Product控制器。 (為了完全準確,應用程序要求presenterFactory服務創建presenter)。 此新控制器將被要求執行操作(與參數id一起顯示)。
控制器是接收請求,路由器翻譯并計算給答案的對象。 這可以是HTML頁面,圖像,XML文檔,文件,JSON,重定向或任何您需要的。 具體來說,ProductPresenter將查詢模型的數據,并將該數據傳遞給模板進行顯示。 這通常在方法renderShow中完成,其中單詞Show必須匹配操作的名稱,并且請求參數id將作為參數傳遞給方法:
~~~
class ProductPresenter extends Nette\Application\UI\Presenter
{
public function renderShow($id)
{
// 我們將從模型中獲取數據并將其傳遞給模板
$this->template->product = $this->model->getProduct($id);
}
}
~~~
如果你真的想要,你可以通過調用$ this-> request-> getParameters()獲得所有GET請求參數的數組,但通常你不需要它們,而是使用路由和動作參數。
同樣,你可以通過調用$ this-> request-> getPost()來獲取所有接收到的POST參數。 通常,你也不需要這個方法。 大多數情況下,你正在處理表單請求,并且有一個表單組件。
然后控制器將渲染模板。
~~~
停~~!可能大家看不明白上面的意思,他實際要做一個實例,想要顯示id為123的產品,怎么做呢?我們先要新建一個類:ProductPresenter,這個分類有一個方法,用來顯示用戶指定ID的產品。所以就要這樣一個方法public function renderShow($id)。明白了吧。意思教你怎么新建控制器。
~~~

**跟我們一步一步來**
我們想要新建一個product控制器來管理商品,所以我們就要在presenters文件夾下面新建一個ProductPresenters.php如下面圖

他的代碼如下。
~~~
<?php
namespace App\Presenters;
use Nette;
use App\Model;
class ProductPresenter extends BasePresenter
{
public function renderShow()
{
}
}
~~~
在里面我們新建一個show方法。但是里面是空的。現在我們先不要管他,我們打開URL
~~~
http://localhost/Nette/sandbox/www/product/show
~~~
從上面URL大家明白這個URL怎么產生了吧!以后路由內容會詳細說明。
現在我們打開后發現出錯,如下圖

怎么會出錯呢?可能細心的人看到后面提示了。沒有show.latte.這就是模板了,好的,我們照他的路徑新建show.latte.里面代碼是空。
我們再次刷新。發現不再出錯了,但是是空白,所以我們再模板填入幾個字。隨意了。再次刷新就出現填入的文字了。
好的,模板方法也明白了嗎?!
好的,如果我們要在控制里做一個變量。傳到模板里顯示呢。很簡單,我們先在ProductPresenter里的show方法加入
~~~
<?php
//?presenter=Product&action=detail&id=123
namespace App\Presenters;
use Nette;
use App\Model;
class ProductPresenter extends BasePresenter
{
public function renderShow()
{
$this->template->sp = '筆記本';
}
}
~~~
我們在show.latte里加入
~~~
{$sp}
~~~
再次刷新

看,顯示變量了,好的,我們跟著教程走,下面就是模板內容了!
# 模板
控制器將從一些簡單的規則產生模板文件的路徑。 還要求進一步顯示Product控制器和show方法,如果這些文件中的一個存在就不會出現錯誤:
~~~
- templates/Product/show.latte
- templates/Product.show.latte
~~~
來解說一下,我們剛才是不是新建了一個ProductPresenter控制器,記得我們只取Presenter前面字母也就是Product為路徑,那他是不是有一個方法renderShow,這個方法我們就取render后面的字母為文件名,所以就產生了相對映模板就是templates/Product/show.latte
記下這個了!我們小實例也說明了這些內容。
除了以上找模板之外控制器將嘗試搜索布局(這是可選的):
這個一般很少用。
~~~
- templates/Product/@layout.latte
- templates/Product.@layout.latte
- templates/@layout.latte layout shared by multiple presenters
~~~
控制器及其組件馬上向模板傳遞了一些有用的變量:
這些變量是公共變量,如CSS或JS路徑,文件路徑。這些大家一定要記下來!
~~~
$basePath 是到根dir的絕對URL路徑(例如/ CD-collection)
$baseUrl 是到root dir的絕對URL(例如http:// localhost / CD-collection)
$user 是表示用戶的對象
$presenter 是當前的控制器
$control是當前組件或控制器
$flashes 方法發送的消息列表flashMessage()
~~~
小總結,從上面這一小節看出來Nette框架創建應用程序其實不是很難! 他的創建過程就是當您請求方法時,例如:Homepage:default,然后
1、將創建類HomepagePresenter的對象
2、renderDefault()方法將被調用(如果它存在)
3、模板,例如templates / Homepage / default.latte與布局(例如templates/@layout.latte)將一起組合渲染視圖。
可是可能大家發現照教程操作會發現出錯。過不去,現在我來說一下,
大家跟我一步一步來:
我們新建ProductPresenter.php控制器,代碼如下
~~~
<?php
namespace App\Presenters;
use Nette;
use App\Model;
class ProductPresenter extends Nette\Application\UI\Presenter
{
public function renderShow($id)
{
$this->template->productId = 15;
//原來教程代碼$this->template->product = $this->model->getProduct($id);現在我們修改成以上代碼。
//因為我們還沒有學到模型。
//一般這個productId是數組,現在為了讓大家能看明白我直接給一個數值上去。
}
}
~~~
然后在show.latte里加入以下代碼
~~~
<a n:href="Product:show $productId">product detail</a>
~~~
現在可以刷新網頁

我們查一下product detail連接,
~~~
http://localhost/Nette/sandbox/www/product/show?id=15
~~~
從這里大家可能明白了吧。
現在看起來在Nette Framework中創建應用程序將是象玩孩子的游戲這么容易。
# 模塊
有時我們要開發復雜的應用程序時,我們可以將控制器和模板的目錄分為子目錄。 我們稱之為模塊。 就象網站有后臺模塊和前臺模塊之分。
如果我們的應用程序將包含例如模塊Front和Admin,其結構就象如下所示:
~~~
sandbox/
app/ ← 目錄與應用程序
AdminModule/ ← Admin 模塊
presenters/ ← 控制器
templates/ ← 視圖模板
FrontModule/ ← Front 模塊
presenters/ ← 控制器
templates/ ← 視圖模板
...
~~~
模塊不必以平面結構排列,您甚至可以創建子模塊。
象上面例子中的ProductPresenter.php如果他存在Front模塊中。那么相對show方法就要修改**Front:Product:show**和** ProductPresenter**將放在**FrontModule**命名空間中:
~~~
namespace FrontModule;
//FrontModule代表路徑。
class ProductPresenter extends \Nette\Application\UI\Presenter
{
// ...
}
~~~
# 創建鏈接
鏈接的創建屬于Nette框架的最強的功能。 由于雙向路由,您不必對您的網址進行硬編碼或輕松組合。 你可以只引用控制器的方法,并傳遞他們的參數,框架將自己生成URL。 創建鏈接與調用函數一樣簡單。
當編程和編碼模板時,我們不必關心URL設計,我們將直接參考控制器的方法,例如上面已經提到的Product:show。
這點上面實例也說明白了。
# 模板中的鏈接
通常我們在模板中創建鏈接。 為了使它盡可能容易,框架提供了三個宏。 最聰明的是**n:href**
~~~
<a n:href="Product:show $productId">product detail</a>
~~~
注意,我們使用n:href代替HTML屬性href。 它的值不是一個URL,就像你習慣使用href屬性一樣,是一個控制器的方法。 語法是
~~~
[Presenter:]action [,] [arg1] [, arg2] [, ...]
~~~
點擊鏈接后,方法ProductPresenter :: renderShow()將獲得它的單詞,作為參數$ id將獲得$ productId的值。 我們可以以同樣的方式傳遞更多的參數,就像我們調用一個方法一樣。 它會變得更容易嗎?
除此之外,甚至可以傳遞命名參數。 下一個鏈接傳遞具有值cs的參數lang:
~~~
<a n:href="Product:show $productId, lang => cs">product detail</a>
~~~
雖然方法renderShow在其聲明中沒有$ lang,它可以通過調用$ lang = $ this-> getParameter('lang')讀取此參數的值。
如果我們有一個數組中的所有參數,我們可以用(expand)運算符展開它們:
~~~
{var $args = [$productId, lang => cs]}
<a n:href="Product:show (expand) $args">product detail</a>
~~~
如果我們正在創建鏈接的模板是Product Presenter的一部分,我們可以省略控制器的名字,直接寫n:href =“show $ productId”。 類似地,如果一個鏈接指向一個名為default的動作,你可以跳過它,并寫n:href =“Product:$ id”(不要忘記冒號)。
鏈接甚至可以引用其他模塊。 這里我們這樣區分,如果它“相對”地指向子模塊,或者“絕對地”指向不同的模塊,則路徑以冒號開頭。 讓我們假設實際的presenter是模塊Front的一部分,然后我們將寫:
~~~
<a n:href="Shop:Product:show">link for Front:Shop:Product:show</a>
<a n:href=":Admin:Product:show">link for Admin:Product:show</a>
~~~
生成的鏈接采用絕對路徑格式。 當您想要生成包含域的絕對鏈接(例如http://example.com)時,只需在開頭n:href =“// show $ productId”處提供兩個斜杠。 如果我們將presenter中的屬性$ absoluteUrls設置為TRUE,默認情況下所有的鏈接都是絕對的。
我們可以使用所謂的片段或錨來引用頁面上的特定部分,使用#符號:
~~~
<a n:href="show#comments">link to Product:show and fragment #comments</a>
~~~
如果我們創建一個HTML標簽`<a>`,宏n:href非常方便。 當我們想在其他地方有這個鏈接時,例如在模板的文本中,我們可以使用具有相同內部語法的{link}宏:
~~~
The address is: {link Product:show $productId}
~~~
{ifCurrent $ link} ... {/ ifCurrent}是一個條件語句。 如果鏈接引用當前頁,則執行標簽內的塊; 否則丟棄。 典型的用例是將CSS類添加到活動鏈接。
~~~
<!-- use cases -->
<a href="{link edit, 10}">edit</a>
<ul class="menu">
<li><a href="{link Default:default}">...</a></li>
<li><a href="{link}">...</a></li>
...
<li {ifCurrent Default:default}class="active"{/ifCurrent}><a href="{link Default:default}">...</a></li>
<!-- scope expanding -->
<li {ifCurrent Default:*}class="active"{/ifCurrent}><a href="{link Default:default}">...</a></li>
</ul>
<!-- negation -->
{ifCurrent Admin:login}{else}<a href="{link Admin:login}">Sign in!</a>{/ifCurrent}
~~~
[有關Latte模板語法](https://latte.nette.org/en/macros)
不過有了前面實例說明,我想大家認真讀起來很易理解鏈接怎樣產生。
# 在控制器中的鏈接
控制器和組件有方法鏈接,可以在模板中創建鏈接對象。 第一個參數是presenters目標操作,后面是傳遞的參數:
~~~
$url = $this->link(destination [,arg [,arg ...]]);
~~~
它們也可以使用數組傳遞。 例如:
~~~
$url = $this->link('Product:show', $productId);
$url = $this->link('Product:show', [$productId, 'lang' => 'en']);
~~~
如果在生成鏈接時傳遞FALSE作為方法參數,它將不會被添加到鏈接。 解決方案是使用默認值TRUE或FALSE定義此參數。 Nette然后將理解它的類型是布爾型,它將作為1或0傳遞到url,并在控制器中處理時轉換回布爾值。
# 無效鏈接
有時發生的一種情況是,我們要創建一個無效的鏈接 - 或者是因為它引用了一個不存在的控制器或者方法和參數,或者當目標方法不能生成一個URL時 。 如何由靜態變量Presenter :: $ invalidLinkMode處理無效鏈接確定。 它可以有這些值(常數)之一:
* Presenter :: INVALID_LINK_SILENT - 靜默模式,以符號#作為網址返回
* presenter:: INVALID_LINK_WARNING - 將生成E_USER_WARNING
* Presenter::INVALID_LINK_TEXTUAL –視覺警告,錯誤文本顯示在鏈接中
* Presenter::INVALID_LINK_EXCEPTION –將拋出InvalidLinkException
生產模式中的默認設置為INVALID_LINK_WARNING,在開發模式下為INVALID_LINK_WARNING | INVALID_LINK_TEXTUAL。 INVALID_LINK_WARNING不會在生產環境中終止腳本,但會記錄該警告。 在開發環境中,Tracy將攔截警告并顯示錯誤藍屏。 如果設置了INVALID_LINK_TEXTUAL,控制器和組件會返回錯誤消息,網址為#error:。 為了使這樣的鏈接可見,我們可以添加一個CSS規則到我們的樣式表:
~~~
a[href^="#error:"] {
background: red;
color: white;
}
~~~
如果我們不想在開發環境中生成警告,我們可以在config.neon配置中打開靜默無效鏈接模式。
~~~
application:
silentLinks: yes
~~~
# 持久參數
除了經典參數,我們現在知道,有所謂的持久參數。 這些是非常重要的方式不同:它們通過請求自動傳遞。 這意味著,我們不必在鏈接中明確提及它們,但它們仍然被傳遞。
當您的應用程序具有多種語言變體時,在每個鏈接中傳遞實際語言將是令人難以置信的疲勞。你可以簡單地將$ lang參數標記為persistent,如下所示:
~~~
class ProductPresenter extends Presenter
{
/** @persistent */
public $lang;
...
~~~
如果參數$ lang的實際值為“en”,則進入鏈接
~~~
<a n:href="Product:show $productId">product detail</a>
~~~
參數lang => en將被自動傳遞。但是我們也可以添加參數lang改變它的值:
~~~
<a n:href="Product:show $productId, lang => cs">detail in Czech language</a>
~~~
嗯。。學過對象一看就明白了。如上面這個$lang他就是ProductPresenter類的公共屬性,所以他下面的方法都會自動自聯到$lang這個變量數值。
該參數將在ProductPresenter的對象中的類變量$ lang中訪問,并且可以通過$ this-> lang訪問。 我們甚至可以將persistent參數的默認值設置為presenter類。 如果參數值將對應于默認值,則不會在URL中傳遞。
持久變量必須聲明為public。
持久性反映了控制器類的層次結構,因此在特定控制器中定義的參數將在從其繼承的每個控制器中被自動考慮。
#控制器的生命周期
我們現在知道,控制器方法導致調用render <Action>方法,因此例如renderShow。 它的生命周期如下圖一樣。

# startup()
方法startup()在創建控制器之后立即被調用。 它初始化變量或檢查用戶權限。
如果你編寫自己的startup()方法,不要忘了調用其父parent :: startup()。
# action<Action>()
類似于方法render<View>()。 有些情況下,控制器執行一些特定的操作(驗證用戶,寫入數據庫),然后重定向到其他地方。 調用render渲染是不合適的,因為沒有渲染。 正因為如此,有一個被稱為action的替代。
重要的是要知道方法action <Action>()在render <View>()之前被調用。 它甚至可以決定更改什么render方法被調用,通過調用$ this-> setView('otherView')(renderOtherView()將被調用)。
# handle<Signal>()
方法處理所謂的信號aka子請求。 主要用于組件和處理AJAX請求。 表格的處理也在這個級別上執行。
# beforeRender()
方法beforeRender,顧名思義,在render <View>()之前被調用,它可以包含例如模板的設置,將變量傳遞給模板common以獲得更多的視圖等等。
# render<View>()
通常將所需數據傳遞到模板
# shutdown()
在控制器生命周期結束時調用。
# 終止控制器
我們還可以在其生命周期的任何時候終止控制器。 我們通常這樣做是為了防止渲染模板。
* 直接通過方法終止控制器:$presenter->terminate()
* 終止呈現者并立即呈現模板:$presenter->sendTemplate()
* 終止控制器和調度有效載荷:$presenter->sendPayload()(在 AJAX里)
* 終止控制器和發送自己的響應:$presenter->sendResponse($response)
控制器BadRequestException也可以通過重定向或通過投擲來終止
以上內容也很少用到。一般處理是重定向。
# 重定向
有兩種方法redirect()和forward()重定向另一個控制器。其具有類似于所提及的語法link()例如,在提交表單和寫入數據庫后,我們將通過調用重定向到產品的詳細信息:
~~~
$this->redirect('Product:show', $id);
~~~
forward()將傳遞到新的方法沒有重定向,redirect()將重定向瀏覽器與HTTP代碼302或303.如果我們想使用不同的代碼,我們將它放在控制器的方法的名稱之前 :
~~~
$this->redirect(301, 'Product:show', $id);
~~~
要轉到應用程序之外的網址,您可以使用redirectUrl()進行重定向:
~~~
$this->redirectUrl('https://nette.org');
~~~
重定向通過拋出所謂的終止異常Nette \ Application \ AbortException立即終止控制器的活動。
有時,在重定向之前,我們想發送一個所謂的彈出消息。 這個消息,將在模板中重定向后出現。
# 錯誤404
當我們無法滿足請求時,因為例如數據庫中的記錄丟失,我們將拋出一個異常Nette \ Application \ BadRequestException,它代表HTTP錯誤404:
~~~
public function renderShow($id)
{
$product = $this->model->getProduct($id);
if (!$product) {
throw new Nette\Application\BadRequestException;
}
// ...
}
~~~
當用戶沒有被授權查看頁面時,我們將拋出一個Nette \ Application \ ForbiddenRequestException異常(錯誤403)。 可以使用Nette \ Application \ BadRequestException的第二個參數返回其他錯誤代碼。
# 彈出信息
這些是通知某些操作的結果的消息。 彈出消息的一個重要功能是,即使在重定向后,它們也可以在模板中使用。 即使顯示,它們可存在三秒鐘 ,如果用戶無意中刷新頁面,消息不會丟失。
只需調用flashMessage()方法,控制器將消息傳遞到模板。 第一個參數是消息的文本,第二個可選參數是其類型(錯誤,警告,信息等)。 方法flashMessage()返回一個彈出消息的實例,以允許我們添加更多信息。
~~~
public function deleteFormSubmitted($form)
{
// ... 模型要求刪除記錄 ...
//刪除成功
// 我們將傳遞彈出信息
$this->flashMessage('Item was removed.');
$this->redirect(...); // 自動跳轉
}
~~~
這些消息在模板變量$ flashes中可用作匿名對象,包含消息和類型屬性,甚至可以包含前面提到的用戶定義的信息。 它們可以像這樣渲染:
~~~
{foreach $flashes as $flash}
<div class="flash {$flash->type}">{$flash->message}</div>
{/foreach}
~~~
# 模型類的用法
正如我們之前所說的,模型是一個由許多類組成的整個層,每個類都處理應用程序邏輯的一部分。 如果我們有一個模型App \ Articles,它將負責加載和保存文章,我們可能會在使用依賴注入的控制器中要求它,假設我們已經在DI Container中正確注冊為服務。
~~~
class ArticlePresenter extends Presenter
{
/** @var \App\Articles @inject */
public $articles;
public function renderShow($id)
{
$this->template->article = $this->articles->find($id);
}
}
~~~
剛剛發生了什么? 讓我們一起去。 正如我們前面所說的,PresenterFactory創建了PresenterFactory的實例,并且這個類在創建之后做了什么,它檢查Presenter是否有一些公共屬性,用@inject注釋,如果是這樣,它試圖找到一個 服務,它實現或是類型的實例,即在@var中,并將此服務從DI Container傳遞到屬性。
由于這種機制,我們可以立即使用服務,不用關心它來自或試圖實例化它,因為我們確信它將被傳遞到屬性,只要它正確地注冊在DI容器。
你只能注入類的公共屬性。
# 控制器與組件
當我們談論控制器時,在組件這一術語下,我們通常指的是Control類的后代。 更準確的是使用術語“控制器”,
**Nette\Application\UI\Presenter**本身,是Control類的后代,因此組件和控制器之間存在很大的相似性。 但是UI \ Control(甚至UI \ Presenter)主要是一個所謂的組件容器。 這意味著你可以添加到其他組件。 就像我們添加到表單輸入(文本字段,按鈕,...)。你可以通過括號訪問它們:
~~~
// 將組件附加到控制器中
$presenter['mymenu'] = new MenuControl;
// 從控制器中獲取組件并呈現它
$presenter['mymenu']->render();
~~~
將組件附加到控制器(綁定它們),您將能夠:
* 在組件中創建鏈接
*
使用 [signals](https://doc.nette.org/en/2.4/components#toc-signal-aka-subrequest)
* 在組件中使用持久性參數
當您不需要這些功能時,您就不必綁定組件與控制器。
# 組件工廠
組件工廠是一個用來創建組件優雅的方式,只有真的需要(惰性/按需)組件。就執行createComponent<Name>()方法調用,<Name>是組件的名稱,將創建和返回所需的組件。 然后將該組件附加到控制器中。createComponent<Name>()方法是可選的傳遞$name的組件,正在創建,作為參數。
~~~
class DefaultPresenter extends Nette\Application\UI\Presenter
{
public function renderDefault()
{
$menu = $this['menu']; // 訪問組件
// 如果這是第一次訪問,方法createComponentMenu()將被調用
// ...
}
protected function createComponentMenu()
{
// 創建和配置組件
$menu = new MenuControl;
$menu->items = $this->item;
// 和運行它
return $menu;
}
}
~~~
組件名稱的第一個字母始終為小寫,盡管工廠名稱中的第一個字母為大寫。
因為所有組件都是在單獨的方法中創建的,所以代碼更干凈,更容易閱讀。
我們從來不直接調用工廠,當我們第一次使用組件時,它們被自動調用。 由于這樣,一個組件在適當的時刻創建,并且只有當它真的需要時。 如果我們不使用組件(例如在一些AJAX請求,我們只返回部分頁面,或者部分緩存),它甚至不會被創建,我們保存服務器的性能。
您可以使用宏{control}訪問和呈現組件。 因此,不需要手動地將組件傳遞到模板。
~~~
<h2>Edit form</h2>
{control editForm}
~~~
您可以在單獨的頁面上閱讀有關組件的[更多詳細信息](https://doc.nette.org/en/2.4/components)。
# 持久組件
不僅參數,而且組件都可以持久化。 它們的狀態然后在跳到另一個控制器時傳遞,就像持久參數一樣。 我們用這個注釋標記持久化組件(這里我們標記組件日歷和菜單):
~~~
/**
* @persistent(calendar, menu)
*/
class DefaultPresenter extends Presenter
{
// ...
}
~~~
你不必將子組件標記為持久化,它們會自動持久化。
# 在哪里可以得到一些組件?
在頁面加載項,插件和組件上,您可以找到由Nette Framework社區制作和共享的一些[開源組件](https://addons.nette.org/)。
# errorPresenter
默認值:Nette:Error
此參數指定在發生錯誤時調用的Presenter。 我們可以在 sandbox中找到一個例子。
如果在執行錯誤提示器時發生錯誤,Tracy將僅顯示一個簡單的錯誤頁面。
# catchExceptions
默認值:在生產模式下為TRUE,在調試模式下為FALSE
此參數決定是否調用錯誤提示程序。 通過在調試模式下將該值更改為TRUE,我們可以測試錯誤提示器的功能。
# mapping
默認值:*:* Module \ * Presenter(在 sandbox:* App * * Module \ Presenters \ * Presenter)
此參數包含具有將控制器名稱轉換為類名稱的規則的數組。 在鍵中,我們定義一個模塊名稱或*作為其他模塊的回退。 該值包含規則掩碼:
~~~
application:
mapping:
Admin: App\Admin\*Module\*Presenter
*: App\*Module\Presenters\*Presenter
~~~
控制器Front:Blog:文章包含在Front模塊內。 由于此模塊沒有顯式映射,將使用帶星號的映射,類將被稱為App \ FrontModule \ BlogModule \ Presenters \ ArticlePresenter。 控制器管理:用戶:編輯是管理模塊的一部分,它有單獨的映射,所以控制器類的名稱將是App \ Admin \ UserModule \ EditPresenter(請注意,由于管理員已經在映射定義,它將被剝離 從結果類名)。
# silentLinks
默認值:FALSE
當鏈接生成失敗時,此選項指定調試模式下的Nette行為。 使用FALSE值Nette將觸發E_USER_WARNING。 將此配置選項更改為TRUE將抑制此錯誤。 在生產環境中,將始終觸發E_USER_WARNING。
我們還可以使用類屬性Presenter :: $ invalidLinkMode來調整此行為。
# scan*
Nette允許自動注冊控制器作為服務。 此步驟提高了控制器創建速度。 有幾個配置選項可以調整Nette在哪里和如何查找它們。
# scanDirs
默認值: %appDir% (/app)
此值包含一個包含目錄的數組,Nette查找控制器。 如果我們在配置中指定另一個值,它將與默認值合并:
~~~
application:
scanDirs:
- foo
~~~
在這種情況下,scanDirs將包含值%appDir%和foo。 如果我們要禁用或覆蓋此配置選項,我們需要使用! 修飾語:
~~~
application:
scanDirs!:
- foo
~~~
現在scanDirs只包含foo。
# scanComposer
默認值:TRUE
此參數指定Nette是否應在Composer類映射中查找控制器。
# scanFilter
默認值:Presenter
指定必須包含在文件和類名稱中的字符串。
- Nette簡介
- 快速開始
- 入門
- 主頁
- 顯示文章詳細頁
- 文章評論
- 創建和編輯帖子
- 權限驗證
- 程序員指南
- MVC應用程序和控制器
- URL路由
- Tracy - PHP調試器
- 調試器擴展
- 增強PHP語言
- HTTP請求和響應
- 數據庫
- 數據庫:ActiveRow
- 數據庫和表
- Sessions
- 用戶授權和權限
- 配置
- 依賴注入
- 獲取依賴關系
- DI容器擴展
- 組件
- 字符串處理
- 數組處理
- HTML元素
- 使用URL
- 表單
- 驗證器
- 模板
- AJAX & Snippets
- 發送電子郵件
- 圖像操作
- 緩存
- 本土化
- Nette Tester - 單元測試
- 與Travis CI的持續集成
- 分頁
- 自動加載
- 文件搜索:Finder
- 原子操作