<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??碼云GVP開源項目 12k star Uniapp+ElementUI 功能強大 支持多語言、二開方便! 廣告
                ~~~ 目錄 序言 5 第 1章 Zend Framework2 簡介 6 1.1 Zend Framework2 簡介 6 1.2 下載安裝 6 1.3 搭建開發環境 6 第2章 創建ZF2項目 7 2.1 新建一個項目 7 2.2 配置網站 8 2.3 偽靜態.htaccess文件 8 2.4 啟動/入口文件 8 2.5 添加全局配置文件 9 2.6 添加自動加載文件 init_autoloader.php 9 2.7 IndexController 控制器 10 第3章 創建模塊 12 3.1 Module 文件 12 3.2 module.config 文件 13 3.2.1 router 路由配置 15 3.2.2 controllers控制器配置 15 3.2.3 view_manager 視圖管理器 16 3.2.4 service_manager 服務管理器 16 3.2.5 translator 翻譯器 17 3.2.5.1 語言文件 zh_CN.po 內容 17 3.2.5.2 語言文件 en_US.po 內容 19 3.2.6 navigation 導航條 22 第4 章 創建控制器 23 4.1 控制器簡介 23 4.2 新建控制器 23 4.3 添加控制器的Action 23 第 5 章 創建視圖 26 5.1 創建模板 26 5.1.1 建立布局目錄 26 5.1.2 建立布局文件 26 5.1.3 建立錯誤異常目錄 26 5.1.4 建立錯誤異常模板文件 26 5.1.5 建立 NewsController 模板目錄 26 5.1.6 建立 NewsController 對應的Action 模板文件 27 5.1.7 視圖中常用函數 27 5.2 模板配置 27 5.3 編寫布局和錯誤異常模板 28 5.3.1 模板文件layout.phtml 28 5.3.2 錯誤異常模板 index.phtml 29 5.3.3 404錯誤模板 404.phtml 31 5.4 編寫Action 對應的模板文件 34 5.5 訪問 IndexAction 34 第 6 章 創建模型 36 6.1 ORM 對象映射法 36 6.1.1 創建 News 類 37 6.1.2 創建 NewsTable 類 38 6.1.3 使用模型讀取數據庫數據 38 6.1.3.1 模塊配置 38 6.1.3.2 控制器中使用模型 40 6.1.3.3 通過模板顯示數據庫查詢結果 41 6.1.3.4 插入數據 42 6.1.3.4.1 創建表單文件 42 6.1.3.4.2 添加過濾器 44 6.1.3.4.3 創建表單 46 6.1.3.4.4 模板輸出表單 47 6.1.3.4.5 添加模型方法saveNews 47 6.1.3.4.6 修改新聞內容 48 6.1.3.4.6.1修改模塊路由 48 6.1.3.4.6.2修改editAction 方法 49 6.1.3.4.6.3修改edit.phtml模板 50 6.1.3.4.7 刪除新聞記錄 51 6.1.3.4.7.1修改deleteAction 方法 51 6.1.3.4.7.2添加模型 deleteNews方法 52 6.1.3.4.7.2修改delete.phtml模板 52 6.2 使用分頁導航 53 6.2.1 修改模塊配置文件 53 6.2.2 修改模型文件 53 6.2.3 修改控制器文件 54 6.2.4 添加分頁導航模板 54 6.2.4 修改新聞列表模板 56 6.3 自定模型 57 6.4 章節總結 60 第 7 章 實例應用 61 7.1 建立Album 模塊 61 7.1.1建立模塊目錄 61 7.1.2 配置模塊全局設置 61 7.2 添加模塊文件 62 7.3 添加模塊配置文件 63 7.4 創建數據表 album 64 7.5 添加模型文件 64 7.5.1 添加 Album.php 64 7.5.2 添加AlbumTable.php 66 7.6 添加表單 AlbumForm 68 7.7 添加控制器 AlbumController 69 7.8 添加模板文件 71 7.8.1 列表模板 index.phtml 71 7.8.2 列表模板 add.phtml 72 7.8.3 列表模板 edit.phtml 72 7.8.4 列表模板 delete.phtml 72 7.8.5 列表模板 paginator.phtml 73 第 8 章 用戶認證 76 8.1 建立數據表 76 8.2 新建認證類 76 8.3 引用認證類 77 第 9 章 結束語 79 序言 在教程的制作前先做一些作者的自我介紹,作者賴少林,男,畢業于 廣州市南洋理工職業學院 計算機應用科學網絡專業 和 海南師范大學 計算機應用科學 應用專業。于2008開始實習工作,從2008年起至今在兩家公司任職過,一個是實習單位,另一個就是目前就職的企業--深圳市奇華基業信息技術有限公司,目前擔任公司的技術總監職位。 說起互聯網大家就可能馬上想到網站,一說到網站就會想到JAVA ,.NET ,PHP,ASP 等開發語言;而對于這幾個的優缺在互聯網的有各種談論在此就不再細說了。PHP同時也是我的一個選擇,這或許也就我今天為什么要寫Zend Framwork2 教程的原因根源之一。當前不論使用哪一種語言進行網站的開發都離不開一個東西---框架,框架到是什么呢?形象的說是一個網站的主體架構,可以理解為一座房屋的主體結構。而Zend Framework2 就是一個基于MVC形式的一個框架;那么MVC 到底是什么呢?他能夠用來做什么的? 在此就簡單的說一下MVC,MVC是Model,Controller,View 三個單詞的縮寫,本意為模型,控制器,視圖;MVC能夠把用戶界面,業務邏輯,數據處理等工作分離開來,使不同的層次來處理不同的工作,從而提高代碼的重用性,項目可維護性。 PHP的框架的有很多,如 Zend Framwork , Symfony ,Codeilgniter, ThinkPHP 等;那么我為什么就選擇Zend Framework2(以下Zend Framework簡寫為ZF) 呢。其實以前選擇ZF 原因很簡單,主要是有這幾個方面的原因:①Zend 官方出的一個框架;②對執行效率高;③使用靈活;④插件豐富,也易于自寫插件;⑤適用于大型項目 等。 在此說說為什么要寫ZF2 教程的原因,作者使用ZF1框架已經有多年的時間,在最開始接觸ZF的時候就已經聽說ZF 很難學,當時我不大相信不就是一個框架嗎,有什么難的,當時就是那樣的想法的。可當真的開始學習ZF的時候問題就不斷的出現問題了,不問題多而且解決方法又少;因為PHP本身在國內發展及ZF在國內的應用緣故(由于國內較少有大學開設PHP語言課程,使得國內使用PHP技術的人員相對較少),導致要找到問題的相關解決方法真不簡單,在國內的網站很少有ZF的相關資料,即使有一些資料也是比較零散的,而且也是已經比較過時的資料,對于解決問題基本上沒有什么幫助;而對國外的資料就豐富多了,因為國外對PHP技術的發展及流行程度比較國內高,資料雖多但全是英文的,如果沒有一定英文的基礎根本無法從中找到有用幫助。直到今日作者發現不管是互聯網上還是實體書本對于ZF的中文版教程還是少之又少。所以決定編寫一本關于ZF 開發的系列教程,希望能夠幫助到一些在ZF迷途的PHPER。 本書中的主要內容都是從項目的實例開發為出發點,并不是對ZF2官方資料的直接翻譯;所以此書不可能將ZF2 類庫的所有內容及配置都進行講解;在寫本書的同時本人也同在開發某款CMS系統,書中大部分代碼均為CMS原文件中的片段,所有代碼都通過本人的調試。本書比較適合用于ZF2 的項目入門指導書籍,書中集中講述了怎樣去使用及掌握ZF2的技術與技能。致以ZF2的底層實現可以查閱ZF2官方網站的開發手冊。 Zend Framework 官方網址:http://www.zendframework.com/ 第 1章 Zend Framework2 簡介 1.1 Zend Framework2 簡介 ZF2是一個基于PHP的開源框架,可以用來開發WEB應用程序的各種服務。ZF2是一個基于面向對象的框架,一切都是以對象為基礎。ZF2有非常豐富的組件庫,而且大部組件之間相互獨立,互不依賴,所以開發者可以獨自開發并使用自定義組件。 ZF2 擁有一個強大而且高效的MVC實現,他具有強大的數據庫操作、路由控制、視圖渲染、HTML表單解析、表單驗證、數據過濾等功能;同時ZF2還提供了多種用戶認證功能,通過證書來保存用戶認證和授權信息,也可以通過Amf 來為Flash等其他語言開發的軟件提供相應的服務。總的來說不論你需要什么功能,你都可以從ZF2中找到相應的組件來實現,從而有效的減少開發時間,提高項目開發的效率。ZF2提供的各種組件可以用來實現你想要的各種功能,也可以添加一些你自定義組件來搭建你強大的WEB應用程序。 1.2 下載安裝 ZF2 的安裝要求PHP的版本不低于5.3,不過作者還是建議可以升級到更高的版本,因為每個更高的版本都會對前一個版本的安全性和性能發揮上做了相應的改善和提高。本書中使用的PHP版本為PHP5.4,經過作者一些相關測試,ZF2框架在PHP 5.5 的版本上運行也是完全正常,所以PHP的版本選擇范圍還是比較廣的。 致以安裝ZF2與ZF1相比明顯要復雜很多,兩者即有明顯的區別,又有一些本質上的聯系;要想了解ZF1與ZF2的之間的區別可以到ZF的官網去找相關的說明與幫助。ZF2框架類庫可以在ZF的官方網站(官網網址:http://www.zendframework.com/,下載網址:http://www.zendframework.com/downloads/latest)下載,本書使用的ZF版本為ZF2.15。至于怎樣安裝使用ZF2的框架,下面會做詳細的介紹。 1.3 搭建開發環境 在開始使用ZF2框架前需要把開發平臺先搭建好,在此作用選擇 xampp 作為開發測試環境,最新版本的xampp已經集成了apache2.4與php5.4.7及其他組件,選擇netbeans 作為開發工具。作者將網站的開發目錄網址設置為:http://127.0.0.1/ | http://localhost/ 這兩個網址是一樣的。 開發環境安裝好以后需要對 apache 做一些相關修改以便支持 .htaccess 文件。通過需要修改的地方為設置你網站目錄的地方(httpd.conf),將 AllowOverride None 修改為 AllowOverride All 在vhost.conf下添加 網站的配置并沒有太多的要求,對于目錄的命名等可以根據自已的情況來配置,網站只要能支持.htaccess 文件就行。 第2章 創建ZF2項目 2.1 新建一個項目 方法一:手動添加目錄,結構如下 / └appliction └css └js └images └library └Zend └module └Application └config └language └src └Application └Controller └Model └views └vendor 目錄解釋: /application 你網站的根目錄 /application/css | js | images 這些主要存放樣式表、js、圖片等文件 /library 存放類庫文件 /module 存放各種模塊,一般在此目錄下的一個子目錄為一個模塊 /module/Application 表一個名叫 Application 的模塊 /module/Application/config Application 模塊的配置文件目錄 /module/Application/language 語言文件目錄,用來支持多國語言實現項目的國際化 /module/Application/src Application 模塊的資源文件目錄,下面包含此模塊的控制器、模型、表單等一系列文件 /module/views Application 模塊的視圖文件目錄 /vendor 自定義類庫或其他第三方類庫 往后需要添加模塊可以根據相似的目錄結果進行添加。 方法二:使用netbeans 或 zend studio 新建一個項目,在創建項目的過程中選擇使用 Zend Framework 框架,這樣就可以創建出一個基于 Zend Framework 框架的項目,目錄結構有些許差異,但目錄功能與上面結構說明類似,你可以在項目找到他們對應的結構說明。下面作者使用Zend studio 來創建一個基于Zend Framework2的項目操作:打開zend studio 軟件 --> File(打開) --> New(新建) --> Project(項目) --> Local PHP Porject(本地PHP項目) --> Next(下一步) --> Project Name(項目名稱,填寫你的項目名稱) --> Location(項目放在位置,選擇項目的保存位置) --> Content(項目內容,選擇 Zend Framework) --> Version(版本,選擇使用框架版本) --> Finish(完成);這樣一個基于Zend Framework 2的項目就已經建立好了,然后調整一個apache的目錄指定。通過這種方法建立項目后可以直接使用 http://localhost/ 來打開項目了。 上面兩種創建項目的方法各有優缺點,方法一:手動輸入相對麻煩,但目錄結構比較靈活;方法二:項目創建簡單,即建即用,但類庫不好找(其實就是放在vendor 下面了)。項目的創建方法不管使用哪一種,只要清楚各個目錄的作用即可。本書創建項目的方法為第一種方法,此方法創建并運行項目需要添加多個文件,而第二種方法則直接創建后就可以直接運行。本書使用第一種方法創建項目的原因還是基于對ZF2框架的深化理解,使用閱讀者能夠真正的了解到ZF2的運行機制,同也使用讀者能更多靈活的掌握和使用ZF2框架。 2.2 配置網站 ZF2項目的基本目錄創建好以后,在你的 apache 服務器上添加一個虛擬網站,配置示例詳情如下: <VirtualHost *:80> ServerName localhost DocumentRoot /path/application <Directory /path/application> DirectoryIndex index.php AllowOverride All Order allow,deny Allow from all </Directory> </VirtualHost> 如果對 apache 配置比較熟練的話可以根據自已的需要進行配置,對配置格式沒有什么特殊的要求。唯一需要注意的就是要將 AllowOverride All 打開偽靜態的支持。 2.3 偽靜態 .htaccess文件 添加/application/.htaccess 如果使用記事本來寫這個文件,那么保存的時候要使用另存為的方式進行保存,如果在netbeans中創建的話就可以直接保存。同時還應當注意在書寫這種文件的時候最后至少要有一行空白行,有時候有些文件就有這樣要求。此文件的主要功能就是實現URL的重寫,根據URL的訪問地址通過前端控制器找到相應的路由,從而實現對資源文件準確定位。重寫URL的好處還在于能夠讓搜索引擎更容易抓取。 在文件中輸入以下內容: RewriteEngine On RewriteCond %{REQUEST_FILENAME} !-f RewriteRule ^.*$ index.php [NC,L] 2.4 添加啟動/入口文件 路徑:/application/index.php 添加以下內容: chdir(__DIR__); if (!defined('APP_PATH')) define ('APP_PATH', __DIR__ . '/../'); if (!defined('LIB')) define('LIB', APP_PATH . 'library'); require 'init_autoloader.php'; Zend\Mvc\Application::init(require APP_PATH . 'config/application.config.php')->run(); // 啟動應用程序 下面代碼解釋: lchdir 修改當前運行目錄 lAPP_PATH 指定網站根目錄 lLIB 指定類庫目錄 linit_autoloader.php 自動加載文件 config/application.config.php 應用程序全局配置文件 Zend\Mvc\Application::init(require APP_PATH . 'config/application.config.php')->run() 啟動應用程序 2.5 添加全局配置文件 路徑:/config/application.config.php 內容如下: return array( 'modules' => array( 'Application' ), 'module_listener_options' => array( 'config_glob_paths' => array( APP_PATH.'config/autoload/{,*.}{global,local}.php', ), 'module_paths' => array( APP_PATH.'module', APP_PATH.'vendor',// 就要應用于phpunit ), ), ); 代碼解釋: lmodules=>array() 這是模塊配置,網站系統的每一個模塊都要添加到此,以便ZF2框架能夠正確的找到模塊 lmodule_listener_options=>array() 此處用于設置模塊的事件偵聽 lconfig_glob_paths=>array() 配置全局路徑,以便系統自動加載相關文件類庫 lmodule_paths=>array() 配置模塊路徑 2.6 添加自動加載文件 init_autoloader.php 路徑:/application/init_autoloader.php if (defined('LIB')) { include LIB . '/Zend/Loader/AutoloaderFactory.php'; Zend\Loader\AutoloaderFactory::factory(array( 'Zend\Loader\StandardAutoloader' => array( 'autoregister_zf' => true ) )); } if (!class_exists('Zend\Loader\AutoloaderFactory')) { throw new RuntimeException('Unable to load ZF2. '); } 代碼解釋: lif (defined('LIB')) 判斷是否有定義預定義變量指向ZF2類庫 linclude LIB . '/Zend/Loader/AutoloaderFactory.php' 導入ZF2框架自動加載工廠文件 lZend\Loader\AutoloaderFactory::factory(array()) 對自動加載工廠類進行設置 lif (!class_exists('Zend\Loader\AutoloaderFactory')) 判斷工廠類是存在,如果不存在則拋出異常 下面是 init_autoloader.php 的另一個寫法,此寫法其實是 Zend studio 或 netbeans 創建項目時自動生成的寫,這樣的寫法其實是加入對 phpunit 單元測試的支持。phpunit 的測試環境一般都是在命令行上操作完成,所以這樣的寫法也是對命令行環境的一種設置,在此就不多加詳解;下面只貼出代碼。 if (file_exists('vendor/autoload.php')) { $loader = include 'vendor/autoload.php'; } if (defined('LIB')) { if (isset($loader)) { $loader->add('Zend', LIB); } else { include LIB. '/Zend/Loader/AutoloaderFactory.php'; Zend\Loader\AutoloaderFactory::factory(array( 'Zend\Loader\StandardAutoloader' => array( 'autoregister_zf' => true ) )); } } if (!class_exists('Zend\Loader\AutoloaderFactory')) { throw new RuntimeException('Unable to load ZF2.'); } 2.7 IndexController 控制器 路徑:/module/Application/src/Application/controller/IndexController.php 代碼如下: namespace Application\Controller; use Zend\Mvc\Controller\AbstractActionController; use Zend\View\Model\ViewModel; class IndexController extends AbstractActionController{ public function indexAction(){ echo “hello world”; exit; } } 代碼說明: lnamespace Application\Controller 指定命名空間 luse Zend\Mvc\Controller\AbstractActionController | use Zend\View\Model\ViewModel 導入相關類庫 lclass IndexController extends AbstractActionController 定義 IndexController 類庫,同時此類必需繼承 AbstractActionController 類,這是ZF2 的硬性要求,除非你重寫此類的實現。 lpublic function indexAction(){} 控制器的一個響應動作,其中indexAction 這個名稱為ZF默認動作 lecho "hello world" 在屏幕上打印出 hello world 到此一個基本的控制器類就已經編寫完成,但是不是現在就可以通過 http://localhost/ 就可以訪問控制器,并可以看到屏幕上的hello world 了呢?答案當然是否定的。如果這是ZF1的話,就添加完這樣一個控制器后應該是可以訪問的了。但此處使用的是ZF2框架,所以要想通過 http://localhost/ 訪問并在屏幕上打印出 hello world 來還需要添加多個文件來共同實現;這也是為什么ZF2比ZF1的使用要更為復雜,也是ZF2比ZF1更加強大、靈活的原因所在。 第3章創建模塊文件 ZF2 使用模塊系統將應用程序的主要代碼集成到各個模塊中去。同時應用模塊還應提供用于引導、錯誤異常、路由等全部的配置信息。在模塊文件里可以根據自已的需要去調整關于 實圖、路由、模型等一系列應用程序級的設置,同時Module不單起到配置信息的作用,同時也是應用程序的必需中間件或橋梁,因為程序從前端控制器的分配及引導下進入的下層級就是Module類,通過解析Module類到達指定資源位置。由此也可以看出ZF2的靈活性還是比較高的。 下面開始添加模塊文件。 3.1 Module 文件 路徑:/moudle/Application/Module.php 內容如下: namespace Application; use Zend\Mvc\ModuleRouteListener; use Zend\Mvc\MvcEvent; class Module { public function onBootstrap(MvcEvent $e){ $e->getApplication()->getServiceManager()->get('translator');// 多國語言支持,這個語言文件需要自已添加 $eventManager = $e->getApplication()->getEventManager();// 獲取當前已經有事件管理器 $moduleRouteListener = new ModuleRouteListener();// 新建一個路由模塊監聽器 $moduleRouteListener->attach($eventManager);// 附加事件管理器 } public function getConfig(){ return include __DIR__ . '/config/module.config.php';// 引入模塊配置文件 } public function getAutoloaderConfig(){ return array( 'Zend\Loader\StandardAutoloader'=>array( 'namespaces'=>array( __NAMESPACE__ => __DIR__ . '/src/' . __NAMESPACE__ // 導入自動加載空間 ) ) ); } } 代碼解釋: lnamespace Application 創建或訪問指定命名空間 luse Zend\Mvc\ModuleRouteListener | use Zend\Mvc\MvcEvent 導入包或類 lclass Module 定義類 lpublic function onBootstrap 啟動模塊,onBootstrap() 將調用每個已經實現此功能的模塊,并且用于執行輕量級任務和注冊事件監聽器等 lpublic function getConfig 獲取此模塊中的配置信息,返回一個符合ZF2自動加載工廠規則的數組 lpublic function getAutoloaderConfig 此模塊中自動加載配置信息 在此說明一下,在ZF2開始引入了namespeace 空間的概念,使得ZF2 與java的相關概念進一步的靠近,這對于已經掌握了java開發的人來說是將是一個好的消息。空間的含義就相當于一個用來裝東西的容器,而對于程序來說,空間可以簡單的理解為裝類的容器;有了空間的出現,所有的類庫都將被包含到一個指定的空間里面,因為ZF2的文件搜索或路由也是通過空間來定位的。再者就是有了空間可以更好的去管理各種類,方便文件功能歸類及使用。 3.2 module.config 文件 路徑:/module/Application/config/module.config.php 內容如下: return array( 'router' => array( 'routes' => array( 'application' => array( 'type' => 'segment', 'options' => array( 'route' => '/[application][:controller][/:action]', 'constraints' => array( 'controller' => '[a-zA-Z][a-zA-Z0-9_-]*', 'action' => '[a-zA-Z0-9_-]*', ), 'defaults' => array( '__NAMESPACE__' => 'Application\Controller', 'controller' => 'Index', 'action' => 'index' ), ), ), ), ), 'controllers' => array( 'invokables' => array( 'Application\Controller\Index' => 'Application\Controller\IndexController' ), ), 'view_manager' => array( 'display_not_found_reason' => true, 'display_exceptions' => true, 'doctype' => 'HTML5', 'not_found_template' => 'error/404', 'exception_template' => 'error/index', 'template_map' => array( 'layout/layout' => __DIR__ . '/../view/layout/layout.phtml', 'application/index/index' => __DIR__ . '/../view/application/index/index.phtml', 'error/404' => __DIR__ . '/../view/error/404.phtml', 'error/index' => __DIR__ . '/../view/error/index.phtml', ), 'template_path_stack' => array( 'application' => __DIR__ . '/../view' ), ), 'service_manager' => array( 'factories' => array( 'translator' => 'Zend\I18n\Translator\TranslatorServiceFactory', 'navigation' => 'Zend\Navigation\Service\DefaultNavigationFactory', ), ), 'translator' => array( 'locale' => 'en_US', 'translation_file_patterns' => array( array( 'type' => 'gettext', 'base_dir' => __DIR__ . '/../language', 'pattern' => '%s.mo', ), ), ), 'navigation' => array( 'default' => array( array( 'label' => 'Home', 'route' => 'applicaton’ ), array( 'label' => 'Application', 'route' => 'application’, 'pages' => array( array( 'label' => 'List', 'route' => 'application’, 'action' => 'list', ), ), ), ), ), ); 先對以上配置進行歸類: array(// 總配置 ‘router’ =>array(),// 路由 ‘controllers’ =>array(),// 控制器 ‘view_manager’ =>array(),// 視圖管理器 ‘service_manager’ =>array(),// 服務器管理器 ‘translator’ =>array(),// 譯碼器或翻譯器 ‘navigation’ =>array(),// 頁面導航 ); 下面對 router、controllers、view_manager、service_manager、translator 、navigation進行逐個解釋。 3.2.1 router 路由配置 路由的配置是對前臺頁面訪問地址的具體配置,此處配置的格式將影響到前臺頁面訪問此模塊的所有地址 鏈:router--->routes--->模塊--->具體配置 lrouter 此數組塊為路由配置信息段 lrouter-->routes 表示此模塊的中路由,路由至少1條以上 lrouter-->routes-->application 表示你的模塊名稱,在此以下的信息為具體配置信息 lrouter-->routes-->application-->type 表示路由模式,可選 segment 或 literal,區別在于 segment 已經處理好了結尾的斜杠,而literal 會把結尾帶與不帶斜杠表示不同的路由進行處理; 如果使用literal 時需要特別注意這一點。 lrouter-->routes-->application-->options 路由具體選項信息區塊 lrouter-->roytes-->application-->options-->route 路由規則,此處規則將最終決定此模塊的路由訪問格式 lrouter-->roytes-->application-->options-->constraints 路由匹配規則 lrouter-->roytes-->application-->options-->constraints-->controller 控制器的路由正規匹配規則 lrouter-->roytes-->application-->options-->constraints-->action action(動作)的路由正規匹配規則 lrouter-->roytes-->application-->options-->defaults 默認路由處理規則 lrouter-->roytes-->application-->options-->defaults-->__NAMESPACE__ 指定模塊控制器所在的命名空間 lrouter-->roytes-->application-->options-->defaults-->controller 指定默認使用的控制器名稱 lrouter-->roytes-->application-->options-->defaults-->action 指定默認使用的action(動作)名稱 3.2.2 controllers控制器配置 控制器的配置將決定哪些控制器類能夠被訪問及使用,在此配置后ZF2自動加載廠可以很快的定位到控制所在的位置,對資源進行快速訪問、使用。 鏈:controllers--->invokables--->控制器 lcontrollers 表示此數組塊為控制器配置信息段 lcontrollers-->invokables 這個是控制器區塊的固定表示方法,表示此區塊下的控制器為可用控制器 lcontrollers-->invokables-->Application\Controller\Index 表示一個控制器,數組的鍵表示DI注入的引用,數組值則表示對應控制器所在的具體路徑 控制器的配置不局限于某一個控制器,可以把所有已經存在并且有效控制器加入到此區塊來進行使用。 3.2.3 view_manager 視圖管理器 視圖管理器主要負責視圖信息的配置,如:錯誤顯示、頁面類型、布局文件、視圖文件位置、404頁面等。 鏈:view_manager-->N , view_manager-->template_map , view_manager-->template_path_stack lview_manager 表示此數組塊為視圖管理器配置信息段 lview_manager-->display_not_found_reason 是否顯示404原因 lview_manager-->display_exceptions 是否顯示異常信息 lview_manager-->doctype 指定視圖頁面類型 lview_manager-->not_found_template 指定404的視圖模板 lview_manager-->exception_template 指定異常模板文件 lview_manager-->template_map 視圖模塊地圖 lview_manager-->template_map-->’layout/layout’ 指定布局文件 lview_manager-->template_map-->’application/index/index’ 指定 application 模塊的視圖文件 lview_manager-->template_map-->’error/404’ 指定404頁面的視圖文件 lview_manager-->template_map-->’error/index’ 指定錯誤異常頁面的視圖文件 lview_manager-->template_path_stack 視圖模板堆棧路徑 lview_manager-->template_path_stack-->application 指定模塊application 視圖目錄所在路徑 3.2.4 service_manager 服務管理器 服務管理器主要負責一些工廠類的配置,使用系統能夠在運行時自動的加載運行某些服務性功能。 鏈:service_manager-->factories lservice_manager 表示此數組塊為服務管理器配置信息段 lservice_manager-->factories 工廠類配置 lservice_manager-->factories-->translator 語言轉換工廠,主要功能是實現多國語言的支持,語言文件需要自已編寫;ZF2框架本身并不提供語言包,但提供對語言包的解析功能,通過語言包通過指定的語言進行轉換;同時語言之間的轉換及格式的變化比較而隨意性也比較大,所以語言包可以根據項目的實現需求來進行訂制,如果實際的項目開發中并不使用到國際化的功能時,可以將多國語言國際功能刪除。 lservice_manager-->factories-->translator-->navigation 導航工廠,主要用來實現頁面的導航和分頁導航 3.2.5 translator 翻譯器 翻譯器的主要工作是負責對各種支持語言的轉換以此為目的,從而實現網站應用程序的多國化甚至全球化。本書內容有涉及到使用語言包,語言包的生成可以參考po,mo 文件創建的其他文獻;你也可以通過 Zend Studio 來創建項目以獲取ZF2 默認生成的language 包。下面將會提供兩個語言包內容 zh_CN.po ,en_US.po 這個為中英文件互轉文件,這兩語言由Zend Studio項目生成時自動創建;他們對應的mo文件可以通過 poedit 軟件來生成,也可能過poedit 來修改po文件。如果在開發時不想使用任何語言轉換,可以不進行任何關于語言轉換相關的配置。 鏈:translator-->locale , translator-->translation_file_patterns ltranslator 表示此數組塊為翻譯器配置信息段 ltranslator-->locale 指明應用程序的本地使用語言,或是應用程序使用的默認語言 ltranslator-->translation_file_patterns 翻譯文件的配置設置 ltranslator-->translation_file_patterns-->type 翻譯文件類型 ltranslator-->translation_file_patterns-->base_dir 指定語言文件目錄 ltranslator-->translation_file_patterns-->pattern 語言文件的匹配規則 下面提供兩個語言包,語言包為中英語言包,可以在開發的時候對兩種語言環境進行切換。 3.2.5.1 語言文件 zh_CN.po 內容 msgid "" msgstr "" "Project-Id-Version: ZendSkeletonApplication\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2012-07-05 22:17-0700\n" "PO-Revision-Date: 2013-05-06 11:26+0800\n" "Last-Translator: \n" "Language-Team: ZF Contibutors <zf-devteam@zend.com>\n" "Language: en_US\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Poedit-KeywordsList: translate\n" "X-Poedit-Basepath: .\n" "X-Generator: Poedit 1.5.5\n" "X-Poedit-SearchPath-0: ..\n" msgid "Home" msgstr "主頁" msgid "All rights reserved." msgstr "版權所有." msgid "Help &amp; Support" msgstr "幫助 &amp; 支持" msgid "An error occurred" msgstr "發生錯誤" msgid "Additional information" msgstr "附加信息" msgid "File" msgstr "文件" msgid "Message" msgstr "消息" msgid "Stack trace" msgstr "Stack trace" msgid "Previous exceptions" msgstr "上一個異常" msgid "No Exception available" msgstr "沒有可用的Exception" msgid "A 404 error occurred" msgstr "404 缺少目標文件" msgid "The requested controller was unable to dispatch the request." msgstr "所請求的控制器不能分發該請求" msgid "" "The requested controller could not be mapped to an existing controller class." msgstr "所請求的控制器不能映射到已存在的控制器類" msgid "The requested URL could not be matched by routing." msgstr "所請求的URL不能與路由對應" msgid "We cannot determine at this time why a 404 was generated." msgstr "我們不能確定為什么這次會出現404" msgid "Controller" msgstr "控制器" msgid "resolves to %s" msgstr "解決: %s" msgid "Exception" msgstr "異常" msgid "Add" msgstr "添加" msgid "Delete" msgstr "刪除" msgid "Edit" msgstr "修改" msgid "Add new album" msgstr "添加新聞" msgid "Title" msgstr "標題" msgid "Artist" msgstr "文章內容" 3.2.5.2 語言文件 en_US.po 內容 msgid "" msgstr "" "Project-Id-Version: ZendSkeletonApplication\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2012-07-05 22:17-0700\n" "PO-Revision-Date: 2012-07-05 22:17-0700\n" "Last-Translator: Evan Coury <me@evancoury.com>\n" "Language-Team: ZF Contibutors <zf-devteam@zend.com>\n" "Language: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Poedit-KeywordsList: translate\n" "X-Poedit-Language: English\n" "X-Poedit-Country: UNITED STATES\n" "X-Poedit-Basepath: .\n" "X-Poedit-SearchPath-0: ..\n" #: ../view/layout/layout.phtml:6 #: ../view/layout/layout.phtml:33 msgid "Skeleton Application" msgstr "" #: ../view/layout/layout.phtml:36 msgid "Home" msgstr "" #: ../view/layout/layout.phtml:50 msgid "All rights reserved." msgstr "" #: ../view/application/index/index.phtml:2 #, php-format msgid "Welcome to %sZend Framework 2%s" msgstr "" #: ../view/application/index/index.phtml:3 #, php-format msgid "Congratulations! You have successfully installed the %sZF2 Skeleton Application%s. You are currently running Zend Framework version %s. This skeleton can serve as a simple starting point for you to begin building your application on ZF2." msgstr "" #: ../view/application/index/index.phtml:4 msgid "Fork Zend Framework 2 on GitHub" msgstr "" #: ../view/application/index/index.phtml:10 msgid "Follow Development" msgstr "" #: ../view/application/index/index.phtml:11 #, php-format msgid "Zend Framework 2 is under active development. If you are interested in following the development of ZF2, there is a special ZF2 portal on the official Zend Framework website which provides links to the ZF2 %swiki%s, %sdev blog%s, %sissue tracker%s, and much more. This is a great resource for staying up to date with the latest developments!" msgstr "" #: ../view/application/index/index.phtml:12 msgid "ZF2 Development Portal" msgstr "" #: ../view/application/index/index.phtml:16 msgid "Discover Modules" msgstr "" #: ../view/application/index/index.phtml:17 #, php-format msgid "The community is working on developing a community site to serve as a repository and gallery for ZF2 modules. The project is available %son GitHub%s. The site is currently live and currently contains a list of some of the modules already available for ZF2." msgstr "" #: ../view/application/index/index.phtml:18 msgid "Explore ZF2 Modules" msgstr "" #: ../view/application/index/index.phtml:22 msgid "Help &amp; Support" msgstr "" #: ../view/application/index/index.phtml:23 #, php-format msgid "If you need any help or support while developing with ZF2, you may reach us via IRC: %s#zftalk on Freenode%s. We'd love to hear any questions or feedback you may have regarding the beta releases. Alternatively, you may subscribe and post questions to the %smailing lists%s." msgstr "" #: ../view/application/index/index.phtml:24 msgid "Ping us on IRC" msgstr "" #: ../view/error/index.phtml:1 msgid "An error occurred" msgstr "" #: ../view/error/index.phtml:8 msgid "Additional information" msgstr "" #: ../view/error/index.phtml:11 #: ../view/error/index.phtml:35 msgid "File" msgstr "" #: ../view/error/index.phtml:15 #: ../view/error/index.phtml:39 msgid "Message" msgstr "" #: ../view/error/index.phtml:19 #: ../view/error/index.phtml:43 #: ../view/error/404.phtml:55 msgid "Stack trace" msgstr "" #: ../view/error/index.phtml:29 msgid "Previous exceptions" msgstr "" #: ../view/error/index.phtml:58 msgid "No Exception available" msgstr "" #: ../view/error/404.phtml:1 msgid "A 404 error occurred" msgstr "" #: ../view/error/404.phtml:10 msgid "The requested controller was unable to dispatch the request." msgstr "" #: ../view/error/404.phtml:13 msgid "The requested controller could not be mapped to an existing controller class." msgstr "" #: ../view/error/404.phtml:16 msgid "The requested controller was not dispatchable." msgstr "" #: ../view/error/404.phtml:19 msgid "The requested URL could not be matched by routing." msgstr "" #: ../view/error/404.phtml:22 msgid "We cannot determine at this time why a 404 was generated." msgstr "" #: ../view/error/404.phtml:34 msgid "Controller" msgstr "" #: ../view/error/404.phtml:41 #, php-format msgid "resolves to %s" msgstr "" #: ../view/error/404.phtml:51 msgid "Exception" msgstr "" 3.2.6 navigation 導航條 導航條的主要功能是生成頁面導航或分頁導航 鏈:navigation-->N lnavigation 表示此數組塊為頁面導航配置信息段 lnavigation-->default 默認頁面導航 lnavigation-->default-->array() 一個導航標簽配置 lnavigation-->default-->label 導航的標簽 lnavigation-->default-->route 導航的路由,其實就是指向的控制器 通過以上對路由、控制器、視圖、服務等各項功能的配置之后,現在已經可以通過 http://localhost/ 或 http://localhost/index/index 的鏈接來訪問我們的操控了。通過 http://localhost/ 訪問就能看到屏幕打印出 hello world ,你會發現這個鏈接即沒有控制器也沒有動作(action),怎么就可以輸出內容了呢,其實通過上面的路由配置,已經設置一個默認的路由,默認路由規則中規定了默認使用的控制器為IndexController,默認訪問的動作為indexAction;而 http://localhost/index/index 也是同樣因為路由設置,同時該連接符合路由規則,因此同樣達到了打印輸出 hello world 的效果 第4 章 創建控制器 4.1 控制器簡介 控制器是ZF2的核心功能,其實現了前端控制器所需的全部接口。如:路由分發、視圖渲染、助手、請求、響應等一系列的功能。同時也可以利用繼承來設計自已的助手類或一些實用性較的插件等,來加強自已的系統功能。 4.2 新建控制器 在ZF2中,控制器是一個類通常稱為{控制器名稱}控制器。 請注意,{控制器名稱}必須以大寫字母開頭。這個類保存在控制器模塊目錄內以名為{控制器名稱}控制器類.php的文件中。 控制器的每個操作都是在一個公共方法內的{動作名稱}中完成。一般情況下{動作名稱}是以小寫字母開頭。 根據前面章節的相關設置,當前項目中的所有控制器都將放在 /module/Application/src/Application/Controller 的目錄下;在本章節及接下來的幾個章節都以一個新聞系統來對相關的知識內容來進行講解。 現在添加一個控制器,在控制器目錄下新一個控制器 NewsController 控制器,路徑:/module/Application/src/Application/Controller/NewsController.php 代碼如下: class NewsController extends AbstractActionController {} 通過以上代碼便創建了一個標準的控制器類,雖然些控制器只有短短的一行代碼,但他擁有操作器所需的全部基本功能;因為他已經繼承了 AbstractActionController 類中的全部方法。 4.3 添加控制器的Action 下面在NewsController控制器中添加幾個 Action: public function indexAction(){ echo “NewsController indexAction”; exit; } public function listAction(){ echo “NewsController listAction”; exit; } public function addAction(){ echo “NewsController addAction”; exit; } public function editAction(){ echo “NewsController editAction”; exit; } public function deleteAction(){ echo “NewsController deleteAction”; exit; } 注意:ZF2控制器的action方法都必需為 public 類型,不然ZF2前端控制器可能無法訪問導致出錯。同時應該注意action 的名稱都是動作名+Action組成的,需要注意大寫(如果項目將來是布置在Linux系統的服務器上時這點就顯得尤為重要)。 經過添加以上的代碼就建立了NewsController 控制器中建立了5個不同的action,在此就可以利用這5個不同的action 來實現5個不同的功能。下面說明一下上面添加的5個 action 的主要作用,indexAction 為控制器的默認action ;listAction 用來實現新聞列表功能;addAction 用來實現添加新聞的功能;editAction 用來實現修改/編輯新聞功能;deleteAction 用來實現刪除新聞的功能。 有了控制器及控制器的action,那么是否就可以直接通過 http://localhost/news 來對NewsController進行訪問了呢?答案是否定的。在前面的章節有提到過關于路由的概念或相關的內容,ZF2 中的所有控制的訪問都需要先通過對控制器路由設定才能進行使用,沒有經過路由設置的控制器就相當于一部不會割草的割草機;這種路由的設定也體現了ZF2中強大的路由功能,你可以將控制器的訪問路徑配置成各種各樣的形式,這種路由的設置模式區別于傳統PHP網址路徑的訪問形式,傳統的訪問地址往往都是包括了文件名,而ZF2的路由配置規則則可完全將文件名隱藏起來。那下面就開始對 NewsController 控制器進行路由的進行設定。 找到模塊配置文件 /module/Application/config/module.config.php,打開文件并找到 router-->routes-->application 節點的未尾,在此節點的末尾添加如下代碼: 'news'=>array( 'type'=>'segment', 'options'=>array( 'route'=>'/news[/:action]', 'constraints'=>array( 'action'=>'[a-zA-Z]' ), 'defaults'=>array( 'controller'=>'Application\Controller\News', 'action'=>'index' ), ), ), 注意:請確認好 application 與 news 的節點是處于同一層次 下面對 NewsController 路由配置進行解釋: lnews=>array() 表示一個路由節點,此節點的路由名稱為 news lnews-->type=>segment 表示路由使用 segment 模式進行解析 lnews-->options=>array() 表示路由配置選項 lnews-->options-->route => /news[/:action] 表示路由地址 lnews-->options-->constraints=>array() 對路由約束規則,其實就是對路由的正則匹配 lnews-->options-->constraints-->action 表示 action 的匹配規則 lnews-->defaults 表示路由默認訪問的配置 lnews-->defaults-->controller 表示默認使用的控制器 lnews-->defaults-->action 表示控制器默認使用的action 現在可以通過 http://localhost/news 訪問到 indexAction http://localhost/news/list 訪問到 listAction http://localhost/news/add 訪問到 addAction http://localhost/news/edit 訪問到 editAction http://localhostnews/delete 訪問到 deleteAction 通過以上的幾個環節便完成了ZF2中的控制器的創建和使用,由此可見ZF2的使用比較ZF1更為復雜,也可以看出路由的設置及訪問方式更加的靈活。在ZF2框架中控制器、視圖、模型 是缺一不可的一個整體體系,缺失任何一個都將損害ZF2的完全性;致以本章節前面提到的要實現控制器中的增、刪、改 的功能將會在視圖及模型章節中加以補充。 第 5 章 創建視圖模板 視圖是任何網站應用程序不可或缺的一個組成部分,它提供了與用戶交互的良好界面,也提供了數據輸入與輸出的接口。ZF2 視圖類主要有 Zend\View 包,其主要功能簡單的說包括:變量傳遞,數據轉換、視圖渲染、請求映射、渲染策略、響應策略等。此外ZF2還通過 Zend\MVC\View 提供了事件偵聽的一系列包,在進行項目開發的時候可以根據需求加入偵聽事件。下面開始創建視圖并開始使用他們。 5.1 創建模板 為要呼應本書各章節的內容,在此將建立三種模板:布局模板、錯誤異常模板、控制器模板 5.1.1 建立布局目錄 路徑:/module/Application/view/layout 此目錄主要用來放置網站應用程序的布局文件,在建站的時候可以根據頁面的不同需要來選擇不同的布局文件。布局的功能可以實現不同模塊不同布局,不同模塊相同布局的實際需要。 5.1.2 建立布局文件 路徑:/module/Application/view/layout/layout.phtml 在此需要注意一下,ZF2默認的視圖文件均以.phtml 為后綴名,如果有其他特殊要求可以修改為其他的后綴名 5.1.3 建立錯誤異常目錄 路徑:/module/Application/view/error 在此目錄下主要用來放置一些關于錯誤異常處理的模板文件 5.1.4 建立錯誤異常模板文件 /module/Application/view/error/index.phtml 錯誤異常信息顯示模板文件 /module/Application/view/error/404.phtml 404錯誤異常信息顯示模板文件 5.1.5 建立 NewsController 模板目錄 路徑:/module/Application/view/application/news 此目錄主要用放置 NewsController 中Action 對應的模板文件。一般情況一個控制會對應一個模板目錄,同時模版目錄的名稱與與控制的名稱一致(不包含Controller)。 5.1.6 建立 NewsController 對應的Action 模板文件 l/module/Application/view/application/news/index.phtml indexAction 使用的模板文件 l/module/Application/view/application/news/list.phtml listAction 使用的模板文件 l/module/Application/view/application/news/add.phtml addAction 使用的模板文件 l/module/Application/view/application/news/edit.phtml editAction 使用的模板文件 l/module/Application/view/application/news/delete.phtml deleteAction 使用的模板文件 從上面的4個模板文件可以看出一個規律,模板的文件名都是控制器Action 的名稱。其實在ZF2里面有一個默認規定,那就是在各個Action 進行模板渲染里默認搜索與Action名相同的模板文件,所以在會命名模板文件名的時候需要注意。如果不想使用默認的模板對應名稱,可以在控制器中返回模板時通過 setTemplate() 函數來設置自已需要的模板文件。為了方便項目的日后維護作者在此也建議不同的action對就不同的模板。 5.1.7 視圖中常用函數 l$this->doctype() 指定文件的文檔類型 l$this->headTitle()->appendName() 輸出文件標題 l$this->headMeta() 設置并輸出文件的Meta 屬性 l$this->headLink() ->prependStylesheet() 加載格式表文件 l$this->headScript()->prependFile() 加載 js 文件 l$this->basePath() 獲取網站根路徑 l$this->navigation()->menu() 輸出導航菜單 l$this->url() 設置超鏈接 l$this->content 輸出頁面內容(其實就是將其他頁面的內容輸出到布局頁面上來) l$this->escapeHmtl() 過濾HTML標簽 l$this->translate() 進行語言轉換(如果有設置多國語言支持) 以上是一些相對較為常用的函數功能,其他的函數可以查看Zend\View\Renderer\PhpRenderer.php 文件中的相關描述 5.2 模板配置 建立好布局文件、模板文件之后需要對他們進行配置,以便后續使用中能夠讓ZF2自動的搜索并加載模板文件。其實對模板的配置在前面的章節已經有一些講解,因為模板的配置也是一個重點的內容,在此對模板的配置再詳細的解釋一遍。打開文件:/module/Application/config/module.config.php 在配置文件中加入如下代碼(已有就不用添加): 'view_manager' => array( 'display_not_found_reason' => true, 'display_exceptions' => true, 'doctype' => 'HTML5', 'not_found_template' => 'error/404', 'exception_template' => 'error/index', 'template_map' => array( 'layout/layout' => __DIR__ . '/../view/layout/layout.phtml', 'error/404' => __DIR__ . '/../view/error/404.phtml', 'error/index' => __DIR__ . '/../view/error/index.phtml', ), 'template_path_stack' => array( 'application' => __DIR__ . '/../view' ), ), view_manager 節點與 router、controllers 節點屬于同一級。 lview_manager 表示此數組塊為視圖管理器配置信息段 lview_manager-->display_not_found_reason 是否顯示404原因 lview_manager-->display_exceptions 是否顯示異常信息 lview_manager-->doctype 指定視圖頁面類型 lview_manager-->not_found_template 指定404的視圖模板 lview_manager-->exception_template 指定異常模板文件 lview_manager-->template_map 視圖模塊地圖 lview_manager-->template_map-->’layout/layout’ 指定布局文件 lview_manager-->template_map-->’error/404’ 指定404頁面的視圖文件 lview_manager-->template_map-->’error/index’ 指定錯誤異常頁面的視圖文件 lview_manager-->template_path_stack 視圖模板堆棧路徑 lview_manager-->template_path_stack-->application 指定模塊application 視圖目錄所在路徑 經過以上 建立目錄、文件、配置后 NewsController 就是可以直接自動的搜索對應的布局和模板等資源文件。由于以上都只是建立了空文件,在實際應用中沒有什么意義,所以接下將對布局、錯誤異常的模板添加一些代碼,致以news 目錄里的模板文件的內容將在后續的講解中逐一的進行添加填充。 5.3 編寫布局和錯誤異常模板 為了使所有的模板文件能夠看起來足夠簡潔和便于理解,在模板文件的編寫上將盡量的用少的代碼及多的常用代碼來進行講解,而且在模板文件中較多的代碼仍為普通的HTML代碼;所以在下面涉及到的 css 文件和 js 文件都是以空文件的形式來引用,使用空文件的原因主要是為說明相關函數的功能特性,同時對模板文件進行解釋的主要內容也在ZF2的函數上。 5.3.1 模板文件layout.phtml 下面是代碼內容: <?php echo $this->doctype(); ?> <html> <head> <meta charset="utf-8"> <?php echo $this->headTitle($this->translate('doc title')); ?> <?php echo $this->headMeta()->appendName('viewport', 'width=device-width, initial-scale=1.0'); ?> <?php echo $this->headLink()->prependStylesheet($this->basePath() . '/css/style.css'); ?> <?php echo $this->headScript()->prependFile($this->basePath() . '/js/jquery.min.js'', 'text/javascript');?> </head> <body> <table border="1" cellspacing="0" cellspadding="0" width="100%"> <tr> <td>header</td> </tr> <tr><td><?php echo $this->content; ?></td></tr> <tr><td>footer</td></tr> </table> </body> </html> 模板內容解釋: lecho $this->doctype() 文檔使用的類型,這個類型與在模塊配置文件中設置的類型有關 lecho $this->headTitle(); 輸出文檔標題 l$this->translate('doc title') 轉換文檔標題,此函數功能的實現需要語言包的支持 lecho $this->headMeta() 輸出HTML人 Meta 屬性 lappendName('viewport', 'width=device-width, initial-scale=1.0') 設置Meta 屬性的具體內容 lecho $this->headLink() 輸出link標簽 lprependStylesheet($this->basePath() . '/css/style.css') 設置link標簽屬性 l$this->basePath() 獲取站點根路徑 lecho $this->headScript() 輸出 script 標簽 lprependFile($this->basePath() . '/js/jquery.min.js'', 'text/javascript') 設置 script 標簽屬性 lecho $this->content 輸出控制器對應的模板頁面內容 以上的內容就是一個簡單的layout 而已模板,沒有復雜的代碼,沒有復雜的樣式;布局的結構最后將呈現出 上-中-下 的三行結構;上部放置導航內容,中部放置頁面主要內容,下部放置版權信息等。所以最終看到的界面大概如下所示: header 頭部內容 content 正文內容 footer 底部內容 5.3.2 錯誤異常模板 index.phtml 下面是代碼內容: <h1><?php echo $this->translate('An error occurred') ?></h1> <h2><?php echo $this->message ?></h2> <?php if (isset($this->display_exceptions) && $this->display_exceptions): ?> <?php if(isset($this->exception) && $this->exception instanceof Exception): ?> <hr/> <h2><?php echo $this->translate('Additional information') ?>:</h2> <h3><?php echo get_class($this->exception); ?></h3> <dl> <dt><?php echo $this->translate('File') ?>:</dt> <dd> <pre class="prettyprint linenums"><?php echo $this->exception->getFile() ?>:<?php echo $this->exception->getLine() ?></pre> </dd> <dt><?php echo $this->translate('Message') ?>:</dt> <dd> <pre class="prettyprint linenums"><?php echo $this->exception->getMessage() ?></pre> </dd> <dt><?php echo $this->translate('Stack trace') ?>:</dt> <dd> <pre class="prettyprint linenums"><?php echo $this->exception->getTraceAsString() ?></pre> </dd> </dl> <?php $e = $this->exception->getPrevious(); if ($e) : ?> <hr/> <h2><?php echo $this->translate('Previous exceptions') ?>:</h2> <ul> <?php while($e) : ?> <li> <h3><?php echo get_class($e); ?></h3> <dl> <dt><?php echo $this->translate('File') ?>:</dt> <dd> <pre class="prettyprint linenums"><?php echo $e->getFile() ?>:<?php echo $e->getLine() ?></pre> </dd> <dt><?php echo $this->translate('Message') ?>:</dt> <dd> <pre class="prettyprint linenums"><?php echo $e->getMessage() ?></pre> </dd> <dt><?php echo $this->translate('Stack trace') ?>:</dt> <dd> <pre class="prettyprint linenums"><?php echo $e->getTraceAsString() ?></pre> </dd> </dl> </li> <?php $e = $e->getPrevious(); endwhile; ?> </ul> <?php endif; ?> <?php else: ?> <h3><?php echo $this->translate('No Exception available') ?></h3> <?php endif ?> <?php endif ?> 代碼解釋: lecho $this->message 輸出錯誤信息 lif (isset($this->display_exceptions) && $this->display_exceptions) 判斷是否顯示異常信息 lecho get_class($this->exception) 輸出異常類型名稱 lecho $this->exception->getFile() 輸出導致異常的文件名 lecho $this->exception->getLine() 輸出導致異常文件的所在行 lecho $this->exception->getMessage() 輸出異常信息 lecho $this->exception->getTraceAsString() 輸出異常堆棧信息 l$e = $this->exception->getPrevious() 獲取上一個異常 以上是錯誤異常模板內容,模板能夠輸出導致錯誤異常的文件名、出錯所在行、錯誤類型等信息。在開發項目的時候便可以通過錯誤的信息提示來查找相關出錯原因。理解并正確使用使用錯誤信息能夠有效的提高開發效率。 5.3.3 404錯誤模板 404.phtml 下面是代碼內容: <h1><?php echo $this->translate('A 404 error occurred') ?></h1> <h2><?php echo $this->message ?></h2> <?php if (isset($this->reason) && $this->reason): ?> <?php $reasonMessage= ''; switch ($this->reason) { case 'error-controller-cannot-dispatch': $reasonMessage = $this->translate('The requested controller was unable to dispatch the request.'); break; case 'error-controller-not-found': $reasonMessage = $this->translate('The requested controller could not be mapped to an existing controller class.'); break; case 'error-controller-invalid': $reasonMessage = $this->translate('The requested controller was not dispatchable.'); break; case 'error-router-no-match': $reasonMessage = $this->translate('The requested URL could not be matched by routing.'); break; default: $reasonMessage = $this->translate('We cannot determine at this time why a 404 was generated.'); break; } ?> <p><?php echo $reasonMessage ?></p> <?php endif ?> <?php if (isset($this->controller) && $this->controller): ?> <dl> <dt><?php echo $this->translate('Controller') ?>:</dt> <dd><?php echo $this->escapeHtml($this->controller) ?> <?php if (isset($this->controller_class) && $this->controller_class && $this->controller_class != $this->controller ) { echo '(' . sprintf($this->translate('resolves to %s'), $this->escapeHtml($this->controller_class)) . ')'; } ?> </dd> </dl> <?php endif ?> <?php if (isset($this->display_exceptions) && $this->display_exceptions): ?> <?php if(isset($this->exception) && $this->exception instanceof Exception): ?> <hr/> <h2><?php echo $this->translate('Additional information') ?>:</h2> <h3><?php echo get_class($this->exception); ?></h3> <dl> <dt><?php echo $this->translate('File') ?>:</dt> <dd> <pre class="prettyprint linenums"><?php echo $this->exception->getFile() ?>:<?php echo $this->exception->getLine() ?></pre> </dd> <dt><?php echo $this->translate('Message') ?>:</dt> <dd> <pre class="prettyprint linenums"><?php echo $this->exception->getMessage() ?></pre> </dd> <dt><?php echo $this->translate('Stack trace') ?>:</dt> <dd> <pre class="prettyprint linenums"><?php echo $this->exception->getTraceAsString() ?></pre> </dd> </dl> <?php $e = $this->exception->getPrevious(); if ($e) : ?> <hr/> <h2><?php echo $this->translate('Previous exceptions') ?>:</h2> <ul> <?php while($e) : ?> <li> <h3><?php echo get_class($e); ?></h3> <dl> <dt><?php echo $this->translate('File') ?>:</dt> <dd> <pre class="prettyprint linenums"><?php echo $e->getFile() ?>:<?php echo $e->getLine() ?></pre> </dd> <dt><?php echo $this->translate('Message') ?>:</dt> <dd> <pre class="prettyprint linenums"><?php echo $e->getMessage() ?></pre> </dd> <dt><?php echo $this->translate('Stack trace') ?>:</dt> <dd> <pre class="prettyprint linenums"><?php echo $e->getTraceAsString() ?></pre> </dd> </dl> </li> <?php $e = $e->getPrevious(); endwhile; ?> </ul> <?php endif; ?> <?php else: ?> <h3><?php echo $this->translate('No Exception available') ?></h3> <?php endif ?> <?php endif ?> 代碼解析: lecho $this->message 輸出錯誤信息 lif (isset($this->reason) && $this->reason) 判斷是否存在錯誤,$this->reason 有多種類型,控制器路由分發錯誤(error-controller-cannot-dispatch)、控制器不存在(error-controller-not-found)、無效控制器(error-controller-invalid)、路由不匹配(error-router-no-match)及其他 l$this->controller 表示控制器名 l$this->controller_class 表示控制器類名 以上內容是404錯誤提示模板內容;模板能夠輸出導致錯誤異常的文件名、出錯所在行、錯誤類型等信息。在后繼開發中可以通過404的相關提示信息找到出錯的問題點。 5.4 編寫Action 對應的模板文件 針對于 Action 對應的模板文件在此就先只寫一個indexAction 對應的模板文件,其他各個Action對應的模板文件內容將在后續的講解中添加,以便與其他內容相互對應。 下面是 index.phtml 內容: <table border="1"> <tr> <td>Welcome to ZF2 world</td> </tr> </table> 模板的內容極其簡單,就是一個表格并在單元格中有句 Welcome to ZF2 world,他的意義就是在訪問 indexAction 的時候可以在頁面上看 Welcome to ZF2 world。 5.5 訪問 IndexAction 經過以上眾多的準備工作,現在我們已經可以通過 http://localhost/news 來訪問到我們的 NewsController 控制器的indexAction 對應的模板了。在訪問地址前我們先將之前文件 /module/Application/src/Application/Controller/NewsController.php 中的indexAction 函數進行一些修改。具體修改如下: public function indexAction(){ $view = new ViewModel(); return $view; } 講解: l$view = new ViewModel() 實例化一個視圖模型,視圖模型前面已經講解,主要是用來解析模板 lreturn $view 將視圖模型返回給前端控制器 現在可以通過 http://localhost/news 來打開我們的網頁了,這時我們的頁面應該顯示如下類似表格: header Welcome to ZF2 world footer 這個表格就是我們在 index.phtml模板中編寫的表格。但為什么會在 Welcome to ZF2 world 的上面出現header,下面出footer 呢?其實header和footer 是由我們的layout 布局模板所產生的,前面在講解布局模板文件的時候我們有說到我們的布局是 “上-中-下“這樣的一個結構;header 就是表示我們將來的導航條,footer 就是表示我們將來的版權信息。 通過上面的代碼可以看出 $view 視圖模型并沒有指定使用的模板文件,但ZF2卻能夠準確的找到 index.phtml模板文件。這是因為ZF2的默認模板搜索機制就是直接查找對應模塊下的視圖目錄,然后再根據模塊配置信息(module.config.php)來搜索相關目錄。其完整的搜索模式如下: ① 先到達模塊下的視圖目錄 ② 根據控制器名稱在視圖目錄找與控制器名稱相同的視圖子目錄 ③ 根據action名稱最終在視圖子目錄下找到與action名相同的模板文件 如果只是需要訪問一個默認的模板文件的話,還有一個更簡單的方式,就是在Action 函數里什么也不寫直接一個空函數,這樣控制器也可以根據框架的默認模板使用規則找到對應的模板。那當然你也可以通過$view視圖模型來指定你想使用的視圖模板。 以上內容就是關于視圖模板使用的主要內容,模板樣式可以根據自已或用戶的需求進行各種各樣的定制,可以把UI模板做得豐富多彩、漂亮。 第 6 章 創建模型 模型不僅是ZF2的重要組成部分,同時也是眾MVC框架的重要組成部分。他的重要性主要在于處理用戶與數據庫之間的訪問與操作功能。ZF2 本身并不直接直接提供 Zend\Model 組件,因為模型是一種業務邏輯,針對不同的項目可能會用不同的商業業務處理邏輯,模型的具體工作流程取決于你對相關模型組件個體設計。雖然ZF2本身并不提供模型,但ZF2提供了很多用于實現用戶模型的各種組件,用戶用通過ZF2提供的組件來構建自已的模型類,并且可以通過映射器對其進行映射,以方便應用程序的前端控制對他進行引用及使用,最終實現對數據庫的一系列操作。 模型類的編寫沒有一個統一的寫法,不同的人有不同的想法及寫法,不同的業務邏輯有不同的實現方法,最終需求根據實際情況進行編寫。在本章節中將介紹兩種作者在開發中常用到的寫法,一種是ORM對象映射技術,另一種是自定義對象。本章節的內容會結合前面章節中包括內容 控制器、視圖模板等,同時也會涉及到ZF2 Zend\Db,Zend\Form 等相關知識內容進行綜合的講解。 6.1 ORM 對象映射法 ORM 對象映射法是在ZF2開發指南中引用的一種模型編寫方法,可以當作是ZF2的推薦寫法,此方法的實現主要通過TableGateway(作者稱為數據庫網關);此方法通過Di來實現,對其進行引用前需要對他做相關配置工作;總的來說引用簡單、模型與模塊關聯性較強。 在編寫模型代碼前先進行數據表的設計,數據表創建在Mysql數據庫的test默認數據庫里表名為news;以下里數據表的設計及多條測試數據。 CREATE TABLE news (id int(10) NOT NULL AUTO_INCREMENT,title varchar(100) NOT NULL,content varchar(1000) NOT NULL,PRIMARY KEY(id)); INSERT INTO news(title,content) VALUES(‘First news’,’This is the first news’); INSERT INTO news(title,content) VALUES(‘Second news’,’This is the second news’); INSERT INTO news(title,content) VALUES(‘Third news’,’This is the third news’); INSERT INTO news(title,content) VALUES(‘fourth news’,’This is the fourth news’); INSERT INTO news(title,content) VALUES(‘Fifth news’,’This is the fifth news’); INSERT INTO news(title,content) VALUES(‘Sixth news’,’This is the sixth news’); 已經有了數據庫、數據表、數據需要對數據庫的訪問屬性(數據庫適配器Adapter)進行設置后模型才能夠正常的連接到我們的數據庫,找到文件 /config/autoload/global.php 文件內容如下: return array( 'db' => array( 'driver' => 'Pdo', 'dsn' => 'mysql:dbname=test;host=localhost', 'driver_options' => array( PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES \'UTF8\'' ), ), 'service_manager' => array( 'factories' => array( 'Zend\Db\Adapter\Adapter' => 'Zend\Db\Adapter\AdapterServiceFactory' ), ), ); ldb 表示數據庫配置信息節點 ldriver 表示數據庫使用的驅動程序類型 ldsn 數據庫連接串,也稱為數據源 ldriver_options 數據庫驅動選項 lservice_manager 表示服務器管理器節點 lfactories 表示服務器管理器需要加載的工廠類 為要安全起見,將數據庫的用戶名與密碼寫入到 /config/autoload/local.php 文件,你同樣也可以將他寫入到global文件的db 節點中。local.php文件內容如下: return array( 'db' => array( 'username' => 'root', 'password' => '' ), ); 6.1.1 創建 News 類 News 類主要包括數據表中個各字段的映射,以及實現數組與對象之間的數據轉換 路徑:/module/Application/src/Application/Model/News.php 在文件中添加收下代碼: namespace Application\Model; class News { public $id; public $title; public $content; public function exchangeArray($data){ $this->id = (isset($data['id'])) ? $data['id'] : null; $this->artist = (isset($data['title'])) ? $data['title'] : null; $this->title = (isset($data['content'])) ? $data['content'] : null; } public function getArrayCopy(){ return get_object_vars($this); } } 代碼講解: lpublic $id,$title,$content 這些公共變量與數據表字段一一對應 lpublic function exchangeArray($data) 對數組數據進行轉換或都說是提取數組數據 lpublic function getArrayCopy() 將類屬性轉化為一個關聯數組,方便后續的使用 6.1.2 創建 NewsTable 類 NewsTable 類的主要是通過TableGateway 數據網關來實現對數據庫操作。 路徑:/module/Application/src/Application/Model/NewsTable.php 在文件中添加以下代碼: namespace Application\Model; use Zend\Db\TableGateway\TableGateway; use Zend\Db\ResultSet\ResultSet; use Zend\Db\Sql\Select; class NewsTable { protected $tableGateway; public function __construct(TableGateway $tg) { $this->tableGateway = $tg; } public function fetchAll() { $resultSet = $this->tableGateway->select(); return $resultSet; } } public function __construct(TableGateway $tg) 構造函數 public funciton fetchAll() 獲取數據表的數據 6.1.3 使用模型讀取數據庫數據 在使用模型的時候需要對其他進行模塊配置,以便ZF2能夠地運行的時候自動加載。 6.1.3.1 模塊配置 找到文件 /module/Application/Module.php ,在添加函數的時候注意導入相關的命名空間,添加函數 public function getServiceConfig(){},函數名稱是固定的,ZF2會在運行的時候自動調用Module 中的全部方法。添加內容后的文件如下: namespace Application; use Zend\Mvc\ModuleRouteListener; use Zend\Mvc\MvcEvent; use Zend\Db\ResultSet\ResultSet; use Zend\Db\TableGateway\TableGateway; use Application\Model\News; use Application\Model\NewsTable; class Module { public function onBootstrap(MvcEvent $e){ $eventManager = $e->getApplication()->getEventManager(); $moduleRouteListener = new ModuleRouteListener(); $moduleRouteListener->attach($eventManager); } public function getConfig(){ return include __DIR__ . '/config/module.config.php'; } public function getAutoloaderConfig(){ return array( 'Zend\Loader\StandardAutoloader'=>array( 'namespaces'=>array( __NAMESPACE__ => __DIR__ . '/src/' . __NAMESPACE__ ) ) ); } public function getServiceConfig(){ return array( 'factories'=>array( 'Application\Model\NewsTable'=>function($sm){ $tg = $sm->get('NewsTableGateway'); $table = new NewsTable($tg); return $table; }, 'NewsTableGateway'=>function($sm){ $adapter = $sm->get('Zend\Db\Adapter\Adapter'); $rs = new ResultSet(); $rs->setArrayObjectPrototype(new News()); return new TableGateway('news',$adapter,null,$rs); } ), ); } } 通過以上的函數就配置好了模塊對模型的引用,從函數getServiceConfig 的內容中可以看出函數本身只返回一個關聯數組,這個關聯數據的 鍵-值 都將在后續中被引用;同時也可以看出我們目錄的配置是針對news 表的操作,也是為什么我們在上面的模型中fetchAll()函數里沒有看到數據表的原因。 6.1.3.2 控制器中使用模型 找到文件 /module/Application/src/Application/Controller/NewsController.php,添加函數 public function getNewsTable(){},同時修改 public function listAction(){}函數內容,注意導入相關包;文件修改后如下: namespace Application\Controller; use Zend\Mvc\Controller\AbstractActionController; use Zend\View\Model\ViewModel; use Application\Model\NewsTable; class NewsController extends AbstractActionController{ protected $newsTalbe; public function __construct(){ } public function indexAction(){ $view = new ViewModel(); return $view; } public function listAction(){ $paginator = $this->getNewsTalbe()->fetchAll(); var_dump($paginator); exit; } public function addAction(){ echo 'NewsController addAction'; exit; } public function editAction(){ echo 'NewsController editAction'; exit; } public function deleteAction(){ echo 'NewsController deleteAction'; exit; } public function getNewsTalbe(){ if(!$this->newsTalbe){ $sm = $this->getServiceLocator(); $this->newsTalbe = $sm->get('Application\Model\NewsTable'); } return $this->newsTalbe; } } lpublic function getNewsTalbe(){} 的主要工作就是完成對數據網關的實例化 l$sm = $this->getServiceLocator() 獲取本地已經初化的服務管理器及服務 l$this->newsTalbe = $sm->get('Application\Model\NewsTable') 獲取在模塊文件中的相關函數 l$paginator = $this->getNewsTalbe()->fetchAll() 通過模型(數據網關)訪問數據庫 通過添加以上代碼就可以通過 http://localhost/news/list 來查看模型對數據庫的相關操作信息了。在此處只是通過 var_dump 函數對模型的操作結果進行打印輸出,而并沒有通過模板來呈現;要想通過模板來呈現模型對數據庫查詢的結果還需要進行一些小的修改。 6.1.3.3 通過模板顯示數據庫查詢結果 模板是匯集網站應用所有操作的一個最終集合點,最終將所有匯集的數據集中展現給用戶。在使用模板前我們還得修改下控制器,以便控制器能將模型操作的結果傳遞到模板中去。修改 listAction 控制器內容為: public function listAction(){ $paginator = $this->getNewsTalbe()->fetchAll(); $view = new ViewModel(); $view->setTemplate('application/news/list.phtml'); $view->setVariable('paginator', $paginator); return $view; } l$paginator = $this->getNewsTalbe()->fetchAll() 獲取模型查詢的數據 l$view = new ViewModel() 實例化一個視圖模型 l$view->setTemplate('application/news/list.phtml') 設置視圖模型所使用的模板 l$view->setVariable('paginator', $paginator) 給視圖傳遞數據 lreturn $view 將視圖模型返回給前端控制器 或者是使用以下代碼: public function listAction(){ $paginator = $this->getNewsTalbe()->fetchAll(); return new ViewModel(array('paginator'=>$paginator)); } 以后兩種方法的最終結果是一樣的。 接下來修改我們的模板文件 /module/Application/view/application/news/list.phtml,模板的內容如下: <table> <tr> <th>Title</th> <th>Content</th> <th>Add news</a></th> </tr> <?php foreach ($paginator as $news) : ?> <tr> <td><?php echo $this->escapeHtml($news->title); ?></td> <td><?php echo $this->escapeHtml($news->content); ?></td> <td> <a href="<?php echo $this->url('news', array('action' => 'edit', 'id' => $news->id));?>"><?php echo $this->translate("Edit") ?></a> <a href="<?php echo $this->url('news', array('action' => 'delete', 'id' => $news->id));?>"><?php echo $this->translate("Delete") ?></a> </td> </tr> <?php endforeach; ?> </table> lforeach ($paginator as $news) 使用foreach 來循環模型查詢結果的數據行 lecho $this->escapeHtml($news->title) 通過對象操作方式輸出新聞標題 lecho $this->escapeHtml($news->content) 通過對象操作方式輸出新聞內容 lecho $this->url('news', array('action' => 'edit', 'id' => $news->id)) 通過url 方法構造編輯新的鏈接 lecho $this->url('news', array('action' => 'delete', 'id' => $news->id)) 通過url 方法構造刪除新的鏈接 現在通過 http://localhost/news/list 看看是不是已經把之前我們插入到數據的數據已經全部輸出了呢。結果如下所示: header Title Content Add news First news This is the first news Edit Delete Second news This is the second news Edit Delete Third news This is the third news Edit Delete fourth news This is the fourth news Edit Delete Fifth news This is the fifth news Edit Delete Sixth news This is the sixth news Edit Delete footer 6.1.3.4 插入數據 插入數據的功能通過添加新聞的方式來進行講解,在使用插入數據的功能時同時涉及到過濾器、表單生成的相關內容,本小節將這三個內容進行結合講解。 6.1.3.4.1 創建表單文件 添加表單文件,路徑:/module/Application/src/Application/Form/NewsForm.php 內容如下: namespace Application\Form; use Zend\Form\Form; class NewsForm extends Form{ public function __construct($name='news') { parent::__construct($name); $this->setAttribute('method', 'post'); $this->add(array( 'name'=>'id', 'type'=>'Hidden' )); $this->add(array( 'name'=>'title', 'type'=>'Text', 'options'=>array( 'label'=>'Title' ), )); $this->add(array( 'name'=>'content', 'type'=>'Text', 'options'=>array( 'label'=>'Content' ), )); $this->add(array( 'name'=>'submit', 'type'=>'submit', 'attributes'=>array( 'value'=>'Go', 'id'=>'submit' ), )); } } 代碼解析: lpublic function __construct($name='news') 就是一個普通的構造函數,$name 為表單名稱 l$this->setAttribute('method', 'post') 設置表單屬性 l$this->add(array('name'=>'id','type'=>'Hidden')); 添加一個表單隱藏域,作為新聞ID l$this->add(array('name'=>'title','type'=>'Text','options'=>array('label'=>'Title' ))); 添加一個input 標簽,作為新聞標題輸入 l$this->add(array('name'=>'content','type'=>'Text','options'=>array('label'=>'Content'))); 添加一個input標簽,作為新聞內容輸入 l$this->add(array('name'=>'submit','type'=>'submit','attributes'=>array('value'=>'Go','id'=>'submit'))); 添加一個提交按鈕 以上代碼就包含了一個新聞記錄所需的全部表單元素。 6.1.3.4.2 添加過濾器 文件:/module/Application/src/Application/Model/News.php 在此文件原來的基礎上添加了內容,文件內容: namespace Application\Model; use Zend\InputFilter\Factory as InputFactory;// 新加導入包 use Zend\InputFilter\InputFilter;// 新加導入包 use Zend\InputFilter\InputFilterAwareInterface;// 新加導入包 use Zend\InputFilter\InputFilterInterface;// 新加導入包 class News implements InputFilterAwareInterface {// 添加了接口 public $id; public $content; public $title; protected $inputFilter; public function exchangeArray($data){ $this->id = (isset($data['id'])) ? $data['id'] : null; $this->content = (isset($data['content'])) ? $data['content'] : null; $this->title = (isset($data['title'])) ? $data['title'] : null; } public function getArrayCopy(){ return get_object_vars($this); } public function getInputFilter() {// 新添加,實現接口方法 if(!$this->inputFilter){ $this->inputFilter = new InputFilter(); $factory = new InputFactory(); $this->inputFilter->add($factory->createInput(array( 'name'=>'id', 'required'=>true, 'filters'=>array( array('name'=>'Int'), ), ))); $this->inputFilter->add($factory->createInput(array( 'name'=>'content', 'required'=>true, 'filters'=>array( array('name'=>'StripTags'), array('name'=>'StringTrim'), ), 'validators'=>array( array( 'name'=>'StringLength', 'options'=>array( 'encoding'=>'UTF-8', 'min'=>5, 'max'=>100, ), ), ), ))); $this->inputFilter->add($factory->createInput(array( 'name'=>'title', 'required'=>true, 'filters'=>array( array('name'=>'StripTags'), array('name'=>'StringTrim'), ), 'validators'=>array( array( 'name'=>'StringLength', 'options'=>array( 'encoding'=>'UTF-8', 'min'=>5, 'max'=>100, ), ), ), ))); } return $this->inputFilter; } public function setInputFilter(InputFilterInterface $inputFilter) {// 新添加,實現接口方法 throw new \Exception('Not used'); }代碼解析: lpublic function getInputFilter() 獲取收入類型過濾器,對指定的表單元素進行過濾。 l$this->inputFilter = new InputFilter(); 實例化一個InputFilter過濾器 l$factory= new InputFactory(); 實例化一個InputFactory 輸入工廠 l$this->inputFilter->add($factory->createInput(array('name'=>'id','required'=>true,'filters'=>array(array('name'=>'Int'))))); 創建過濾規則并將附加到InputFilter上,規則內容:name為id的標簽為必填項,并且限制為整形輸入 l$this->inputFilter->add($factory->createInput(array('name'=>'content','required'=>true,'filters'=>array(array('name'=>'StripTags'),array('name'=>'StringTrim'))'validators'=>array(array('name'=>'StringLength','options'=>array('encoding'=>'UTF-8','min'=>5,'max'=>100))))));建過濾規則并將附加到InputFilter上,此處的過濾規則為一個過濾鏈,規則內容:name 為 content的標簽為必填項,并對其他輸入進行去HTML標簽(StripTags)和去空格(StringTrim)處理,同時對輸入內容進一步校驗,校驗規則為將輸入內容限制為utf-8,同時長度為5~100的個字符。 lpublic function setInputFilter(InputFilterInterface $inputFilter) 設置過濾,實現接口的方法 6.1.3.4.3 創建表單 通過上面兩個小節的內容已經完成了創建表單的基本要素,下面將通過控制器中的方法來引用上面的內容來生成一個新聞表單。 打開文件:/module/Application/src/Application/Controller/NewsController.php,添加如下內容: ①導入包 use Application\Form\NewsForm; use Application\Model\News; ②修改public function addAction(){} 函數內容,具體內容如下: public function addAction(){ $form = new NewsForm(); $form->get('submit')->setValue('Add'); $request = $this->getRequest(); if($request->isPost()){ $news= new News(); $form->setInputFilter($news->getInputFilter()); $form->setData($request->getPost()); if($form->isValid()){ $album->exchangeArray($form->getData()); $this->getNewsTalbe()->saveNews($news); return $this->redirect()->toRoute('news');// 或者使用URL$this->redirect()->toUrl('/news/list'); } } return array('form'=>$form); } addAction 函數內容代碼解釋: l$form = new NewsForm(); 實例化一個新聞表單 l$form->get('submit')->setValue('Add');修改新聞表單的提交按鈕名稱 l$request = $this->getRequest(); 獲取用戶請求 lif($request->isPost()){} 判斷 是否為 POST請求 l$form->setInputFilter($news->getInputFilter()); 為表單添加過濾器 l$form->setData($request->getPost()); 設置表單數據 lif($form->isValid()){} 判斷表單是否通過校驗 l$news->exchangeArray($form->getData()); 能表單數據進行轉換 l$this->getNewsTalbe()->saveNews($news); 通過模型將表單提交的數據保存到數據庫里 lreturn $this->redirect()->toRoute('news'); 實現路由跳轉 lreturn array('form'=>$form); 返回一個表單對象 6.1.3.4.4 模板輸出表單 收到從控制器中傳遞過來數據并將數據在模板中輸出,打開文件:/module/Application/view/application/news/add.phtml,文件具體內容如下: $form = $this->form; // 接收到控制器傳遞過來的表單對象 $form->setAttribute('action',$this->url('news',array('action'=>'add')));// 設置表單的action屬性 echo $this->form()->openTag($form);// 打開form表單 echo $this->formCollection($this->form);// 輸出表單里的元素集合 echo $this->form()->closeTag();// 閉合form表單 此處是使用簡潔法輸出表單,即通過打開表單,輸出表單、閉合表單這個動作一次性把表單里的所有元素輸出。這種方法的好處是只用3行代碼就能把表單里的全部元素輸出,缺點就是全部屬性都使用$form對象的默認設置屬性,靈活度沒那么好。另一種表單輸出的方法就是對$form表單對象里的元素一個一個輸出,并且可以對表單對象元素進行相關修改,靈活度較好,但代碼量較大。 通過前面四節的課內容現在可以通過 http://localhost/news/add 打開新聞表單了,并可以通過表單將將數據提交到數據庫進行保存。頁面結果如下: header 窗體頂端 Title窗體底端 Content footer 6.1.3.4.5 添加模型方法saveNews 要把新聞表單的數據能夠提交到數據庫中進行保存,還需要在模型中添加保存新聞的模型方法,打開模型文件 /module/Application/src/Application/Model/NewsTables.php 文件,添加如下方法: public function saveNews(News $news) { $data = array( 'content' =>$news->content, 'title' =>$news->title ); $id = (int) $news->id; if($id == 0){ $this->tableGateway->insert($data); }else{ if($this->getNews($id)){ $this->tableGateway->update($data,array('id'=>$id)); }else{ throw new \Exception("Could not find row {$id}"); } } } 代碼解釋: l$data = array( 'content' =>$news->content,'title' =>$news->title); 將傳遞過來的數據保存到數組中,因為在ZF2中對數據的操作很多是通過數組來傳遞的 l$this->tableGateway->insert($data); 如果id不存在的時候將數據里的數據插入到數據庫,此處實現插入功能 l$this->tableGateway->update($data,array('id'=>$id)); 如果id存在的時候,對數據庫里指定id的數據行進行更新 lthrow new \Exception("Could not find row {$id}"); 如果更新出現錯誤則拋出一個異常 public function saveNews(News $news){} 方法說明 ,此方法不單用來保存添加新聞時的數據,也將用來保存更新新聞內容后的數據,即包含了插入和更新功能。 模型方法saveNews 建立好后就可以通過 http://loaclhost/news/add 來添加新聞并保存到數據庫了。 6.1.3.4.6 修改新聞內容 上面一節內容已經講解了怎么通過表單將一個新插入到數據庫里,接下來就是要實現如果使用表單來修改一條新聞記錄并將他保存到數據庫。在前一節講解內容的時候已經說過 saveNews 保存數據功能不僅用于添加新聞,也用于新聞的修改,表單也是重用之前內容的表單,所以這些部分的內容就不再重復進行講解。下面將重點放在控制器的 editAction方法和edit.phtml模板中。 6.1.3.4.6.1修改模塊路由 在繼續制作editAction 和 edit.phtml 前我需要對我們的module.config.php 的模塊文件做一個小的修改,在修改前可以看一下之前輸出的新聞列表的最后一個列中 Edit 種 Delete 的鏈接,看看鏈接地址的后面是不是沒有出現我們平時做網站時應該出現的id 值。這是由于我們之前對模塊路由的配置中并沒有包括對參數傳遞的功能,如果路由上沒有配置這些傳遞參數的功能,即使你強行在鏈接地址的后面加上去也會被路由匹配規則給過濾掉,最終可能導致一個404的錯誤出現。 打開文件:/module/Application/config/module.config.php 將路由 news 區段修改為如下內容: 'news'=>array( 'type'=>'segment', 'options'=>array( 'route'=>'/news[/][:action][/:id]', 'constraints'=>array( 'action'=>'[a-zA-Z]*', 'id'=>'[0-9]+' ), 'defaults'=>array( 'controller'=>'Application\Controller\News', 'action'=>'index' ), ), ), 路由做過調整的地方: l'route'=>'/news[/][:action]' 修改為 route'=>'/news[/][:action][/:id]', l'id'=>'[0-9]+' 添加了路由中id 的匹配規則,只匹配數字類型的id 添加模型方法 public function getNews($id){},此方法功能是根據$id查找數據庫中的新聞記錄并返回查詢結果行。打開文件:/module/Application/src/Application/Model/NewsTable.php 在文件原來的基礎上添加如下內容: public function getNews($id) { $id = (int) $id; $rowset = $this->tableGateway->select(array('id'=>$id)); $row = $rowset->current(); if(!$row){ throw new \Exception("Could not find row {$id}"); } return $row; } 模型方法內容解釋: l$id = (int) $id; 將傳遞過來的id強制轉換為整形 l$rowset = $this->tableGateway->select(array('id'=>$id)); 根據id查詢新聞結果集 l$row = $rowset->current(); 取出結果集的第一行記錄 lif(!$row){} 判斷是否存在指定id 的新聞記錄行,如果不存在則拋出一個異常 lreturn $row 返回查詢結果的新聞記錄行 6.1.3.4.6.2修改editAction 方法 打開文件:/module/Application/src/Application/Controller/NewsController.php,找到editAction 方法并將內容修改為如下: public function editAction(){ $id = (Int) $this->params()->fromRoute('id',0); if(!$id){ return $this->redirect()->toRoute('news',array('action'=>'add')); } try{ $news = $this->getNewsTalbe()->getNews($id); }catch(\Exception $e){ return $this->redirect()->toRoute('news',array('action'=>'list')); } $form = new NewsForm(); $form->bind($news); $form->get('submit')->setAttribute('value', 'Edit'); $request = $this->getRequest(); if($request->isPost()){ $form->setInputFilter($news->getInputFilter()); $form->setData($request->getPost()); if($form->isValid()){ $this->getNewsTalbe()->saveNews($news); $this->redirect()->toUrl('/news/list'); } } return array('id'=>$id,'form'=>$form); } 代碼解釋: l$id = (Int) $this->params()->fromRoute('id',0); 從路由中分離id,也就是獲取新聞id lif(!$id){} 如果id 不存在則直接跳轉到添加新聞頁面 l$news = $this->getNewsTalbe()->getNews($id); 通過數據網關獲取指定id的新聞記錄 lreturn $this->redirect()->toRoute('news',array('action'=>'list')); 如果在獲取新聞記錄中出現異常則直接跳轉到列表頁 l$form = new NewsForm(); 實例化一個新聞表單 l$form->bind($news); 給表單綁定數據 l$form->get('submit')->setAttribute('value', 'Edit');設置表單提交按鈕名稱 l$request = $this->getRequest(); 獲取用戶請求 lif($request->isPost()){} 判斷是否通過post提交的請求 l$form->setInputFilter($news->getInputFilter()); 為表單添加過濾器 l$form->setData($request->getPost());為表單附加數據 lif($form->isValid()){} 判斷表單數據是否通過校驗 l$this->getNewsTalbe()->saveNews($news);將編輯后的數據更新到數據庫 l$this->redirect()->toUrl('/news/list'); 跳轉到新聞列表 lreturn array('id'=>$id,'form'=>$form); 返回一個表單對象和新聞id到模板,此處的表單對象與前面章節中插入數據的表單有所區別,此表單里面的標簽都已經有數據的了,而之前插入新聞的表單只是一個空的表單。 6.1.3.4.6.3修改edit.phtml模板 打開文件:/module/Applicaiton/view/application/news/edit.phtml,將文件內容修改為如下: $form = $this->form; $form->setAttribute('action',$this->url('news',array('action'=>'edit','id'=>$this->id))); // 設置表單的action 屬性 echo $this->form()->openTag($form);// 打開form 表單 echo $this->formCollection($this->form);// 生成表單元素 echo $this->form()->closeTag();// 關閉表單 到目前為止就已經完成了新聞修改功能的全部工作,現在可以通過新聞列表中的 Edit 鏈接來打開修改新聞的頁面了,修改新聞的頁面與添加新聞的頁面外觀上看上去是一樣的;只不過新聞修改頁面多了一重判斷,當指定id的新聞記錄存在時則可以進行修改,如果指定的id還在,則進行的是添加功能。 6.1.3.4.7 刪除新聞記錄 本節將講解關于數據庫CURD中的最后一個是重要環節--數據庫的刪除操作,本章節所講解的主要任務是實現對指定新聞id的刪除功能。 6.1.3.4.7.1修改deleteAction 方法 打開文件:/module/Application/src/Application/Controller/NewsController.php,找到deleteAction 方法并將內容修改為如下: public function deleteAction(){ $id = (Int) $this->params()->fromRoute('id',0); if(!$id){ $this->redirect()->toUrl('/news/list'); } $request = $this->getRequest(); if($request->isPost()){ $del = $request->getPost('del','No'); if($del=='Yes'){ $id = (Int)$request->getPost('id'); $this->getNewsTalbe()->deleteNews($id); } $this->redirect()->toUrl('/news/list'); } return array('id'=>$id,'news'=>$this->getNewsTalbe()->getNews($id)); } 代碼解釋: l$id = (Int) $this->params()->fromRoute('id',0) 獲取新聞記錄id lif(!$id){$this->redirect()->toUrl('/news/list');} 判斷是否有傳遞id 值,如果沒有則直接跳轉到新聞列表頁面 lif($request->isPost()){} 判斷用戶請求類型是否為post 請求 l$del = $request->getPost('del','No'); 獲取用戶處理動作{Yes或No} lif($del=='Yes'){} 如果用戶操作就連Yes,則進行刪除操作 l$id = (Int)$request->getPost('id'); 獲取新聞id l$this->getNewsTalbe()->deleteNews($id); 刪除指定的新聞記錄 l$this->redirect()->toUrl('/news/list'); // 完成刪除后跳轉到新聞列表 lreturn array('id'=>$id,'news'=>$this->getNewsTalbe()->getNews($id)); 如果用戶請求為非post 請求,則返回數據給模板 6.1.3.4.7.2添加模型 deleteNews方法 打開模型文件 /module/Application/src/Application/Model/NewsTables.php 文件,添加如下方法: public function deleteNews($id) { $this->tableGateway->delete(array('id'=>$id)); } 代碼解釋: $this->tableGateway->delete(array('id'=>$id)); 根據傳遞過來的id刪除新聞記錄 6.1.3.4.7.2修改delete.phtml模板 打開文件:/module/Applicaiton/view/application/news/delete.phtml,將文件內容修改為如下: $title = 'Delete news'; $this->headTitle($title); ?> <h1><?php echo $this->escapeHtml($title); ?></h1> <p>Are you sure that you want to delete '<?php echo $this->escapeHtml($news->title); ?>' by '<?php echo $this->escapeHtml($news->content); ?>'? </p> <?php $url = $this->url('news', array( 'action' => 'delete', 'id' => $this->id, )); ?> <form action="<?php echo $url; ?>" method="post"> <div> <input type="hidden" name="id" value="<?php echo (int) $news->id; ?>" /> <input type="submit" name="del" value="Yes" /> <input type="submit" name="del" value="No" /> </div> </form> 代碼解釋: l$this->headTitle($title); 設置文件標題 lecho $this->escapeHtml($news->title); 輸出新聞標題 lecho $this->escapeHtml($news->content); 輸出新聞內容 l$url = $this->url('news', array('action' => 'delete','id' => $this->id)); 構造表單的action鏈接 以上為主要的php內容,致以表單中其他的html代碼就不再做解釋。下面轉到新聞列表頁面,http://localhost/news/list 在新聞列表中點擊Delete將跳轉到刪除的確認頁面,然后確認是否刪除。 6.2 使用分頁導航 當天新聞記錄不斷增加的時候,必然導致新聞列表不的加長以致用戶不能在一屏內顯示完所有內容,致使用戶需要不停的拉動滾動條來獲取更多的內容,這樣無形之中給用戶瀏覽新聞添加了不少障礙;因此對數據進行分頁將是必然。之前做過網站的都應該知道分頁樣式及功能的實現有多種方法,分頁可以根據不同的需要進行定制,但有一個缺點就是開發者基本上都需要自已寫一個分頁類庫來加以調用。ZF2為了減少開發自已類庫的麻煩ZF2類庫本身就已經集成了分頁的類庫,ZF2提供的分頁類庫簡單易用,開發都也可以根據需要重寫分頁類或對分類的CSS樣式進行重新設定;接下來的內容將重點講解ZF2分頁類庫的使用方法。 6.2.1 修改模塊配置文件 打開文件 /module/Application/config/module.config.php,對news 路由區段塊進行修改,具體修改內容如下: 'news'=>array( 'type'=>'segment', 'options'=>array( 'route'=>'/news[/][:action][[/:id][/page/:page]]', 'constraints'=>array( 'action'=>'[a-zA-Z]*', 'id'=>'[0-9]+', 'page'=>'[0-9]+', ), 'defaults'=>array( 'controller'=>'Application\Controller\News', 'action'=>'index' ), ), ) 修改的地址: l'route'=>'/news[/][:action][/:id]' 修改為 'route'=>'/news[/][:action][[/:id][/page/:page]]' 此處修改的主要作用是為使用路由能匹配出/page/n 這樣的分頁鏈接路徑 l添加page'=>'[0-9]+' 路由正則區別規則 6.2.2 修改模型文件 打開文件 /module/Application/src/Model/NewsTable.php,修改public function fetchAll(){}函數,具體內容如下: public function fetchAll($paginated=false) { if($paginated){ $select = new Select('news'); $rs = new ResultSet(); $rs->setArrayObjectPrototype(new News()); $pageAdapter = new DbSelect($select,$this->tableGateway->getAdapter(),$rs); $paginator = new Paginator($pageAdapter); return $paginator; } $resultSet = $this->tableGateway->select(); return $resultSet; } 代碼解釋: lif($paginated){} 判斷是否使用分頁 l$select = new Select('news'); 實例化一個 select ,對指定表進行操作 l$rs = new ResultSet(); 實例化一個結果集,用來保存查詢結果 l$rs->setArrayObjectPrototype(new News()); 設置結果集的操作屬性 l$pageAdapter = new DbSelect($select,$this->tableGateway->getAdapter(),$rs); 實例化一個DbSelect,并通過數據網關及select來對數據庫進行操作,并將最終結果傳遞到$rs結果集中 l$paginator = new Paginator($pageAdapter); 實例化一個分頁導航,并將DbSelect 傳遞過去 lreturn $paginator; 返回分頁導航實例 6.2.3 修改控制器文件 打開文件 /module/Application/src/Application/Controller/NewsController.php,對public function listAction(){} 方法進行修改,具體內容如下: public function listAction(){ $paginator = $this->getNewsTalbe()->fetchAll(true); $paginator->setCurrentPageNumber((int)$this->params()->fromRoute('page',1)); $paginator->setItemCountPerPage(5); return new ViewModel(array('paginator'=>$paginator)); } 代碼解釋: l$paginator = $this->getNewsTalbe()->fetchAll(true); 表示使用分頁技術進行操作 l$paginator->setCurrentPageNumber((int)$this->params()->fromRoute('page',1)); 設置當前頁,如果不存在頁面則默認設置為第一頁 l$paginator->setItemCountPerPage(5);設置每個分頁將顯示的記錄行數 lreturn new ViewModel(array('paginator'=>$paginator)); 將分頁導航對象返回給模板調用 6.2.4 添加分頁導航模板 添加文件 /module/Application/view/application/partial/parginator.phtml, 具體內容如下: <?php if ($this->pageCount): ?> <div class="pagination pagination-centered"> <ul> <!-- Previous page link --> <?php if (isset($this->previous)): ?> <li> <a href="<?php echo $this->url($this->route) . $this->action; ?>/page/<?php echo $this->previous;?>"><<</a> </li> <?php else: ?> <li class="disabled"> <a href="#"> << </a> </li> <?php endif; ?> <?php foreach ($this->pagesInRange as $page): ?> <?php if ($page != $this->current): ?> <li> <a href="<?php echo $this->url($this->route) . $this->action; ?>/page/<?php echo $page; ?>"> <?php echo $page; ?> </a> </li> <?php else: ?> <li class="active"> <a href="#"><?php echo $page; ?></a> </li> <?php endif; ?> <?php endforeach; ?> <?php if (isset($this->next)): ?> <li> <a href="<?php echo $this->url($this->route) . $this->action; ?>/page/<?php echo $this->next; ?>"> >> </a> </li> <?php else: ?> <li class="disabled"> <a href="#"> >> </a> </li> <?php endif; ?> </ul> </div> <?php endif; ?> 代碼解釋: lif ($this->pageCount) 判斷分頁數量決定是否顯示分頁導航 lif (isset($this->previous)) 判斷是否有上一頁 l<a href="<?php echo $this->url($this->route) . $this->action; ?>/page/<?php echo $this->previous;?>"><<</a>上一頁鏈接 lforeach ($this->pagesInRange as $page) 循環首頁鏈接頁碼 l<a href="<?php echo $this->url($this->route) . $this->action; ?>/page/<?php echo $page; ?>"><?php echo $page; ?></a>生成各頁的導航鏈接 lif (isset($this->next)) 判斷是否有下一頁 l<a href="<?php echo $this->url($this->route) . $this->action; ?>/page/<?php echo $this->next; ?>">>></a>下一頁鏈接 6.2.4 修改新聞列表模板 打開文件 /module/Appliction/view/application/news/list.phtml,在此文件末尾添加收下內容: <?php echo $this->paginationControl($this->paginator,'sliding',array('application/partial/paginator.phtml','News'),array('route'=>'news','action'=>'list')); ?> 此內容的主要功能是將分頁模板輸出。 經過以上內容的添加修改整合后,現在可以通過 http://localhost/news/list 看到新的新聞列表頁,與之前唯一的不同之處就是有分頁導航條了,各個可能點擊分頁的頁面數字對各個頁面進行切換顯示。結果如下: header Title Content Add news First news This is the first news Edit Delete Second news This is the second news Edit Delete Third news This is the third news Edit Delete fourth news This is the fourth news Edit Delete Fifth news This is the fifth news Edit Delete << 1 2 >> footer 6.3 自定模型 在任何一個網站系統中數據庫的操作都是一個重心核心問題,在很多時候做為一個開發都有自已已經熟練使用的一套數據庫操作類庫,使用自已熟悉的類庫不僅有助于提高開發效率,也有助于發現問題。在此作者根據自已的使用習慣套用ZF2中的相關數據庫操作類庫重寫了一個實用模型。此節的內容重不在于類庫本身,而是通過這個類庫來擴展自已的思維,以便日后可以自已的需要重寫自已使用的類庫。 新建模型文件:/module/Application/src/Application/Model/NewsModel.php,文件的內容如下: namespace Application\Model; use Zend\Db\Adapter\Adapter; use Zend\Db\Sql\Sql; use Zend\Db\ResultSet\ResultSet; class NewsModel { protected $adapter; /** * 構造函數 * @param Array $config 數據庫連接配置 */ public function __construct($config=null) { if($config==null) $this->adapter = new Adapter(array( 'driver'=>'Pdo_Mysql', 'database'=>'test', 'hostname'=>'localhost', 'username'=>'root', 'password'=>'' )); else $this->adapter = new Adapter($config); } /** * 返回查詢結果的第一行數據 * @param String $table 操作的數據表名 * @param String $where 查詢條件 * @return Array */ public function fetchRow($table,$where=null){ $sql = "SELECT * FROM {$table}"; if($where!=null) $sql .= "WHERE {$where}"; $statement = $this->adapter->createStatement($sql); $result = $statement->execute(); return $result->current(); } /** * 返回查詢的所有結果 * @param String $table 數據表名 * @param String $where 查詢條件 * @return Array */ public function fetchAll($table,$where=null){ $sql = "SELECT * FROM {$table}"; if($where!=null) $sql .= "WHERE {$where}"; $stmt = $this->adapter->createStatement($sql); $stmt->prepare(); $result = $stmt->execute(); $resultset = new ResultSet; $resultset->initialize($result); $rows = array(); $rows = $resultset->toArray(); return $rows; } /** * 返回指定表的所有數據 * @param String $table 表名 * @return Array */ public function getTableRecords($table) { $sql = new Sql($this->adapter); $select = $sql->select(); $select->from($table); $stmt = $sql->prepareStatementForSqlObject($select); $result = $stmt->execute(); $resultSet = new ResultSet(); $resultSet->initialize($result); return $resultSet->toArray(); } /** * 插入數據到數據表 * @param String $table * @param Array $data * @return Int 返回受影響的行數 */ public function insert($table,$data){ $sql = new Sql($this->adapter); $insert=$sql->insert($table); $insert->values($data); return $sql->prepareStatementForSqlObject($insert)->execute()->getAffectedRows(); } /** * 更新數據表 * @param String $table 數據表名 * @param String $data 需要更新的數據 * @param String|Array $where 更新條件 * @return Int 返回受影響的行數 */ public function update($table,$data,$where){ $sql = new Sql($this->adapter); $update=$sql->update($table); $update->set($data); $update->where($where); return $sql->prepareStatementForSqlObject($update)->execute()->getAffectedRows(); } /** * 刪除數據 * @param String $table 數據表名 * @param String|Array $where 刪除條件 * @return Int 返回受影響的行數 */ public function delete($table,$where){ $sql = new Sql($this->adapter); $delete = $sql->delete($table)->where($where); return $sql->prepareStatementForSqlObject($delete)->execute()->getAffectedRows(); } /** * 返回最后插入的主鍵值 * @return Int */ public function lastInsertId(){ return $this->adapter->getDriver()->getLastGeneratedValue(); } } 以上代碼為一個完整的模型代碼,這個模型中使用了多個ZF2中的DB類庫來實現不能的功能需求,上面只是一個范例且已經對各個函數方法給出了注釋,在此就不對該模型做一一詳解。 6.4 章節總結 第6章節是綜合性的一個章節,章節內容包含從模塊配置到建立模型、模板、使用模型、模板等內容。知識要點多,掌握不易,要想能夠輕松快捷的使用ZF2框架給開發提供的數據庫驅動,就需要不斷的練習數據操作類庫的使用。本章節內容的的重點及難道就是如何使用ZF2框架提供的數據庫驅動對完成對數據庫的完全操作,掌握本章節內容至少可以說已經基本完成了對ZF2的入門。為了進一步鞏固前面第1章至第6章的內容,在接下來的章節里將再通過兩個實例來加強ZF2的開發的重點要點內容。兩個實例:一個是ZF2官網的Album實例整合應用;另一個是用戶登錄驗證,使用持久性驗證。 第 7 章 實例應用 在接下來的內容中將以Album 為實例模塊名進行講解, 讀者可能會發現這個名字很熟悉,不錯ZF2官網也有一個Album實例類似內容,官網上的對該實例的講解比較分散,對于ZF2初入門者來說不易掌握;之所以在本章節也以Album 來命名是因為實例的內容符合本書的要求,同時作者也為了使閱讀者能夠更加清晰及準確掌握相關內容知識要點;此章節的內容閱讀者可以與ZF2官網的實例進行對比,找出兩者之間的不同點及相同點,但本章節的內容與官網所要表達的結果是一致,都是為讓開發者掌握ZF2對數據庫的基本操作。本章的所有內容都在前面6章節的內容上完全,這樣更能體現出一個網站應用的完整性。 7.1 建立Album 模塊 實例內容通過一個模塊來進行講解,可以更好的了解模塊之間的對比性,以及模塊與模塊之前的耦合性。 7.1.1建立模塊目錄 目錄結構如下: /module/Album 模塊目錄 /module/Album/config 模塊配置文件目錄 /module/Album/src 模塊資源文件目錄 /module/Album/src/Album/Controller 控制器文件目錄 /module/Album/src/Album/Form 表單文件目錄 /module/Album/src/Album/Model 模型文件目錄 /module/Album/view 模塊模板文件目錄 /module/Album/view/album/album 模板文件目錄 /module/Album/view/partial 其他通用模板文件目錄 7.1.2 配置模塊全局設置 要讓一個新添加的模塊加入到ZF2搭建的網站系統中就必需為對新的模塊進行設置。 打開全部配置文件:/config/application.config.php 內容如下: return array( 'modules' => array( 'Application', 'Album' // 此行為新加內容 ), 'module_listener_options' => array( 'config_glob_paths' => array( APP_PATH.'config/autoload/{,*.}{global,local}.php', ), 'module_paths' => array( APP_PATH.'module', APP_PATH.'vendor',// 就要應用于phpunit ), ), ); 此文件只添加了一個行:在modules 區塊中的 ‘Album’;在前面章節的內容已經有說明,每增加一個模塊都需要在全局配置文件中添加進行,也就是對模塊進行注冊使用。 7.2 添加模塊文件 添加文件:/module/Album/Module.php,內容如下: namespace Album; use Album\Model\Album; use Album\Model\AlbumTable; use Zend\Db\ResultSet\ResultSet; use Zend\Db\TableGateway\TableGateway; class Module{ public function getAutoloaderConfig(){ return array( 'Zend\Loader\StandardAutoloader'=>array( 'namespaces'=>array( __NAMESPACE__ => __DIR__ . '/src/' . __NAMESPACE__, ), ), ); } public function getConfig(){ return include __DIR__ . '/config/module.config.php'; } public function getServiceConfig() { return array( 'factories'=>array( 'Album\Model\AlbumTable'=>function($sm){ $tg = $sm->get('AlbumTableGateway'); $table = new AlbumTable($tg); return $table; }, 'AlbumTableGateway'=>function($sm){ $adapter = $sm->get('Zend\Db\Adapter\Adapter'); $rs = new ResultSet(); $rs->setArrayObjectPrototype(new Album()); return new TableGateway('album',$adapter,null,$rs); } ), ); } } 代碼簡單解釋: lpublic function getAutoloaderConfig(){} 配置文件加載路徑 lpublic function getConfig(){} 獲取模塊配置文件 lpublic function getServiceConfig(){} 獲取模塊服務配置信息 7.3 添加模塊配置文件 模塊配置文件主要對路由、視圖等進行配置,此處配置關系到整個模塊的訪問方式及其他使用方式。 添加文件:/module/Album/config/module.config.php ,添加內容如下: return array( 'router' => array( 'routes' => array( 'album' => array( 'type' => 'segment', 'options' => array( 'route' => '/album[/][:action][/:id]', 'constraints' => array( 'action' => '[a-zA-Z0-9_-]*', 'id'=>'[0-9]*' ), 'defaults' => array( 'controller' => 'Album\Controller\Album', 'action' => 'index' ), ), ), ), ), 'controllers' => array( 'invokables' => array( 'Album\Controller\Album' => 'Album\Controller\AlbumController' ), ), 'view_manager' => array( 'template_path_stack' => array( 'album' => __DIR__ . '/../view', ), ), ); 代碼解釋: l'router' => array() 路徑配置區塊,可以包括有多條路由 l'controllers' => array() 控制器配置區塊,此處可以配置控制的使用情況 l'view_manager' => array() 視圖配置區塊,此處配置視圖存放路徑;Album 模塊沒有再單獨使用layout配置,與之前 的Application共用同一們layout布局 7.4 創建數據表 album 在此我們仍然后前面章節提到的 test 數據庫,在test數據庫里添加一個album表并插入數據,具體如下: CREATE TABLE album(id int(10) NOT NULL AUTO_INCREMENT,title varchar(100) NOT NULL,artist varchar(1000) NOT NULL,PRIMARY KEY(id)); INSERT INTO news(title,artist) VALUES(‘First album’,’artist01’); INSERT INTO news(title,artist) VALUES(‘Second album’,’artist02’); INSERT INTO news(title,artist) VALUES(‘Third album’,’artist03’); INSERT INTO news(title,artist) VALUES(‘fourth album’,’artist04’); INSERT INTO news(title,artist) VALUES(‘Fifth album’,’artist05’); INSERT INTO news(title,artist) VALUES(‘Sixth album’,’artist06’); 7.5 添加模型文件 模型是ZF2對數據庫操作的核心內容,也是進行數據過濾、數據交換的功能專區。 7.5.1 添加 Album.php 此文件包括數據交換、表單數據過濾功能;添加 /module/Album/src/Album/Model/Album.php 內容如下: namespace Album\Model; use Zend\InputFilter\Factory as InputFactory; use Zend\InputFilter\InputFilter; use Zend\InputFilter\InputFilterAwareInterface; use Zend\InputFilter\InputFilterInterface; class Album implements InputFilterAwareInterface { public $id; public $artist; public $title; protected $inputFilter; public function exchangeArray($data){ $this->id = (isset($data['id'])) ? $data['id'] : null; $this->artist = (isset($data['artist'])) ? $data['artist'] : null; $this->title = (isset($data['title'])) ? $data['title'] : null; } public function getArrayCopy(){ return get_object_vars($this); } public function getInputFilter() { if(!$this->inputFilter){ $this->inputFilter = new InputFilter(); $factory = new InputFactory(); $this->inputFilter->add($factory->createInput(array( 'name'=>'id', 'required'=>true, 'filters'=>array( array('name'=>'Int'), ), ))); $this->inputFilter->add($factory->createInput(array( 'name'=>'artist', 'required'=>true, 'filters'=>array( array('name'=>'StripTags'), array('name'=>'StringTrim'), ), 'validators'=>array( array( 'name'=>'StringLength', 'options'=>array( 'encoding'=>'UTF-8', 'min'=>5, 'max'=>100, ), ), ), ))); $this->inputFilter->add($factory->createInput(array( 'name'=>'title', 'required'=>true, 'filters'=>array( array('name'=>'StripTags'), array('name'=>'StringTrim'), ), 'validators'=>array( array( 'name'=>'StringLength', 'options'=>array( 'encoding'=>'UTF-8', 'min'=>5, 'max'=>100, ), ), ), ))); } return $this->inputFilter; } public function setInputFilter(InputFilterInterface $inputFilter) { throw new \Exception('Not used'); } } 代碼解釋: lpublic function exchangeArray($data){} 數據轉換 lpublic function getArrayCopy(){} 克隆對象內屬性 lpublic function getInputFilter() {} 過濾器 7.5.2 添加AlbumTable.php 此文件為數據庫操作網關,實現對數據庫的一系列操作;添加文件:/module/Album/src/Album/Model/AlbumTable.php,具體內容如下: namespace Album\Model; use Zend\Db\TableGateway\TableGateway; use Zend\Db\ResultSet\ResultSet; use Zend\Db\Sql\Select; use Zend\Paginator\Adapter\DbSelect; use Zend\Paginator\Paginator; class AlbumTable { protected $tableGateway; public function __construct(TableGateway $tg) { $this->tableGateway = $tg; } public function fetchAll($paginated=false) { if($paginated){// 分頁 $select = new Select('album'); $rs = new ResultSet(); $rs->setArrayObjectPrototype(new Album()); $pageAdapter = new DbSelect($select,$this->tableGateway->getAdapter(),$rs); $paginator = new Paginator($pageAdapter); return $paginator; } $resultSet = $this->tableGateway->select(); return $resultSet; } public function getAlbum($id) { $id = (int) $id; $rowset = $this->tableGateway->select(array('id'=>$id)); $row = $rowset->current(); if(!$row){ throw new \Exception("Could not find row {$id}"); } return $row; } public function saveAlbum(Album $album) { $data = array( 'artist' =>$album->artist, 'title' =>$album->title ); $id = (int) $album->id; if($id == 0){ $this->tableGateway->insert($data); }else{ if($this->getAlbum($id)){ $this->tableGateway->update($data,array('id'=>$id)); }else{ throw new \Exception("Could not find row {$id}"); } } } public function deleteAlbum($id) { $this->tableGateway->delete(array('id'=>$id)); } } 代碼解釋: lpublic function fetchAll($paginated=false){} 獲取數據表中的所有記錄 lpublic function getAlbum($id){} 獲取指定ID的記錄行 lpublic function saveAlbum(Album $album){} 保存數據到數據庫 lpublic function deleteAlbum($id){} 刪除指定ID的記錄行 7.6 添加表單 AlbumForm 添加文件:/module/Album/src/Album/Form/AlbumForm.php,具體內容如下: namespace Album\Form; use Zend\Form\Form; class AlbumForm extends Form{ public function __construct($name=null) { parent::__construct('album'); $this->setAttribute('method', 'post'); $this->add(array( 'name'=>'id', 'type'=>'Hidden' )); $this->add(array( 'name'=>'title', 'type'=>'Text', 'options'=>array( 'label'=>'Title' ), )); $this->add(array( 'name'=>'artist', 'type'=>'Text', 'options'=>array( 'label'=>'Artist' ), )); $this->add(array( 'name'=>'submit', 'type'=>'submit', 'attributes'=>array( 'value'=>'Go', 'id'=>'submit' ), )); } } 7.7 添加控制器 AlbumController 添加文件:/module/Album/src/Controller/AlbumController.php 具體內容如下: namespace Album\Controller; use Zend\Mvc\Controller\AbstractActionController; use Zend\View\Model\ViewModel; use Album\Model\Album; use Album\Form\AlbumForm; class AlbumController extends AbstractActionController{ protected $albumTalbe; public function indexAction(){ $paginator = $this->getAlbumTalbe()->fetchAll(true); $paginator->setCurrentPageNumber((int)$this->params()->fromQuery('page',1)); $paginator->setItemCountPerPage(5); return new ViewModel(array('paginator'=>$paginator)); } public function addAction(){ $form = new AlbumForm(); $form->get('submit')->setValue('Add'); $request = $this->getRequest(); if($request->isPost()){ $album = new Album(); $form->setInputFilter($album->getInputFilter()); $form->setData($request->getPost()); if($form->isValid()){ $album->exchangeArray($form->getData()); $this->getAlbumTalbe()->saveAlbum($album); return $this->redirect()->toRoute('album'); } } return array('form'=>$form); } public function editAction(){ $id = (Int) $this->params()->fromRoute('id',0); if(!$id){ return $this->redirect()->toRoute('album',array('action'=>'add')); } try{ $album = $this->getAlbumTalbe()->getAlbum($id); }catch(\Exception $e){ return $this->redirect()->toRoute('album',array('action'=>'index')); } $form = new AlbumForm(); $form->bind($album); $form->get('submit')->setAttribute('value', 'Edit'); $request = $this->getRequest(); if($request->isPost()){ $form->setInputFilter($album->getInputFilter()); $form->setData($request->getPost()); if($form->isValid()){ $this->getAlbumTalbe()->saveAlbum($form->getData()); return $this->redirect()->toRoute('album'); } } return array('id'=>$id,'form'=>$form); } public function deleteAction(){ $id = (Int) $this->params()->fromRoute('id',0); if(!$id){ return $this->redirect()->toRoute('album'); } $request = $this->getRequest(); if($request->isPost()){ $del = $request->getPost('del','No'); if($del=='Yes'){ $id = (Int)$request->getPost('id'); $this->getAlbumTalbe()->deleteAlbum($id); } return $this->redirect()->toRoute('album'); } return array('id'=>$id,'album'=>$this->getAlbumTalbe()->getAlbum($id)); } public function getAlbumTalbe(){ if(!$this->albumTalbe){ $sm = $this->getServiceLocator(); $this->albumTalbe = $sm->get('Album\Model\AlbumTable'); } return $this->albumTalbe; } } 代碼解釋: lpublic function indexAction(){} album默認訪問action,也是album列表action lpublic function addAction(){} 添加album 的 action lpublic function editAction(){} 編輯修改album的action lpublic function deleteAction(){} 刪除album 的action lpublic function getAlbumTalbe(){} 設置數據庫網關 7.8 添加模板文件 模板是數據處理后的最終展現平臺,也是用戶操作與感知的入口。Album模塊使用的模板有4個:index.phtml , add.phtml , edit.phtml , delete.phtml , paginator.phtml 分別 列表、添加、修改、刪除、分頁導航 7.8.1 列表模板 index.phtml 添加文件:/module/Album/view/album/album/index.phtml,內容如下: <table> <tr> <th><?php echo $this->translate("Title") ?></th> <th><?php echo $this->translate("Artist") ?></th> <th><a href="<?php echo $this->url('album', array('action' => 'add')); ?>"><?php echo $this->translate("Add new album") ?></a></th> </tr> <?php foreach ($paginator as $album) : ?> <tr> <td><?php echo $this->escapeHtml($album->title); ?></td> <td><?php echo $this->escapeHtml($album->artist); ?></td> <td> <a href="<?php echo $this->url('album', array('action' => 'edit', 'id' => $album->id)); ?>"><?php echo $this->translate("Edit") ?></a> <a href="<?php echo $this->url('album', array('action' => 'delete', 'id' => $album->id)); ?>"><?php echo $this->translate("Delete") ?></a> </td> </tr> <?php endforeach; ?> </table> <?php echo $this->paginationControl($this->paginator,'sliding',array('partial/paginator.phtml','Album'),array('route'=>'album')); ?> 7.8.2 列表模板 add.phtml 添加文件:/module/Album/view/album/album/add.phtml,內容如下: <?php $form = $this->form; $form->setAttribute('action',$this->url('album',array('action'=>'add'))); echo $this->form()->openTag($form); echo $this->formCollection($this->form); echo $this->form()->closeTag(); ?> 7.8.3 列表模板 edit.phtml 添加文件:/module/Album/view/album/album/edit.phtml,內容如下: <?php $form = $this->form; $form->setAttribute('action',$this->url('album',array('action'=>'edit','id'=>$this->id))); echo $this->form()->openTag($form); echo $this->formCollection($this->form); echo $this->form()->closeTag(); ?> 7.8.4 列表模板 delete.phtml 添加文件:/module/Album/view/album/album/delete.phtml,內容如下: <?php $title = 'Delete album'; $this->headTitle($title); ?> <h1><?php echo $this->escapeHtml($title); ?></h1> <p>Are you sure that you want to delete '<?php echo $this->escapeHtml($album->title); ?>' by '<?php echo $this->escapeHtml($album->artist); ?>'? </p> <?php $url = $this->url('album', array( 'action' => 'delete', 'id' => $this->id, )); ?> <form action="<?php echo $url; ?>" method="post"> <div> <input type="hidden" name="id" value="<?php echo (int) $album->id; ?>" /> <input type="submit" name="del" value="Yes" /> <input type="submit" name="del" value="No" /> </div> </form> 7.8.5 列表模板 paginator.phtml 添加文件:/module/Album/view/partial/paginator.phtml,內容如下: <?php if ($this->pageCount): ?> <div class="pagination pagination-centered"> <ul> <!-- Previous page link --> <?php if (isset($this->previous)): ?> <li> <a href="<?php echo $this->url($this->route); ?>?page=<?php echo $this->previous;?>"><<</a> </li> <?php else: ?> <li class="disabled"> <a href="#"> << </a> </li> <?php endif; ?> <!-- Numbered page links --> <?php foreach ($this->pagesInRange as $page): ?> <?php if ($page != $this->current): ?> <li> <a href="<?php echo $this->url($this->route); ?>?page=<?php echo $page; ?>"> <?php echo $page; ?> </a> </li> <?php else: ?> <li class="active"> <a href="#"><?php echo $page; ?></a> </li> <?php endif; ?> <?php endforeach; ?> <!-- Next page link --> <?php if (isset($this->next)): ?> <li> <a href="<?php echo $this->url($this->route); ?>?page=<?php echo $this->next; ?>"> >> </a> </li> <?php else: ?> <li class="disabled"> <a href="#"> >> </a> </li> <?php endif; ?> </ul> </div> <?php endif; ?> 經過以上的準備工作,接下可以通過 http://localhost/album 來打開album 列表的,打開的頁面結果如下所示: header Title Artist Add new album First album artist01 EditDelete Second album artist02 EditDelete Third album artist03 EditDelete fourth album artist04 EditDelete Fifth album artist05 EditDelete << 1 2 >> footer 點擊 列表上面的Add new album 可以進行 album 添加,頁面結果如下所示: header 窗體頂端 Title窗體底端 Artist footer 點擊列表右邊的 Edit 可以對album 的信息進行修改,頁面結果如下所示: header 窗體頂端 Title窗體底端 Artist footer 點擊列表右邊的 Delete 可以對album 記錄進行刪除,確認刪除頁面如下所示: header Delete album Are you sure that you want to delete 'First album' by 'artist01'? 窗體頂端 窗體底端 footer 到此已經完成了對整個Album模塊的的構造及功能的實現,此實例雖然沒有實際的應用意義,但他已經完整的展示了在ZF2一個完整模塊的存在形式,以及與其他模塊同時并存且同時協同工作的具體應用,在進行具體項目開發的時候可以借鑒或參考此例以便開發出不同功能的模塊,使用項目模塊能夠共同協同工作。 第 8 章 用戶認證 用戶認證在項目的開發過程是一個不可或缺的重要組成部分,他的作用擔負著對整個的項目的合法認證及準入機制。 8.1 建立數據表 在開始前先建立一個數據表用來存放用戶的認證字段,通俗的說就是用戶名與密碼。還使用之前的test數據庫,建立一個user表,具體內容如下: CREATE TABLE user(id int(10) NOT NULL AUTO_INCREMENT,username varchar(100) NOT NULL,password varchar(1000) NOT NULL,PRIMARY KEY(id)); INSERT INTO news(username,password) VALUES(‘admin’,’admin’); INSERT INTO news(username,password) VALUES(‘test’,’test’); 8.2 新建認證類 為了方便講解與引用,將認證類放在Album模塊的模型目錄下。用戶認證的方式有多種,在這里介紹的一種是作者在開發中常用到的一種,數據庫認證的持久性認證。ZF2中的持久性認證其本質還是通過Session來實現的,只不過開發者在開發的時候根據就不會察覺到Session在這期間的存在。ZF2在使用持久性認證的時候如果沒有對認證空間進行重新命名,ZF2使用使用一個Zend_auth為其Session的默認命名空間,當你在new一個認證的實例的時候系統會自動的找到Zend_auth對應的Session命名空間。在此也可以看認證類與其他類在進行new 的時候可能會有所不同,這種不同也正是由于Session的相關機制所帶來的。 添加文件:/module/Album/src/Album/Model.MyAuth.php,具體內容如下: namespace Album\Model; use Zend\Db\Adapter\Adapter as DbAdapter; use Zend\Authentication\Adapter\DbTable as AuthAdapter; use Zend\Authentication\AuthenticationService; class MyAuth { protected $adapter; public function __construct() { $this->adapter = new DbAdapter(array( 'driver'=>'Pdo_Mysql', 'database'=>'test', 'host'=>'localhost', 'username'=>'root', 'password'=>'' )); } public function auth() { $authAdapter = new AuthAdapter($this->adapter); $authAdapter ->setTableName('user') // 認證的數據表 ->setIdentityColumn('username') // 認證字段 ->setCredentialColumn('password'); // 校驗字段 $authAdapter ->setIdentity('admin') // 認證值 ->setCredential('admin');// 校驗值 $auth = new AuthenticationService(); $result = $auth->authenticate($authAdapter); if($result->isValid()){ $auth->getStorage()->write($authAdapter->getResultRowObject()); return true; } return false; } public function isAuth(){ $auth = new AuthenticationService(); if($auth->hasIdentity()) return true; return false; } } 代碼解釋: lpublic function auth() {} 進行認證 lpublic function isAuth(){} 通過持久性認證判斷是否已經通過認證 l$authAdapter = new AuthAdapter($this->adapter); 實例為一個認證適配器 l$auth = new AuthenticationService(); 實例化一個認證服務,以實現持久性認證 8.3 引用認證類 認證類已經建立好,將在AlbumController 中進行引用,以驗證認證類是否可用,打開文件:/module/Album/src/Album/Controller/AlbumController.php,添加如下兩個方法: public function authAction(){ $auth = new \Album\Model\MyAuth(); if($auth->auth()) echo "Authentication Success"; else echo "Authentication Failure"; exit; } public function isauthAction(){ $auth = new \Album\Model\MyAuth(); if($auth->isAuth()) echo "Already Authentication Success"; else echo "Authentication Failure"; exit; } 代碼解釋: lpublic function authAction(){} 驗證是否可能對指定的用戶名與密碼進行認證 lpublic function isauthAction(){} 驗證持久性驗證是否有效 添加代碼后,在瀏覽器中先打開:http://localhost/album/auth 查看是否通過了認證,接著在瀏覽器打開:http://localhost/album/isauth 查看是否在其他頁面也通過了認證。 以上認證的用戶名與密碼作者固定的設置為了admin,這個可以根據需求進行修改。也可以根據自已的需求對MyAuth的認證類進行改進和擴展以適應具體項目的要求。 第 9 章 結束語 經過前面8個章節的講解,本書也就可以說已經完成了他的使命;本書從零開始對ZF2框架的使用進行了一翻的講解,內容主要包括:下載ZF2框架、搭建適合ZF2運行的服務器環境、ZF2項目的創建方法、模塊的配置、模型的建立、模型的配置、控制器的建立、路由的配置、視圖模板配置等一系列內容;本書的內容雖然比較簡單的,講法也比較通俗,但他的內容已經基本覆蓋了一般普通項目所需的全部要素。作者本身做為一個開發者,全書的內容也是以一個開發的身份去寫、去講解,盡量使全書的內容表達更直接,更直白,直易于理解,以便達到書名的要求--ZF2 入門教程。這個教程是作者的第一個比較大篇幅的教程,對于寫教程的原由在開始寫這個文章前已經有所介紹。由于國內Zend Framework2 的開發資料相對匱乏,對于剛剛接觸ZF2的開發來是確實是件很頭痛的事,有些時候可能在電腦盲目的弄了幾天,但最后還是連個 hello world 都沒搞出來。作者希望本書的閱讀者可能從這些簡單的例子入手,逐漸的去接近ZF2的核心內容。ZF系列的框架其他各種強大的功能作者也不再重述,相信能夠接觸到ZF系列框架的開發者已經很明確了。 對于本書中的例子作者也做了一一的測試,全部功能都可以正常運行并到預期效果。書中例子如果在某些特定的情況下使用的話,開發者也可以根據自已的需求情況對文中的例子做進行一步擴展與完善。例如模型的功能,開發可以根據不同的環境要求對模型進行重構或直接根據需要重寫,還有多國語言支持,如果開發需要做一個大型項目也可以根據項目要求添加各個國家語言。作者在此再交希望閱讀者能夠從中書的例子舉一反三進行大量反復的練習,熟練掌握ZF2框架的使用方法;然后再對ZF2框架的相關底層實現進行有針對性的研究,進而從更大的深度及廣度上去使用ZF2框架。在此作者也向閱讀者們推薦使用ZF官網上的開發使用手冊,如果必需尋找相關的開發例程請使用Google 進行搜索,主要搜索英文網站。通過作者的相關經驗ZF還有很多的英文實例的,但中文的例程可能不太多了,即使有中文的例程也免不了你抄我,我抄的習慣,所以在國內搜索出來的文章可以說是大多雷同,基本上就是直接復制粘貼行為,沒人進一步的驗證例程的可用性;以此同時即浪費了時間,也誤導了不少開發者。 ZF官網開發幫助:http://framework.zend.com/learn/ 這里基本上可以找到你所需要的所有的ZF分類的內容,或一些簡短的例子。 本書到此結束,作者真心希望此書已經引導您進入了ZF2的世界。祝愿所有開發者一路順利。謝謝。 ~~~
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看