因爲篇幅過長使用過QQ空間的自動清理功能,不知道是否被刪減有內容。javascript
zend framework2 入門教程源碼php
http://pan.baidu.com/s/1kTuRGn9css
目錄html
序言 5前端
1.2 下載安裝 6mysql
1.3 搭建開發環境 6jquery
2.6 添加自動加載文件 init_autoloader.php 9
3.2.4 service_manager 服務管理器 16
5.1.5 創建 NewsController 模板目錄 26
5.1.6 創建 NewsController 對應的Action 模板文件 27
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
在教程的製做前先作一些做者的自我介紹,做者賴少林,男,畢業於 廣州市南洋理工職業學院 計算機應用科學網絡專業 和 海南師範大學 計算機應用科學 應用專業。於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/
ZF2是一個基於PHP的開源框架,能夠用來開發WEB應用程序的各類服務。ZF2是一個基於面向對象的框架,一切都是以對象爲基礎。ZF2有很是豐富的組件庫,並且大部組件之間相互獨立,互不依賴,因此開發者能夠獨自開發並使用自定義組件。
ZF2 擁有一個強大並且高效的MVC實現,他具備強大的數據庫操做、路由控制、視圖渲染、HTML表單解析、表單驗證、數據過濾等功能;同時ZF2還提供了多種用戶認證功能,經過證書來保存用戶認證和受權信息,也能夠經過Amf 來爲Flash等其餘語言開發的軟件提供相應的服務。總的來講不論你須要什麼功能,你均可以從ZF2中找到相應的組件來實現,從而有效的減小開發時間,提升項目開發的效率。ZF2提供的各類組件能夠用來實現你想要的各類功能,也能夠添加一些你自定義組件來搭建你強大的WEB應用程序。
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的框架,下面會作詳細的介紹。
在開始使用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 文件就行。
方法一:手動添加目錄,結構以下
/
└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框架。
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 打開僞靜態的支持。
添加/application/.htaccess
若是使用記事原本寫這個文件,那麼保存的時候要使用另存爲的方式進行保存,若是在netbeans中建立的話就能夠直接保存。同時還應當注意在書寫這種文件的時候最後至少要有一行空白行,有時候有些文件就有這樣要求。此文件的主要功能就是實現URL的重寫,根據URL的訪問地址經過前端控制器找到相應的路由,從而實現對資源文件準肯定位。重寫URL的好處還在於可以讓搜索引擎更容易抓取。
在文件中輸入如下內容:
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^.*$ index.php [NC,L]
路徑:/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() 啓動應用程序
路徑:/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() 配置模塊路徑
路徑:/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.');
}
路徑:/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更增強大、靈活的緣由所在。
ZF2 使用模塊系統將應用程序的主要代碼集成到各個模塊中去。同時應用模塊還應提供用於引導、錯誤異常、路由等所有的配置信息。在模塊文件裏能夠根據自已的須要去調整關於 實圖、路由、模型等一系列應用程序級的設置,同時Module不單起到配置信息的做用,同時也是應用程序的必需中間件或橋樑,由於程序從前端控制器的分配及引導下進入的下層級就是Module類,經過解析Module類到達指定資源位置。由此也能夠看出ZF2的靈活性仍是比較高的。
下面開始添加模塊文件。
路徑:/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的文件搜索或路由也是經過空間來定位的。再者就是有了空間能夠更好的去管理各類類,方便文件功能歸類及使用。
路徑:/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進行逐個解釋。
路由的配置是對前臺頁面訪問地址的具體配置,此處配置的格式將影響到前臺頁面訪問此模塊的全部地址
鏈: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(動做)名稱
控制器的配置將決定哪些控制器類可以被訪問及使用,在此配置後ZF2自動加載廠能夠很快的定位到控制所在的位置,對資源進行快速訪問、使用。
鏈:controllers--->invokables--->控制器
lcontrollers 表示此數組塊爲控制器配置信息段
lcontrollers-->invokables 這個是控制器區塊的固定表示方法,表示此區塊下的控制器爲可用控制器
lcontrollers-->invokables-->Application\Controller\Index 表示一個控制器,數組的鍵表示DI注入的引用,數組值則表示對應控制器所在的具體路徑
控制器的配置不侷限於某一個控制器,能夠把全部已經存在而且有效控制器加入到此區塊來進行使用。
視圖管理器主要負責視圖信息的配置,如:錯誤顯示、頁面類型、佈局文件、視圖文件位置、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 視圖目錄所在路徑
服務管理器主要負責一些工廠類的配置,使用系統可以在運行時自動的加載運行某些服務性功能。
鏈:service_manager-->factories
lservice_manager 表示此數組塊爲服務管理器配置信息段
lservice_manager-->factories 工廠類配置
lservice_manager-->factories-->translator 語言轉換工廠,主要功能是實現多國語言的支持,語言文件須要自已編寫;ZF2框架自己並不提供語言包,但提供對語言包的解析功能,經過語言包經過指定的語言進行轉換;同時語言之間的轉換及格式的變化比較而隨意性也比較大,因此語言包能夠根據項目的實現需求來進行訂製,若是實際的項目開發中並不使用到國際化的功能時,能夠將多國語言國際功能刪除。
lservice_manager-->factories-->translator-->navigation 導航工廠,主要用來實現頁面的導航和分頁導航
翻譯器的主要工做是負責對各類支持語言的轉換以此爲目的,從而實現網站應用程序的多國化甚至全球化。本書內容有涉及到使用語言包,語言包的生成能夠參考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 語言文件的匹配規則
下面提供兩個語言包,語言包爲中英語言包,能夠在開發的時候對兩種語言環境進行切換。
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 & Support"
msgstr "幫助 & 支持"
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 "文章內容"
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 & 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 ""
導航條的主要功能是生成頁面導航或分頁導航
鏈: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 的效果
控制器是ZF2的核心功能,其實現了前端控制器所需的所有接口。如:路由分發、視圖渲染、助手、請求、響應等一系列的功能。同時也能夠利用繼承來設計自已的助手類或一些實用性較的插件等,來增強自已的系統功能。
在ZF2中,控制器是一個類一般稱爲{控制器名稱}控制器。 請注意,{控制器名稱}必須以大寫字母開頭。這個類保存在控制器模塊目錄內以名爲{控制器名稱}控制器類.php的文件中。 控制器的每一個操做都是在一個公共方法內的{動做名稱}中完成。通常狀況下{動做名稱}是以小寫字母開頭。
根據前面章節的相關設置,當前項目中的全部控制器都將放在 /module/Application/src/Application/Controller 的目錄下;在本章節及接下來的幾個章節都以一個新聞系統來對相關的知識內容來進行講解。
如今添加一個控制器,在控制器目錄下新一個控制器 NewsController 控制器,路徑:/module/Application/src/Application/Controller/NewsController.php
代碼以下:
class NewsController extends AbstractActionController {}
經過以上代碼便建立了一個標準的控制器類,雖然些控制器只有短短的一行代碼,但他擁有操做器所需的所有基本功能;由於他已經繼承了 AbstractActionController 類中的所有方法。
下面在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的徹底性;致以本章節前面提到的要實現控制器中的增、刪、改 的功能將會在視圖及模型章節中加以補充。
視圖是任何網站應用程序不可或缺的一個組成部分,它提供了與用戶交互的良好界面,也提供了數據輸入與輸出的接口。ZF2 視圖類主要有 Zend\View 包,其主要功能簡單的說包括:變量傳遞,數據轉換、視圖渲染、請求映射、渲染策略、響應策略等。此外ZF2還經過 Zend\MVC\View 提供了事件偵聽的一系列包,在進行項目開發的時候能夠根據需求加入偵聽事件。下面開始建立視圖並開始使用他們。
爲要呼應本書各章節的內容,在此將創建三種模板:佈局模板、錯誤異常模板、控制器模板
路徑:/module/Application/view/layout
此目錄主要用來放置網站應用程序的佈局文件,在建站的時候能夠根據頁面的不一樣須要來選擇不一樣的佈局文件。佈局的功能能夠實現不一樣模塊不一樣佈局,不一樣模塊相同佈局的實際須要。
路徑:/module/Application/view/layout/layout.phtml
在此須要注意一下,ZF2默認的視圖文件均以.phtml 爲後綴名,若是有其餘特殊要求能夠修改成其餘的後綴名
路徑:/module/Application/view/error
在此目錄下主要用來放置一些關於錯誤異常處理的模板文件
/module/Application/view/error/index.phtml 錯誤異常信息顯示模板文件
/module/Application/view/error/404.phtml 404錯誤異常信息顯示模板文件
路徑:/module/Application/view/application/news
此目錄主要用放置 NewsController 中Action 對應的模板文件。通常狀況一個控制會對應一個模板目錄,同時模版目錄的名稱與與控制的名稱一致(不包含Controller)。
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對就不一樣的模板。
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 文件中的相關描述
創建好佈局文件、模板文件以後須要對他們進行配置,以便後續使用中可以讓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 目錄裏的模板文件的內容將在後續的講解中逐一的進行添加填充。
爲了使全部的模板文件可以看起來足夠簡潔和便於理解,在模板文件的編寫上將盡可能的用少的代碼及多的經常使用代碼來進行講解,並且在模板文件中較多的代碼仍爲普通的HTML代碼;因此在下面涉及到的 css 文件和 js 文件都是以空文件的形式來引用,使用空文件的緣由主要是爲說明相關函數的功能特性,同時對模板文件進行解釋的主要內容也在ZF2的函數上。
下面是代碼內容:
<?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 底部內容 |
下面是代碼內容:
<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() 獲取上一個異常
以上是錯誤異常模板內容,模板可以輸出致使錯誤異常的文件名、出錯所在行、錯誤類型等信息。在開發項目的時候即可以經過錯誤的信息提示來查找相關出錯緣由。理解並正確使用使用錯誤信息可以有效的提升開發效率。
下面是代碼內容:
<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的相關提示信息找到出錯的問題點。
針對於 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。
通過以上衆多的準備工做,如今咱們已經能夠經過 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 |
|
|
|
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模板作得豐富多彩、漂亮。
模型不只是ZF2的重要組成部分,同時也是衆MVC框架的重要組成部分。他的重要性主要在於處理用戶與數據庫之間的訪問與操做功能。ZF2 自己並不直接直接提供 Zend\Model 組件,由於模型是一種業務邏輯,針對不一樣的項目可能會用不一樣的商業業務處理邏輯,模型的具體工做流程取決於你對相關模型組件個體設計。雖然ZF2自己並不提供模型,但ZF2提供了不少用於實現用戶模型的各類組件,用戶用經過ZF2提供的組件來構建自已的模型類,而且能夠經過映射器對其進行映射,以方便應用程序的前端控制對他進行引用及使用,最終實現對數據庫的一系列操做。
模型類的編寫沒有一個統一的寫法,不一樣的人有不一樣的想法及寫法,不一樣的業務邏輯有不一樣的實現方法,最終需求根據實際狀況進行編寫。在本章節中將介紹兩種做者在開發中經常使用到的寫法,一種是ORM對象映射技術,另外一種是自定義對象。本章節的內容會結合前面章節中包括內容 控制器、視圖模板等,同時也會涉及到ZF2 Zend\Db,Zend\Form 等相關知識內容進行綜合的講解。
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' => ''
),
);
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() 將類屬性轉化爲一個關聯數組,方便後續的使用
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() 獲取數據表的數據
在使用模型的時候須要對其餘進行模塊配置,以便ZF2可以地運行的時候自動加載。
找到文件 /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()函數裏沒有看到數據表的緣由。
找到文件 /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 函數對模型的操做結果進行打印輸出,而並無經過模板來呈現;要想經過模板來呈現模型對數據庫查詢的結果還須要進行一些小的修改。
模板是聚集網站應用全部操做的一個最終集合點,最終將全部聚集的數據集中展示給用戶。在使用模板前咱們還得修改下控制器,以便控制器能將模型操做的結果傳遞到模板中去。修改 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 |
|||||||||||||||||||||
|
|||||||||||||||||||||
footer |
插入數據的功能經過添加新聞的方式來進行講解,在使用插入數據的功能時同時涉及到過濾器、表單生成的相關內容,本小節將這三個內容進行結合講解。
添加表單文件,路徑:/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'))); 添加一個提交按鈕
以上代碼就包含了一個新聞記錄所需的所有表單元素。
文件:/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) 設置過濾,實現接口的方法
經過上面兩個小節的內容已經完成了建立表單的基本要素,下面將經過控制器中的方法來引用上面的內容來生成一個新聞表單。
打開文件:/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); 返回一個表單對象
收到從控制器中傳遞過來數據並將數據在模板中輸出,打開文件:/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 |
要把新聞表單的數據可以提交到數據庫中進行保存,還須要在模型中添加保存新聞的模型方法,打開模型文件 /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 來添加新聞並保存到數據庫了。
上面一節內容已經講解了怎麼經過表單將一個新插入到數據庫裏,接下來就是要實現若是使用表單來修改一條新聞記錄並將他保存到數據庫。在前一節講解內容的時候已經說過 saveNews 保存數據功能不只用於添加新聞,也用於新聞的修改,表單也是重用以前內容的表單,因此這些部分的內容就再也不重複進行講解。下面將重點放在控制器的 editAction方法和edit.phtml模板中。
在繼續製做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 返回查詢結果的新聞記錄行
打開文件:/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到模板,此處的表單對象與前面章節中插入數據的表單有所區別,此表單裏面的標籤都已經有數據的了,而以前插入新聞的表單只是一個空的表單。
打開文件:/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還在,則進行的是添加功能。
本節將講解關於數據庫CURD中的最後一個是重要環節--數據庫的刪除操做,本章節所講解的主要任務是實現對指定新聞id的刪除功能。
打開文件:/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 請求,則返回數據給模板
打開模型文件 /module/Application/src/Application/Model/NewsTables.php 文件,添加以下方法:
public function deleteNews($id)
{
$this->tableGateway->delete(array('id'=>$id));
}
代碼解釋:
$this->tableGateway->delete(array('id'=>$id)); 根據傳遞過來的id刪除新聞記錄
打開文件:/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將跳轉到刪除的確認頁面,而後確認是否刪除。
當天新聞記錄不斷增長的時候,必然致使新聞列表不的加長以至用戶不能在一屏內顯示完全部內容,導致用戶須要不停的拉動滾動條來獲取更多的內容,這樣無形之中給用戶瀏覽新聞添加了很多障礙;所以對數據進行分頁將是必然。以前作過網站的都應該知道分頁樣式及功能的實現有多種方法,分頁能夠根據不一樣的須要進行定製,但有一個缺點就是開發者基本上都須要自已寫一個分頁類庫來加以調用。ZF2爲了減小開發自已類庫的麻煩ZF2類庫自己就已經集成了分頁的類庫,ZF2提供的分頁類庫簡單易用,開發都也能夠根據須要重寫分頁類或對分類的CSS樣式進行從新設定;接下來的內容將重點講解ZF2分頁類庫的使用方法。
打開文件 /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]+' 路由正則區別規則
打開文件 /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; 返回分頁導航實例
打開文件 /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)); 將分頁導航對象返回給模板調用
添加文件 /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>下一頁連接
打開文件 /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 |
||||||||||||||||||
|
||||||||||||||||||
footer |
在任何一個網站系統中數據庫的操做都是一個重心核心問題,在不少時候作爲一個開發都有自已已經熟練使用的一套數據庫操做類庫,使用自已熟悉的類庫不只有助於提升開發效率,也有助於發現問題。在此做者根據自已的使用習慣套用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章節是綜合性的一個章節,章節內容包含從模塊配置到創建模型、模板、使用模型、模板等內容。知識要點多,掌握不易,要想可以輕鬆快捷的使用ZF2框架給開發提供的數據庫驅動,就須要不斷的練習數據操做類庫的使用。本章節內容的的重點及難道就是如何使用ZF2框架提供的數據庫驅動對完成對數據庫的徹底操做,掌握本章節內容至少能夠說已經基本完成了對ZF2的入門。爲了進一步鞏固前面第1章至第6章的內容,在接下來的章節裏將再經過兩個實例來增強ZF2的開發的重點要點內容。兩個實例:一個是ZF2官網的Album實例整合應用;另外一個是用戶登陸驗證,使用持久性驗證。
在接下來的內容中將以Album 爲實例模塊名進行講解, 讀者可能會發現這個名字很熟悉,不錯ZF2官網也有一個Album實例相似內容,官網上的對該實例的講解比較分散,對於ZF2初入門者來講不易掌握;之因此在本章節也以Album 來命名是由於實例的內容符合本書的要求,同時做者也爲了使閱讀者可以更加清晰及準確掌握相關內容知識要點;此章節的內容閱讀者能夠與ZF2官網的實例進行對比,找出二者之間的不一樣點及相同點,但本章節的內容與官網所要表達的結果是一致,都是爲讓開發者掌握ZF2對數據庫的基本操做。本章的全部內容都在前面6章節的內容上徹底,這樣更能體現出一個網站應用的完整性。
實例內容經過一個模塊來進行講解,能夠更好的瞭解模塊之間的對比性,以及模塊與模塊以前的耦合性。
目錄結構以下:
/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 其餘通用模板文件目錄
要讓一個新添加的模塊加入到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’;在前面章節的內容已經有說明,每增長一個模塊都須要在全局配置文件中添加進行,也就是對模塊進行註冊使用。
添加文件:/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(){} 獲取模塊服務配置信息
模塊配置文件主要對路由、視圖等進行配置,此處配置關係到整個模塊的訪問方式及其餘使用方式。
添加文件:/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佈局
在此咱們仍而後前面章節提到的 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’);
模型是ZF2對數據庫操做的核心內容,也是進行數據過濾、數據交換的功能專區。
此文件包括數據交換、表單數據過濾功能;添加 /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() {} 過濾器
此文件爲數據庫操做網關,實現對數據庫的一系列操做;添加文件:/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的記錄行
添加文件:/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'
),
));
}
}
添加文件:/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(){} 設置數據庫網關
模板是數據處理後的最終展示平臺,也是用戶操做與感知的入口。Album模塊使用的模板有4個:index.phtml , add.phtml , edit.phtml , delete.phtml , paginator.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'));
?>
添加文件:/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();
?>
添加文件:/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();
?>
添加文件:/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>
添加文件:/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 |
||||||||||||||||||
|
||||||||||||||||||
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一個完整模塊的存在形式,以及與其餘模塊同時並存且同時協同工做的具體應用,在進行具體項目開發的時候能夠借鑑或參考此例以便開發出不一樣功能的模塊,使用項目模塊可以共同協同工做。
用戶認證在項目的開發過程是一個不可或缺的重要組成部分,他的做用擔負着對整個的項目的合法認證及准入機制。
在開始前先創建一個數據表用來存放用戶的認證字段,通俗的說就是用戶名與密碼。還使用以前的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’);
爲了方便講解與引用,將認證類放在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(); 實例化一個認證服務,以實現持久性認證
認證類已經創建好,將在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的認證類進行改進和擴展以適應具體項目的要求。
通過前面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的世界。祝願全部開發者一路順利。謝謝。