最佳 MVC 實務 ================== 雖然大多數的網站開發工程師都已知道 模型-視圖-控制器 (MVC),但對於如何正確地使用 MVC 還是摸不著頭緒。MVC 背後的主要想法是 **程式碼的重用和關注點分離**。在這個章節中,我們將介紹一些常見的準則來如何在開發 Yii 應用程式時,也能遵循 MVC 模式。 在介紹這些準則,我們假設一個 Web 應用程式包含了許多子應用程式。例如: * 前端:一個面對一般終端用戶的公開網站; * 後端:一個包含許多用來管理應用程式功能的網站。通常只允許管理員使用; * 控制台: 一個運行在終端視窗且包含許多控制台指令的應用程式或支援整個應用程式的排程工作。 * Web 應用程式介面: 提供第三方介面來整合應用程式。 子應用程式可以是用 [模組](/doc/guide/basics.module) 的方式實作,或是一個完整的 Yii 應用程式但與其他子應用程式共享程式碼。 模型 ----- [模型](/doc/guide/basics.model) 代表與 Web 應用程式相關的資料結構,並且常常會在不同的子應用程式之間共享。例如,一個 `LoginForm` 模型可能會被前端和後端同時使用;一個 `News` 模型可能會被控制台命令、Web 應用程式介面和前/後端應用程式使用。因此,模型 * 應該包含代表特定資料的屬性; * 應該包含商業邏輯 (例如:驗證規則) 來確保資料複合設計的需求。 * 可能會包含操作資料的程式碼。例如,一個 'SearchForm' 模型,除了代表搜尋的輸入資料,也包含了一個搜尋方法來搜尋資料。 有時候,遵循上述第三條規則可能會使得模型變得肥大,因為包含太多的程式碼。甚至,如果它有許多不同的功用,會造成這個模型更難維護。例如,一個 `News` 模型可能會有一個方法叫做 `getLatestNews` 只給前端使用;它也可能包含一個方法叫做 `getDeletedNews` 只給後端使用。對於一個中、小型的應用來說,或許這樣不錯。但是對於大型的應用程式,下面要介紹的策略或許比較適合: * 定義一個 `NewsBase` 模型類別只包含一些在不同子應用程式之間分享用的程式碼 (例如:前端、後端) * 在不同的子應用程式裡,定義一個 從 `NewsBase` 擴展出來的 `News` 模型,然後把相應的程式碼放置到相應的 `News` 模型裡。 所以,拿上述的所舉的例子來說,前端的 `News` 模型就會有一個 `getLastestNews` 的方法,而後端的的 `News` 則是會有一個 `getDeletedNews` 方法。 大致上來說,模型不應該包含處理終端使用者的邏輯。更詳細的說,模型 * 不能使用 `$_GET` 和 `$_POST`,或是那些類似終端使用者的要求變數。記住一點,模型會被那些完全不相干的子應用程式所使用 (例如:單元測試和 Web 應用程式介面),並不是所有的變數都代表使用者的要求,這種與終端使用者需求有關的變數,應該要交由控制器來處理。 * 應該避免內嵌 HTML 或是顯示畫面用的程式碼。因為不同的使用者要求,通常會有不同的畫面要顯示 (例如:前端和後端對於一則新聞的顯示方式可能完全不同),這需要交由視圖來處理。 視圖 ---- [視圖](/doc/guide/basics.view) 必須根據使用者所要求的格式來顯示模型。大致上,視圖 * 主要包含用來顯示畫面的程式碼,像是 HTML 或是簡單的 PHP 程式碼用來循覽、格式化和呈現資料; * 必須避免執行資料庫查詢的程式碼。它們應該被放在模型裡。 * 必須避免存許 `$_GET` 和 `$_POST`,或是那些類似終端使用者的要求變數。這是控制器的工作。視圖必須專注在控制器或是模型所提供的資料的顯示和布局,而不是試圖直接去存取要求變數或是資料庫。 * 或許會直接存取控制器和模型的屬性和方法。但是,必須只是用來當作顯示的目的。 視圖可以以不同的方式被重用: * 佈局:共同顯示的區域 (例如:頁面的頭部和底部) 可以放在佈局視圖。 * 部分視圖:使用部分試圖 (不是佈局所使用的視圖) 來重用顯示程式碼的片段。例如,使用 `_form.php` 部分視圖來呈現模型在建立和更新時的輸入表單。 * 小工具:如果,在呈現一個部份視圖需要有許多邏輯,那比較好的方式是把部份視圖轉換成小工具,並把邏輯的部分放在它的類別檔案裡。如果一個小工具需要產生許多 HTML 標記,最好的方式是使用含有這些標記的小工具。 * 輔助類別:在視圖裡,我們常常會使用程式碼片段來完成一些小小的工作像是格式化資料或產生 HTML 標記。除了直接將程式碼放在視圖裡,還有一個更好的方法就是把它們放在視圖輔助類別裡,然後再視圖裡使用這個輔助類別。為此 Yii 提供了一個範例。Yii 有一個非常強而有力的 [CHtml] 輔助類別可以用來產生共同使用的 HTML 程式碼。輔助類別可以放在一個 [自動載入資料夾](/doc/guide/basics.namespace) 來使用而不必明確地宣告載入。 控制器 ---------- [控制器](/doc/guide/basics.controller) 是用來銜接模型、視圖和其他元件在一起成為一個可以運行的應用程式。控制器必須直接面對處理使用者的需求。因此,控制器 * 可以存取 `$_GET`, `$_POST` 和那些類似終端使用者的要求變數; * 可以建立模型的實體和管理它們的生命週期。例如,一個典型的模型更新方法,控制器會先建立一個模型實體;然後將使用者輸入的 `$_POST` 帶入模型;在成功的儲存模型之後,控制器會將使用者的瀏覽器導向到描述模型頁面。注意一點,儲存模型的行為是在模型裡面完成,而不是控制器。 * 應該避免內嵌 SQL 述句,它們應該被放置在模型裡。 * 應該避免包含任何 HTML 或是顯示用的標記。他們應該被放置在視圖裡。 在一個設計良好的 MVC 應用程式裡,控制器通常會非常的瘦小,可能只會包含些許的程式碼;而模型會非常的肥大,將包含那些必須用來展現或是操作的資料。這是因為一個模型的資料結構和邏輯通常屬於特定的應用領域,需要大量的客製化來達成需求;反觀控制器的邏輯則是遵循一種非常相似的模式,橫跨在不同的應用程式之間,進而可以藉由框架或是基礎類別來簡化。