程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> 網頁編程 >> PHP編程 >> 關於PHP編程 >> ThinkPHP- 3.1,thinkphp-3.1

ThinkPHP- 3.1,thinkphp-3.1

編輯:關於PHP編程

ThinkPHP- 3.1,thinkphp-3.1


基礎:

1. 基礎概念

LAMP

LAMP是基於Linux,Apache,MySQL和PHP的開放資源網絡開發平台。這個術語來自歐洲,在那裡這些程序常用來作為一種標准開發環境。名字來源於每個程序的第一個字母。每個程序在所有權裡都符合開放源代碼標准:Linux是開放系統;Apache是最通用的網絡服務器;MySQL是帶有基於網絡管理附加工具的關系數據庫;PHP是流行的對象腳本語言,它包含了多數其它語言的優秀特征來使得它的網絡開發更加有效。開發者在Windows操作系統下使用這些Linux環境裡的工具稱為使用WAMP。
雖然這些開放源代碼程序本身並不是專門設計成同另外幾個程序一起工作的,但由於它們都是影響較大的開源軟件,擁有很多共同特點,這就導致了這些組件經常在一起使用。在過去的幾年裡,這些組件的兼容性不斷完善,在一起的應用情形變得更加普遍。並且它們為了改善不同組件之間的協作,已經創建了某些擴展功能。目前,幾乎在所有的Linux發布版中都默認包含了這些產品。Linux操作系統、Apache服務器、MySQL數據庫和Perl、PHP或者 Python語言,這些產品共同組成了一個強大的Web應用程序平台。
隨著開源潮流的蓬勃發展,開放源代碼的LAMP已經與J2EE和.Net商業軟件形成三足鼎立之勢,並且該軟件開發的項目在軟件方面的投資成本較低,因此受到整個IT界的關注。從網站的流量上來說,70%以上的訪問流量是LAMP來提供的,LAMP是最強大的網站解決方案.

OOP

面向對象編程(Object Oriented Programming,OOP,面向對象程序設計)是一種計算機編程架構。OOP 的一條基本原則是計算機程序是由單個能夠起到子程序作用的單元或對象組合而成。OOP 達到了軟件工程的三個主要目標:重用性、靈活性和擴展性。為了實現整體運算,每個對象都能夠接收信息、處理數據和向其它對象發送信息。OOP 主要有以下的概念和組件: 
組件 - 數據和功能一起在運行著的計算機程序中形成的單元,組件在 OOP 計算機程序中是模塊和結構化的基礎。 
抽象性 - 程序有能力忽略正在處理中信息的某些方面,即對信息主要方面關注的能力。 
封裝 - 也叫做信息封裝:確保組件不會以不可預期的方式改變其它組件的內部狀態;只有在那些提供了內部狀態改變方法的組件中,才可以訪問其內部狀態。每類組件都提供了一個與其它組件聯系的接口,並規定了其它組件進行調用的方法。 
多態性 - 組件的引用和類集會涉及到其它許多不同類型的組件,而且引用組件所產生的結果得依據實際調用的類型。 
繼承性 - 允許在現存的組件基礎上創建子類組件,這統一並增強了多態性和封裝性。典型地來說就是用類來對組件進行分組,而且還可以定義新類為現存的類的擴展,這樣就可以將類組織成樹形或網狀結構,這體現了動作的通用性。 
由於抽象性、封裝性、重用性以及便於使用等方面的原因,以組件為基礎的編程在腳本語言中已經變得特別流行。

MVC

MVC是一個設計模式,它強制性的使應用程序的輸入、處理和輸出分開。使用MVC應用程序被分成三個核心部件:模型(M)、視圖(V)、控制器(C),它們各自處理自己的任務。 
視圖 :視圖是用戶看到並與之交互的界面。對老式的Web應用程序來說,視圖就是由HTML元素組成的界面,在新式的Web應用程序中,HTML依舊在視圖中扮演著重要的角色,但一些新的技術已層出不窮,它們包括Adobe Flash和象XHTML,XML/XSL,WML等一些標識語言和Web services。如何處理應用程序的界面變得越來越有挑戰性。MVC一個大的好處是它能為你的應用程序處理很多不同的視圖。在視圖中其實沒有真正的處理發生,不管這些數據是聯機存儲的還是一個雇員列表,作為視圖來講,它只是作為一種輸出數據並允許用戶操縱的方式。 
模型 :模型表示企業數據和業務規則。在MVC的三個部件中,模型擁有最多的處理任務。例如它可能用象EJBs和ColdFusion Components這樣的構件對象來處理數據庫。被模型返回的數據是中立的,就是說模型與數據格式無關,這樣一個模型能為多個視圖提供數據。由於應用於模型的代碼只需寫一次就可以被多個視圖重用,所以減少了代碼的重復性。 
控制器 :控制器接受用戶的輸入並調用模型和視圖去完成用戶的需求。所以當單擊Web頁面中的超鏈接和發送HTML表單時,控制器本身不輸出任何東西和做任何處理。它只是接收請求並決定調用哪個模型構件去處理請求,然後確定用哪個視圖來顯示模型處理返回的數據。 
現在我們總結MVC的處理過程,首先控制器接收用戶的請求,並決定應該調用哪個模型來進行處理,然後模型用業務邏輯來處理用戶的請求並返回數據,最後控制器用相應的視圖格式化模型返回的數據,並通過表示層呈現給用戶。

ORM

對象-關系映射(Object/Relation Mapping,簡稱ORM),是隨著面向對象的軟件開發方法發展而產生的。面向對象的開發方法是當今企業級應用開發環境中的主流開發方法,關系數據庫是企業級應用環境中永久存放數據的主流數據存儲系統。對象和關系數據是業務實體的兩種表現形式,業務實體在內存中表現為對象,在數據庫中表現為關系數據。內存中的對象之間存在關聯和繼承關系,而在數據庫中,關系數據無法直接表達多對多關聯和繼承關系。因此,對象-關系映射(ORM)系統一般以中間件的形式存在,主要實現程序對象到關系數據庫數據的映射。
面向對象是從軟件工程基本原則(如耦合、聚合、封裝)的基礎上發展起來的,而關系數據庫則是從數學理論發展而來的,兩套理論存在顯著的區別。為了解決這個不匹配的現象,對象關系映射技術應運而生。

AOP

AOP(Aspect-Oriented Programming,面向方面編程),可以說是OOP(Object-Oriented Programing,面向對象編程)的補充和完善。OOP引入封裝、繼承和多態性等概念來建立一種對象層次結構,用以模擬公共行為的一個集合。當我們需要為分散的對象引入公共行為的時候,OOP則顯得無能為力。也就是說,OOP允許你定義從上到下的關系,但並不適合定義從左到右的關系。例如日志功能。日志代碼往往水平地散布在所有對象層次中,而與它所散布到的對象的核心功能毫無關系。對於其他類型的代碼,如安全性、異常處理和透明的持續性也是如此。這種散布在各處的無關的代碼被稱為橫切(cross-cutting)代碼,在OOP設計中,它導致了大量代碼的重復,而不利於各個模塊的重用。而AOP技術則恰恰相反,它利用一種稱為“橫切”的技術,剖解開封裝的對象內部,並將那些影響了多個類的公共行為封裝到一個可重用模塊,並將其名為“Aspect”,即方面。所謂“方面”,簡單地說,就是將那些與業務無關,卻為業務模塊所共同調用的邏輯或責任封裝起來,便於減少系統的重復代碼,降低模塊間的耦合度,並有利於未來的可操作性和可維護性。AOP代表的是一個橫向的關系,如果說“對象”是一個空心的圓柱體,其中封裝的是對象的屬性和行為;那麼面向方面編程的方法,就仿佛一把利刃,將這些空心圓柱體剖開,以獲得其內部的消息。而剖開的切面,也就是所謂的“方面”了。然後它又以巧奪天功的妙手將這些剖開的切面復原,不留痕跡。
使用“橫切”技術,AOP把軟件系統分為兩個部分:核心關注點和橫切關注點。業務處理的主要流程是核心關注點,與之關系不大的部分是橫切關注點。橫切關注點的一個特點是,他們經常發生在核心關注點的多處,而各處都基本相似。比如權限認證、日志、事務處理。Aop 的作用在於分離系統中的各種關注點,將核心關注點和橫切關注點分離開來。正如Avanade公司的高級方案構架師Adam Magee所說,AOP的核心思想就是“將應用程序中的商業邏輯同對其提供支持的通用服務進行分離。”

CURD

CURD是一個數據庫技術中的縮寫詞,一般的項目開發的各種參數的基本功能都是CURD。它代表創建(Create)、更新(Update)、讀取(Read)和刪除(Delete)操作。CURD 定義了用於處理數據的基本原子操作。之所以將CURD 提升到一個技術難題的高度是因為完成一個涉及在多個數據庫系統中進行CURD操作的匯總相關的活動,其性能可能會隨數據關系的變化而有非常大的差異。
CURD在具體的應用中並非一定使用create、update 、read和delete字樣的方法,但是他們完成的功能是一致的。例如,ThinkPHP就是使用add、save、select和delete方法表示模型的CURD操作。

ActiveRecord

Active Record(中文名:活動記錄)是一種領域模型模式,特點是一個模型類對應關系型數據庫中的一個表,而模型類的一個實例對應表中的一行記錄。Active Record 和 Row Gateway (行記錄入口)十分相似,但前者是領域模型,後者是一種數據源模式。關系型數據庫往往通過外鍵來表述實體關系,Active Record 在數據源層面上也將這種關系映射為對象的關聯和聚集。   Active Record 適合非常簡單的領域需求,尤其在領域模型和數據庫模型十分相似的情況下。如果遇到更加復雜的領域模型結構(例如用到繼承、策略的領域模型),往往需要使用分離數據源的領域模型,結合 Data Mapper (數據映射器)使用。
Active Record 驅動框架一般兼有 ORM 框架的功能,但 Active Record 不是簡單的 ORM,正如和 Row Gateway 的區別。由Rails最早提出,遵循標准的ORM模型:表映射到記錄,記錄映射到對象,字段映射到對象屬性。配合遵循的命名和配置慣例,能夠很大程度的快速實現模型的操作,而且簡潔易懂。

單一入口

單一入口通常是指一個項目或者應用具有一個統一(但並不一定是唯一)的入口文件,也就是說項目的所有功能操作都是通過這個入口文件進行的,並且往往入口文件是第一步被執行的。
單一入口的好處是項目整體比較規范,因為同一個入口,往往其不同操作之間具有相同的規則。另外一個方面就是單一入口帶來的好處是控制較為靈活,因為攔截方便了,類似如一些權限控制、用戶登錄方面的判斷和操作可以統一處理了。
或者有些人會擔心所有網站都通過一個入口文件進行訪問,是否會造成太大的壓力,其實這是杞人憂天的想法。

2. 目錄結構

目錄/文件說明 ThinkPHP.php 框架入口文件 Common 框架公共文件目錄 Conf 框架配置文件目錄 Lang 框架系統語言目錄 Lib 系統核心基類庫目錄 Tpl 系統模板目錄 Extend 框架擴展目錄(關於擴展目錄的詳細信息請參考後面的擴展章節) 注意:如果你下載的是核心版本,有可能Extend目錄是空的,因為ThinkPHP本身不依賴任何擴展。

3. 命名規范

使用ThinkPHP開發的過程中應該盡量遵循下列命名規范:
  • 類文件都是以.class.php為後綴(這裡是指的ThinkPHP內部使用的類庫文件,不代表外部加載的類庫文件),使用駝峰法命名,並且首字母大寫,例如DbMysql.class.php;
  • 確保文件的命名和調用大小寫一致,是由於在類Unix系統上面,對大小寫是敏感的(而ThinkPHP在調試模式下面,即使在Windows平台也會嚴格檢查大小寫);
  • 類名和文件名一致(包括上面說的大小寫一致),例如 UserAction類的文件命名是UserAction.class.php, InfoModel類的文件名是InfoModel.class.php, 並且不同的類庫的類命名有一定的規范;
  • 函數、配置文件等其他類庫文件之外的一般是以.php為後綴(第三方引入的不做要求);
  • 函數的命名使用小寫字母和下劃線的方式,例如 get_client_ip;
  • 方法的命名使用駝峰法,並且首字母小寫或者使用下劃線“_”,例如 getUserName,_parseType,通常下劃線開頭的方法屬於私有方法;
  • 屬性的命名使用駝峰法,並且首字母小寫或者使用下劃線“_”,例如 tableName、_instance,通常下劃線開頭的屬性屬於私有屬性;
  • 以雙下劃線“__”打頭的函數或方法作為魔法方法,例如 __call 和 __autoload;
  • 常量以大寫字母和下劃線命名,例如 HAS_ONE和 MANY_TO_MANY;
  • 配置參數以大寫字母和下劃線命名,例如HTML_CACHE_ON;
  • 語言變量以大寫字母和下劃線命名,例如MY_LANG,以下劃線打頭的語言變量通常用於系統語言變量,例如 _CLASS_NOT_EXIST_;
  • 對變量的命名沒有強制的規范,可以根據團隊規范來進行;
  • ThinkPHP的模板文件默認是以.html 為後綴(可以通過配置修改);
  • 數據表和字段采用小寫加下劃線方式命名,並注意字段名不要以下劃線開頭,例如 think_user 表和 user_name字段,類似 _username 這樣的數據表字段可能會被過濾。
在ThinkPHP裡面,有一個函數命名的特例,就是單字母大寫函數,這類函數通常是某些操作的快捷定義,或者有特殊的作用。例如,ADSL方法等等。 另外有一點非常關鍵,ThinkPHP默認全部使用UTF-8編碼,所以請確保你的程序文件采用UTF-8編碼格式保存,並且去掉BOM信息頭(去掉BOM頭信息有很多方式,不同的編輯器都有設置方法,也可以用工具進行統一檢測和處理),否則可能導致很多意想不到的問題。

4. CBD架構

ThinkPHP3.0版本引入了全新的CBD(核心Core+行為Behavior+驅動Driver)架構模式,因為從底層開始,框架就采用核心+行為+驅動的架構體系,核心保留了最關鍵的部分,並在重要位置設置了標簽用以標記,其他功能都采用行為擴展和驅動的方式組合,開發人員可以根據自己的需要,對某個標簽位置進行行為擴展或者替換,就可以方便的定制框架底層,也可以在應用層添加自己的標簽位置和添加應用行。而標簽位置類似於AOP概念中的“切面”,行為都是圍繞這個“切面”來進行編程,如果把系統內置的核心擴展看成是一種標准模式的話,那麼用戶可以把這一切的行為定制打包成一個新的模式,所以在ThinkPHP裡面,稱之為模式擴展,事實上,模式擴展不僅僅可以替換和增加行為,還可以對底層的MVC進行替換和修改,以達到量身定制的目的。利用這一新的特性,開發人員可以方便地通過模式擴展為自己量身定制一套屬於自己或者企業的開發框架,新版的模式擴展是框架擴展的集大成者,開創了新的裡程碑,這正是新版的真正魅力所在。

5. 開發流程

使用ThinkPHP創建應用的一般開發流程是:
  • 系統設計、創建數據庫和數據表;(可選)
  • 項目命名並創建項目入口文件,開啟調試模式;
  • 完成項目配置;
  • 創建項目函數庫;(可選)
  • 開發項目需要的擴展(模式、驅動、標簽庫等);(可選)
  • 創建控制器類;
  • 創建模型類;(可選)
  • 創建模板文件;
  • 運行和調試、分析日志;
  • 開發和設置緩存功能;(可選)
  • 添加路由支持;(可選)
  • 安全檢查;(可選 )
  • 部署到生產環境。

6. 入口文件

ThinkPHP采用單一入口模式進行項目部署和訪問,無論完成什麼功能,一個項目都有一個統一(但不一定是唯一)的入口。應該說,所有項目都是從入口文件開始的,並且所有的項目的入口文件是類似的,入口文件中主要包括:
  • 定義框架路徑、項目路徑和項目名稱(可選)
  • 定義調試模式和運行模式的相關常量(可選)
  • 載入框架入口文件(必須)

7. 項目目錄

生成的項目目錄結構和系統目錄類似,包括: 目錄說明 Common 項目公共文件目錄,一般放置項目的公共函數 Conf 項目配置目錄,項目所有的配置文件都放在這裡 Lang 項目語言包目錄(可選 如果不需要多語言支持 可刪除) Lib 項目類庫目錄,通常包括Action和Model子目錄 Tpl 項目模板目錄,支持模板主題 Runtime 項目運行時目錄,包括Cache(模板緩存)、Temp(數據緩存)、Data(數據目錄)和Logs(日志文件)子目錄,如果存在分組的話,則首先是分組目錄。 如果需要把index.php 移動到App目錄的外面,只需要在入口文件中增加項目名稱和項目路徑定義。
  1. <?php
  2.     //定義項目名稱
  3.     define('APP_NAME', 'App');
  4.     //定義項目路徑
  5.     define('APP_PATH', './App/');
  6.     //加載框架入文件
  7.     require './App/ThinkPHP/ThinkPHP.php';
APP_NAME 是指項目名稱,注意APP_NAME 不要隨意設置,通常是項目的目錄名稱,如果你的項目是直接部署在Web根目錄下面的話,那麼需要設置APP_NAME 為空。
APP_PATH 是指項目路徑(必須以“/”結束),項目路徑是指項目的Common、Lib目錄所在的位置,而不是項目入口文件所在的位置。
注意:在類Unix或者Linux環境下面Runtime目錄需要可寫權限。

8. 部署目錄

目錄/文件說明 ThinkPHP 系統目錄(下面的目錄結構同上面的系統目錄) Public 網站公共資源目錄(存放網站的Css、Js和圖片等資源) Uploads 網站上傳目錄(用戶上傳的統一目錄) Home 項目目錄(下面的目錄結構同上面的應用目錄) Admin 後台管理項目目錄 …… 更多的項目目錄 index.php 項目Home的入口文件 admin.php 項目Admin的入口文件 …… 更多的項目入口文件 項目的模板文件還是放到項目的Tpl目錄下面,只是將外部調用的資源文件, 包括圖片 JS 和CSS統一放到網站的公共目錄Public下面,分Images、Js和Css子目錄存放,如果有可能的話,甚至也可以把這些資源文件單獨放一個外部的服務器遠程調用,並進行優化。

事實上,系統目錄和項目目錄可以放到非WEB訪問目錄下面,網站目錄下面只需要放置Public公共目錄和入口文件,從而提高網站的安全性。

 

如果希望自己設置目錄,可以在入口文件裡面更改RUNTIME_PATH常量進行更改,例如:
  1. define('RUNTIME_PATH','./App/temp/');
注意RUNTIME_PATH目錄必須設置為可寫權限。
除了自定義編譯緩存目錄之外,還支持自定義編譯緩存文件名,例如:
  1. define('RUNTIME_FILE','./App/temp/runtime_cache.php');
ThinkPHP框架中所有配置文件的定義格式均采用返回PHP數組的方式,格式為:
  1. //項目配置文件
  2.  return array(
  3.     'DEFAULT_MODULE'     => 'Index', //默認模塊
  4.     'URL_MODEL'          => '2', //URL模式
  5.     'SESSION_AUTO_START' => true, //是否開啟session
  6.     //更多配置參數
  7.     //...
  8.  );
配置參數不區分大小寫(因為無論大小寫定義都會轉換成小寫)
還可以在配置文件中可以使用二維數組來配置更多的信息,例如:
  1. //項目配置文件
  2.  return array(
  3.     'DEFAULT_MODULE'     => 'Index', //默認模塊
  4.     'URL_MODEL'          => '2', //URL模式
  5.     'SESSION_AUTO_START' => true, //是否開啟session
  6.     'USER_CONFIG'        => array(
  7.         'USER_AUTH' => true,
  8.         'USER_TYPE' => 2,
  9.     ),
  10.     //更多配置參數
  11.     //...
  12.  );
需要注意的是,二級參數配置區分大小寫,也就說讀取確保和定義一致。

9. 慣例配置和項目配置,調試配置

慣例重於配置是系統遵循的一個重要思想,系統內置有一個慣例配置文件(位於系統目錄下面的Conf\convention.php),按照大多數的使用對常用參數進行了默認配置。 項目配置文件是最常用的配置文件,項目配置文件位於項目的配置文件目錄Conf下面,文件名是config.php。
在項目配置文件裡面除了添加內置的參數配置外,還可以額外添加項目需要的配置參數。 如果沒有配置應用狀態,系統默認則默認為debug狀態,也就是說默認的配置參數是:
  1. 'APP_STATUS' => 'debug', //應用調試模式狀態
debug.php配置文件只需要配置和項目配置文件以及系統調試配置文件不同的參數或者新增的參數。
如果想在調試模式下面增加應用狀態,例如測試狀態,則可以在項目配置文件中改變設置如下:
  1. 'APP_STATUS' => 'test', //應用調試模式狀態
由於調試模式沒有任何緩存,因此涉及到較多的文件IO操作和模板實時編譯,所以在開啟調試模式的情況下,性能會有一定的下降,但不會影響部署模式的性能。
注意:一旦關閉調試模式,項目的調試配置文件即刻失效。

10. 分組配置和讀取配置,動態配置

如果啟用了模塊分組,則可以在對每個分組單獨定義配置文件,分組配置文件位於:
項目配置目錄/分組名稱/config.php
可以通過如下配置啟用分組:
  1. 'APP_GROUP_LIST' => 'Home,Admin', //項目分組設定
  2.  'DEFAULT_GROUP'  => 'Home', //默認分組
現在定義了Home和Admin兩個分組,則我們可以定義分組配置文件如下:
Conf/Home/config.php
Conf/Admin/config.php
每個分組的配置文件僅在當前分組有效,分組配置的定義格式和項目配置是一樣的。
注意:分組名稱區分大小寫,必須和定義的分組名一致。 定義了配置文件之後,可以使用系統提供的C方法(如果覺得比較奇怪的話,可以借助Config單詞來幫助記憶)來讀取已有的配置:
  1. C('參數名稱')//獲取已經設置的參數值
例如,C('APP_STATUS') 可以讀取到系統的調試模式的設置值,如果APP_STATUS尚未存在設置,則返回NULL。 C方法同樣可以用於讀取二維配置:
  1. C('USER_CONFIG.USER_TYPE')//獲取用戶配置中的用戶類型設置
因為配置參數是全局有效的,因此C方法可以在任何地方讀取任何配置,哪怕某個設置參數已經生效過期了。 在具體的Action方法裡面,我們仍然可以對某些參數進行動態配置,主要是指那些還沒有被使用的參數。 
設置新的值:
  1. C('參數名稱','新的參數值');
例如,我們需要動態改變數據緩存的有效期的話,可以使用
  1. C('DATA_CACHE_TIME','60');
也可以支持二維數組的讀取和設置,使用點語法進行操作,如下:
獲取已經設置的參數值:
  1. C('USER_CONFIG.USER_TYPE');
設置新的值:
  1. C('USER_CONFIG.USER_TYPE','1');
3.1版本開始,C函數支持配置保存功能,僅對批量設置有效,使用方法:
  1. C($array,'name');
其中array是一個數組變量,會把批量設置後的配置參數列表保存到name標識的緩存數據中

獲取緩存的設置列表數據 可以用
  1. C('','name'); //或者C(null,'name');
會讀取name標識的緩存配置數據到當前配置數據(合並)。

11. 擴展配置

項目配置文件在部署模式的時候會納入編譯緩存,也就是說編譯後再修改項目配置文件就不會立刻生效,需要刪除編譯緩存後才能生效。擴展配置文件則不受此限制影響,即使在部署模式下面,修改配置後可以實時生效,並且配置格式和項目配置一樣。
設置擴展配置的方式如下(多個文件用逗號分隔):
  1. 'LOAD_EXT_CONFIG' => 'user,db', // 加載擴展配置文件
項目設置了加載擴展配置文件user.php 和db.php分別用於用戶配置和數據庫配置,那麼會自動加載項目配置目錄下面的配置文件Conf/user.php和Conf/db.php。 如果希望采用二級配置方式,可以設置如下:
  1. 'LOAD_EXT_CONFIG' => array(
  2.     'USER' => 'user', //用戶配置
  3.     'DB'   => 'db', //數據庫配置
  4.  ), //加載擴展配置文件
同樣的user.php 配置文件內容,但最終獲取用戶參數的方式就變成了:
  1. C('USER.USER_AUTH_ID');
這種方式可以避免大項目情況中的參數沖突問題。 下面的一些配置文件已經被系統使用,請不要作為自定義的擴展配置重新定義: 文件名說明 config.php 項目配置文件 tags.php 項目行為配置文件 alias.php 項目別名定義文件 debug.php 項目調試模式配置文件(以及項目設置的APP_STATUS對應的配置文件) core.php 項目追加的核心編譯列表文件(不會覆蓋核心編譯列表)

12. 函數庫

ThinkPHP中的函數庫可以分為系統函數庫和項目函數庫。

系統函數庫

庫系統函數庫位於系統的Common目錄下面,有三個文件:
common.php是全局必須加載的基礎函數庫,在任何時候都可以直接調用;
functions.php是框架標准模式的公共函數庫,其他模式可以替換加載自己的公共函數庫或者對公共函數庫中的函數進行重新定義;
runtime.php是框架運行時文件,僅在調試模式或者編譯過程才會被加載,因此其中的方法在項目中不能直接調用;

項目函數庫

庫項目函數庫通常位於項目的Common目錄下面,文件名為common.php,該文件會在執行過程中自動加載,並且合並到項目編譯統一緩存,如果使用了分組部署方式,並且該目錄下存在"分組名稱/function.php"文件,也會根據當前分組執行時對應進行自動加載,因此項目函數庫的所有函數也都可以無需手動載入而直接使用。
如果項目配置中使用了動態函數加載配置的話,項目Common目錄下面可能會存在更多的函數文件,動態加載的函數文件不會納入編譯緩存。
在特殊的情況下,模式可以改變自動加載的項目函數庫的位置或者名稱。

擴展函數庫

庫我們可以在項目公共目錄下面定義擴展函數庫,方便需要的時候加載和調用。擴展函數庫的函數定義規范和項目函數庫一致,只是函數庫文件名可以隨意命名,一般來說,擴展函數庫並不會自動加載,除非你設置了動態載入。

函數加載

系統函數庫和項目函數庫中的函數無需加載就可以直接調用,對於項目的擴展函數庫,可以采用下面兩種方式調用:
動態載入
我們可以在項目配置文件中定義LOAD_EXT_FILE參數,例如:
  1. "LOAD_EXT_FILE"=>"user,db"
通過上面的設置,就會執行過程中自動載入項目公共目錄下面的擴展函數庫文件user.php和db.php,這樣就可以直接在項目中調用擴展函數庫user.php和db.php中的函數了,而且擴展函數庫的函數修改是實時生效的。

手動載入
如果你的函數只是個別模塊偶爾使用,則不需要采用自動加載方式,可以在需要調用的時候采用load方法手動載入,方式如下:
  1. load("@.user")
@.user表示加載當前項目的user函數文件,這樣就可以直接user.php擴展函數庫中的函數了。

13. 類庫

ThinkPHP的類庫包括基類庫和應用類庫,系統的類庫命名規則如下: 類庫規則示例 控制器類 模塊名+Action 例如 UserAction、InfoAction 模型類 模型名+Model 例如 UserModel、InfoModel 行為類 行為名+Behavior 例如CheckRouteBehavior Widget類 Widget名+Widget 例如BlogInfoWidget 驅動類 引擎名+驅動名 例如DbMysql表示mysql數據庫驅動、CacheFile表示文件緩存驅動

基類庫

基類庫是指符合ThinkPHP類庫規范的系統類庫,包括ThinkPHP的核心基類庫和擴展基類庫。核心基類庫目錄位於系統的Lib目錄,核心基類庫也就是Think類庫,擴展基類庫位於Extend/Library目錄,可以擴展ORG 、Com擴展類庫。核心基類庫的作用是完成框架的通用性開發而必須的基礎類和內置支持類等,包含有: 目錄調用路徑說明 Lib/Core Think.Core 核心類庫包 Lib/Behavior Think.Behavior 內置行為類庫包 Lib/Driver Think.Driver 內置驅動類庫包 Lib/Template Think.Template 內置模板引擎類庫包 核心類庫包下面包含下面核心類庫: 類名說明 Action 系統基礎控制器類 App 系統應用類 Behavior 系統行為基礎類 Cache 系統緩存類 Db 系統抽象數據庫類 Dispatcher URL調度類 Log 系統日志類 Model 系統基礎模型類 Think 系統入口和靜態類 ThinkException 系統基礎異常類 View 視圖類 Widget 系統Widget基礎類

應用類庫

應用類庫是指項目中自己定義或者使用的類庫,這些類庫也是遵循ThinkPHP的命名規范。應用類庫目錄位於項目目錄下面的Lib目錄。應用類庫的范圍很廣,包括Action類庫、Model類庫或者其他的工具類庫,通常包括: 目錄調用路徑說明 Lib/Action @.Action或自動加載 控制器類庫包 Lib/Model @.Model或自動加載 模型類庫包 Lib/Behavior 用B方法調用或自動加載 應用行為類庫包 Lib/Widget 用W方法在模板中調用 應用Widget類庫包 項目根據自己的需要可以在項目類庫目錄下面添加自己的類庫包,例如Lib/Common、Lib/Tool等。

類庫導入

一、Import顯式導入

ThinkPHP模擬了Java的類庫導入機制,統一采用import方法進行類文件的加載。import方法是ThinkPHP內建的類庫導入方法,提供了方便和靈活的文件導入機制,完全可以替代PHP的require和include方法。例如:
  1. import("Think.Util.Session");
  2.  import("App.Model.UserModel");
import方法具有緩存和檢測機制,相同的文件不會重復導入,如果導入了不同的位置下面的同名類庫文件,系統也不會再次導入 注意:在Unix或者Linux主機下面是區別大小寫的,所以在使用import方法的時候要注意目錄名和類庫名稱的大小寫,否則會導入失敗。對於import方法,系統會自動識別導入類庫文件的位置,ThinkPHP的約定是Think、ORG、Com包的導入作為基類庫導入,否則就認為是項目應用類庫導入。
  1. import("Think.Util.Session");
  2.  import("ORG.Util.Page");
上面兩個方法分別導入了Think基類庫的Util/Session.class.php文件和ORG擴展類庫包的Util/Page.class.php文件。
要導入項目的應用類庫文件也很簡單,使用下面的方式就可以了,和導入基類庫的方式看起來差不多:
  1. import("MyApp.Action.UserAction");
  2.  import("MyApp.Model.InfoModel");
上面的方式分別表示導入MyApp項目下面的Lib/Action/UserAction.class.php和Lib/Model/InfoModel.class.php類文件。 通常我們都是在當前項目裡面導入所需的類庫文件,所以,我們可以使用下面的方式來簡化代碼
  1. import("@.Action.UserAction");
  2.  import("@.Model.InfoModel");

二,別名導入

除了命名空間的導入方式外,import方法還可以支持別名導入,要使用別名導入,首先要定義別名,我們可以在項目配置目錄下面增加alias.php 用以定義項目中需要用到的類庫別名,例如:
  1. return array(
  2.     'rbac' =>LIB_PATH.'Common/Rbac.class.php',
  3.     'page' =>LIB_PATH.'Common/Page.class.php',
  4.  );
那麼,現在就可以直接使用:
  1. import("rbac");
  2.  import("page");
導入Rbac和Page類,別名導入方式禁止使用import方法的第二和第三個參數,別名導入方式的效率比命名空間導入方式要高效,缺點是需要預先定義相關別名。

導入第三方類庫

第三方類庫統一放置在系統擴展目錄下的Vendor 目錄,並且使用vendor 方法導入,其參數和 import 方法是 一致的,只是默認的值有針對變化。 例如,我們把 Zend 的 Filter\Dir.php 放到 Vendor 目錄下面,這個時候 Dir 文件的路徑就是  Vendor\Zend\Filter\Dir.php,我們使用vendor 方法導入只需要使用:
  1. Vendor('Zend.Filter.Dir');
就可以導入Dir類庫了。
Vendor方法也可以支持和import方法一樣的基礎路徑和文件名後綴參數,例如:
  1. Vendor('Zend.Filter.Dir',dirname(__FILE__),'.class.php');

自動加載

在大多數情況下,我們無需手動導入類庫,而是通過配置采用自動加載機制即可,自動加載機制是真正的按需加載,可以很大程度的提高性能。自動加載有三種情況,按照加載優先級從高到低分別是:別名自動加載、系統規則自動加載和自定義路徑自動加載。

一、別名自動加載

在前面我們提到了別名的定義方式,並且采用了import方法進行別名導入,其實所有定義別名的類庫都無需再手動加載,系統會按需自動加載。

二、 系統規則自動加載

果你沒有定義別名的話,系統會首先按照內置的規則來判斷加載,系統規則僅針對行為類、模型類和控制器類,搜索規則如下: 類名規則說明 行為類 規則1 搜索系統類庫目錄下面的Behavior目錄 規則2 搜索系統擴展目錄下面的Behavior目錄 規則3 搜索應用類庫目錄下面的Behavior目錄 規則4 如果啟用了模式擴展,則搜索模式擴展目錄下面的Behavior目錄 模型類 規則1 如果啟用分組,則搜索應用類庫目錄的Model/當前分組 目錄 規則2 搜索應用類庫下面的Model目錄 規則3 搜索系統擴展目錄下面的Model目錄 控制器類 規則1 如果啟用分組,則搜索應用類庫目錄的Action/當前分組 目錄 規則2 搜索項目類庫目錄下面的Action目錄 規則3 搜索系統擴展目錄下面的Action目錄 注意:搜索的優先順序從上至下 ,一旦找到則返回,後面規則不再檢測。如果全部規則檢測完成後依然沒有找到類庫,則開始進行第三個自定義路徑自動加載檢測。

三、 自定義路徑自動加載

當你的類庫比較集中在某個目錄下面,而且不想定義太多的別名導入的話,可以使用自定義路徑自動加載方式,這種方式需要在項目配置文件中添加自動加載的搜索路徑,例如:
  1. 'APP_AUTOLOAD_PATH' =>'@.Common,@.Tool',
表示,在當前項目類庫目錄下面的Common和Tool目錄下面的類庫可以自動加載。多個搜索路徑之間用逗號分割,並且注意定義的順序也就是自動搜索的順序。
注意:自動搜索路徑定義只能采用命名空間方式,也就是說這種方式只能自動加載項目類庫目錄和基類庫目錄下面的類庫文件。 ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

控制器:

1. URL模式

傳統方式的文件入口訪問會變成由URL的參數來統一解析和調度。 ThinkPHP支持四種URL模式,可以通過設置URL_MODEL參數來定義,包括普通模式、PATHINFO、REWRITE和兼容模式。 一、普通模式:設置URL_MODEL 為0
采用傳統的URL參數模式
  1. http://serverName/appName/?m=module&a=action&id=1
二、PATHINFO模式(默認模式):設置URL_MODEL 為1
默認情況使用PATHINFO模式,ThinkPHP內置強大的PATHINFO支持,提供靈活和友好URL支持。PATHINFO模式自動識別模塊和操作,例如
  1. http://serverName/appName/module/action/id/1/或者
  1. http://serverName/appName/module,action,id,1/
三、REWRITE模式: 設置URL_MODEL 為2
該URL模式和PATHINFO模式功能一樣,除了可以不需要在URL裡面寫入口文件,和可以定義.htaccess 文件外。在開啟了Apache的URL_REWRITE模塊後,就可以啟用REWRITE模式了,具體參考下面的URL重寫部分。 四、兼容模式: 設置URL_MODEL 為3
兼容模式是普通模式和PATHINFO模式的結合,並且可以讓應用在需要的時候直接切換到PATHINFO模式而不需要更改模板和程序,還可以和URL_WRITE模式整合。兼容模式URL可以支持任何的運行環境。 兼容模式的效果是:
  1. http://serverName/appName/?s=/module/action/id/1/
並且也可以支持參數分割符號的定義,例如在URL_PATHINFO_DEPR為~的情況下,下面的URL有效:
  1. http://serverName/appName/?s=module~action~id~1
其實是利用了VAR_PATHINFO參數,用普通模式的實現模擬了PATHINFO的模式。但是兼容模式並不需要自己傳s變量,而是由系統自動完成URL部分。正是由於這個特性,兼容模式可以和PATHINFO模式之間直接切換,而不需更改模板文件裡面的URL地址連接。我們建議的方式是采用PATHINFO模式開發,如果部署的時候環境不支持PATHINFO則改成兼容URL模式部署即可,程序和模板都不需要做任何改動。

2. 模塊和操作

http://域名/項目名/分組名/模塊名/操作名/其他參數
Dispatcher會根據URL地址來獲取當前需要執行的項目、分組(如果有定義的話)模塊、操作以及其他參數,在某些情況下,項目名可能不會出現在URL地址中(通常情況下入口文件則代表了某個項目,而且入口文件可以被隱藏)。
每一個模塊就是一個控制器類,通常位於項目的Lib\Action目錄下面。
3.1版本開始,增加ACTION_SUFFIX配置參數,用於設置操作方法的後綴。
例如,如果設置:
  1. 'ACTION_SUFFIX'=>'Act'
那麼訪問某個模塊的add操作對應讀取模塊類的操作方法則由原來的add方法變成addAct方法。

3. 定義控制器和空操作,空模塊

一個應用如果不需要和數據庫交互的時候可以不需要定義模型類,但是必須定義Action控制器,一般位於項目的Lib/Action目錄下面。
Action控制器的定義非常簡單,只要繼承Action基礎類就可以了,例如:
  1. Class UserAction extends Action{}
控制器文件的名稱是UserAction.class.php。
空操作是指系統在找不到指定的操作方法的時候,會定位到空操作(_empty)方法來執行,利用這個機制,我們可以實現錯誤頁面和一些URL的優化。
空模塊的概念是指當系統找不到指定的模塊名稱的時候,系統會嘗試定位空模塊(EmptyAction),利用這個機制我們可以用來定制錯誤頁面和進行URL的優化。

4. 模塊分組

模塊分組相關的配置參數包括: 配置參數說明 APP_GROUP_LIST 項目分組列表(配置即表示開啟分組) DEFAULT_GROUP 默認分組(默認值為Home) TMPL_FILE_DEPR 分組模板下面模塊和操作的分隔符,默認值為“/” VAR_GROUP 分組的URL參數名,默認為g(普通模式URL才需要) 例如我們把當前的項目分成Home和Admin兩個組,分別表示前台和後台功能,那麼只需要在項目配置中添加下面的配置:
  1. 'APP_GROUP_LIST' => 'Home,Admin', //項目分組設定
  2.  'DEFAULT_GROUP'  => 'Home', //默認分組
多個分組之間用逗號分隔即可,默認分組只允許設置一個。

5. URL偽靜態

ThinkPHP支持偽靜態URL設置,可以通過設置URL_HTML_SUFFIX參數隨意在URL的最後增加你想要的靜態後綴,而不會影響當前操作的正常執行。例如,我們設置
  1. 'URL_HTML_SUFFIX'=>'shtml'
的話,我們可以把下面的URL
  1. http://serverName/Blog/read/id/1
變成
  1. http://serverName/Blog/read/id/1.shtml
後者更具有靜態頁面的URL特征,但是具有和前面的URL相同的執行效果,並且不會影響原來參數的使用。
注意:偽靜態後綴設置時可以不包含後綴中的“.”。所以,下面的配置其實是等效的:
  1. 'URL_HTML_SUFFIX'=>'.shtml'
偽靜態設置後,如果需要動態生成一致的URL,可以使用U方法在模板文件裡面生成URL。 3.1版本開始,默認情況下,可以支持所有的靜態後綴,並且會記錄當前的偽靜態後綴到常量__EXT__,但不會影響正常的頁面訪問。如果要獲取當前的偽靜態後綴,通過常量__EXT__獲取即可。 如果只是希望支持配置的偽靜態後綴,可以直接設置成可以支持多個後綴,例如:
  1. 'URL_HTML_SUFFIX'=>'html|shmtl|xml' // 多個用 | 分割
如果設置了多個偽靜態後綴的話,使用U函數生成的URL地址中會默認使用第一個後綴,也支持指定後綴生成url地址。

6. URL路由

ThinkPHP支持URL路由功能,要啟用路由功能,需要設置URL_ROUTER_ON 參數為true。開啟路由功能後,並且配置URL_ROUTE_RULES參數後,系統會自動進行路由檢測,如果在路由定義裡面找到和當前URL匹配的路由名稱,就會進行路由解析和重定向。
詳情見:http://doc.thinkphp.cn/manual/url_route.html

7. URL重寫

詳情見:http://doc.thinkphp.cn/manual/url_rewrite.html

8. URL生成

為了配合所使用的URL模式,我們需要能夠動態的根據當前的URL設置生成對應的URL地址,為此,ThinkPHP內置提供了U方法,用於URL的動態生成,可以確保項目在移植過程中不受環境的影響。
U方法的定義規則如下(方括號內參數根據實際應用決定):
  1. U('[分組/模塊/操作]?參數' [,'參數','偽靜態後綴','是否跳轉','顯示域名'])
如果不定義項目和模塊的話 就表示當前項目和模塊名稱,下面是一些簡單的例子:
  1. U('User/add') // 生成User模塊的add操作的URL地址
  2. U('Blog/read?id=1') // 生成Blog模塊的read操作 並且id為1的URL地址
  3. U('Admin/User/select') // 生成Admin分組的User模塊的select操作的URL地址
U方法的第二個參數支持數組和字符串兩種定義方式,如果只是字符串方式的參數可以在第一個參數中定義,例如:
  1. U('Blog/cate',array('cate_id'=>1,'status'=>1))
  2. U('Blog/cate','cate_id=1&status=1')
  3. U('Blog/cate?cate_id=1&status=1')
三種方式是等效的,都是 生成Blog模塊的cate操作 並且cate_id為1 status為1的URL地址
但是不允許使用下面的定義方式來傳參數
  1. U('Blog/cate/cate_id/1/status/1')
根據項目的不同URL設置,同樣的U方法調用可以智能地對應產生不同的URL地址效果,例如針對
  1. U('Blog/read?id=1')這個定義為例。
如果當前URL設置為普通模式的話,最後生成的URL地址是: 
http://serverName/index.php?m=Blog&a=read&id=1
如果當前URL設置為PATHINFO模式的話,同樣的方法最後生成的URL地址是: 
http://serverName/index.php/Blog/read/id/1
如果當前URL設置為REWRITE模式的話,同樣的方法最後生成的URL地址是: 
http://serverName/Blog/read/id/1
如果當前URL設置為REWRITE模式,並且設置了偽靜態後綴為.html的話,同樣的方法最後生成的URL地址是: 
http://serverName/Blog/read/id/1.html
U方法還可以支持路由,如果我們定義了一個路由規則為:
  1.  'news/:id\d'=>'News/read'
那麼可以使用
  1. U('/news/1')
最終生成的URL地址是:
  1. http://serverName/index.php/news/1

注意:如果你是在模板文件中直接使用U方法的話,需要采用 {:U('參數1', '參數2'…)} 的方式,具體參考模板引擎章節的8.3 使用函數內容。

如果你的應用涉及到多個子域名的操作地址,那麼也可以在U方法裡面指定需要生成地址的域名,例如:

  1. U('Blog/[email protected]','id=1');

@後面傳入需要指定的域名即可。

此外,U方法的第5個參數如果設置為true,表示自動識別當前的域名,並且會自動根據子域名部署設置APP_SUB_DOMAIN_DEPLOY和APP_SUB_DOMAIN_RULES自動匹配生成當前地址的子域名。
如果開啟了URL_CASE_INSENSITIVE,則會統一生成小寫的URL地址。

 

9. URL大小寫

只要在項目配置中,增加:

 

  1. 'URL_CASE_INSENSITIVE' =>true

就可以實現URL訪問不再區分大小寫了。

這裡需要注意一個地方,如果我們定義了一個UserTypeAction的模塊類,那麼URL的訪問應該是:

  1. http://serverName/index.php/user_type/list
  2.  //而不是
  3. http://serverName/index.php/usertype/list

利用系統提供的U方法可以為你自動生成相關的URL地址。
如果設置

  1. 'URL_CASE_INSENSITIVE' =>false

的話,URL就又變成:

  1. http://serverName/index.php/UserType/list

注意:URL不區分大小寫並不會改變系統的命名規范,並且只有按照系統的命名規范後才能正確的實現URL不區分大小寫。

 

10. 前置和後置操作

系統會檢測當前操作是否具有前置和後置操作,如果存在就會按照順序執行,前置和後置操作的方法名是在要執行的方法前面加 _before_和_after_,例如:

 

  1. class CityAction extends Action{
  2.     //前置操作方法
  3.     public function _before_index(){
  4.         echo 'before<br/>';
  5.     }
  6.     public function index(){
  7.         echo 'index<br/>';
  8.     }
  9.     //後置操作方法
  10.     public function _after_index(){
  11.         echo 'after<br/>';
  12.     }
  13.  }
對於任何操作方法我們都可以按照這樣的規則來定義前置和後置方法。
如果當前的操作並沒有定義操作方法,而是直接渲染模板文件,那麼如果定義了前置 和後置方法的話,依然會生效。真正有模板輸出的可能僅僅是當前的操作,前置和後置操作一般情況是沒有任何輸出的。
需要注意的是,在有些方法裡面使用了exit或者錯誤輸出之類的話 有可能不會再執行後置方法了。
例如,如果在當前操作裡面調用了系統Action的error方法,那麼將不會再執行後置操作,但是不影響success方法的後置方法執行。

11. 跨模塊調用

例如,我們在Index模塊調用User模塊的操作方法
  1. class IndexAction extends Action{
  2.     public function index(){
  3.         //實例化UserAction
  4.         $User = new UserAction();
  5.         //其他用戶操作
  6.          //...
  7.         $this->display(); //輸出頁面模板
  8.     }
  9.  }
因為系統會自動加載Action控制器,因此 我們不需要導入UserAction類就可以直接實例化。
並且為了方便跨模塊調用,系統內置了A方法和R方法。    $User = A('User');
事實上,A方法還支持跨分組或者跨項目調用,默認情況下是調用當前項目下面的模塊。
跨項目調用的格式是:
A('[項目名://][分組名/]模塊名')
例如:
  1. A('User') //表示調用當前項目的User模塊
  2. A('Admin://User') //表示調用Admin項目的User模塊
  3. A('Admin/User') //表示調用Admin分組的User模塊
  4. A('Admin://Tool/User') //表示調用Admin項目Tool分組的User模塊
R方法表示調用一個模塊的某個操作方法,調用格式是:
R('[項目名://][分組名/]模塊名/操作名',array('參數1','參數2'…))
例如:
  1. R('User/info') //表示調用當前項目的User模塊的info操作方法
  2. R('Admin/User/info') //表示調用Admin分組的User模塊的info操作方法
  3. R('Admin://Tool/User/info') //表示調用Admin項目Tool分組的User模塊的info操作方法
R方法還支持對調用的操作方法需要傳入參數,例如User模塊中我們定義了一個info方法:
  1. class UserAction extends Action{
  2.     protected function info($id){
  3.         $User = M('User');
  4.         $User->find($id);
  5.         //...
  6.     }
  7.  }
接下來,我們可以在其他模塊中調用:
  1. R('User/info',array(15))
表示調用當前項目的User模塊的info操作方法,並且id參數傳入15

12. 頁面跳轉

系統的Action類內置了兩個跳轉方法success和error,用於頁面跳轉提示,而且可以支持ajax提交。使用方法很簡單,舉例如下:
  1. $User = M('User'); //實例化User對象
  2. $result = $User->add($data); 
  3. if($result){
  4.     //設置成功後跳轉頁面的地址,默認的返回頁面是$_SERVER['HTTP_REFERER']
  5.     $this->success('新增成功', 'User/list');
  6. } else {
  7.     //錯誤頁面的默認跳轉頁面是返回前一頁,通常不需要設置
  8.     $this->error('新增失敗');
  9. }
Success和error方法都有對應的模板,並且是可以設置的,默認的設置是兩個方法對應的模板都是:
  1. //默認錯誤跳轉對應的模板文件
  2. 'TMPL_ACTION_ERROR' => THINK_PATH . 'Tpl/dispatch_jump.tpl';
  3. //默認成功跳轉對應的模板文件
  4. 'TMPL_ACTION_SUCCESS' => THINK_PATH . 'Tpl/dispatch_jump.tpl';
也可以使用項目內部的模板文件
  1. //默認錯誤跳轉對應的模板文件
  2. 'TMPL_ACTION_ERROR' => 'Public:error';
  3. //默認成功跳轉對應的模板文件
  4. 'TMPL_ACTION_SUCCESS' => 'Public:success';
模板文件可以使用模板標簽,並且可以使用下面的模板變量: $msgTitle 操作標題 $message 頁面提示信息 $status 操作狀態 1表示成功 0 表示失敗 具體還可以由項目本身定義規則 $waitSecond 跳轉等待時間 單位為秒 $jumpUrl 跳轉頁面地址 success和error方法會自動判斷當前請求是否屬於Ajax請求,如果屬於Ajax請求則會調用ajaxReturn方法返回信息,具體可以參考後面的AJAX返回部分。 3.1版本開始,error和success方法支持傳值,無論是跳轉模板方式還是ajax方式 都可以使用assign方式傳參。例如:
  1. $this->assign('var1','value1');
  2. $this->assign('var2','value2');
  3. $this->error('錯誤的參數','要跳轉的URL地址');
當正常方式提交的時候,var1和var2變量會賦值到錯誤模板的模板變量。
當采用AJAX方式提交的時候,會自動調用ajaxReturn方法傳值過去(包括跳轉的URL地址url和狀態值status)

13. 重定向

Action類的redirect方法可以實現頁面的重定向功能。
redirect方法的參數用法和U函數的用法一致(參考上面的URL生成部分),例如:
  1. //重定向到New模塊的Category操作
  2. $this->redirect('New/category', array('cate_id' => 2), 5, '頁面跳轉中...');
上面的用法是停留5秒後跳轉到News模塊的category操作,並且顯示頁面跳轉中字樣,重定向後會改變當前的URL地址。
如果你僅僅是想重定向要一個指定的URL地址,而不是到某個模塊的操作方法,可以直接使用redirect方法重定向,例如:
  1. //重定向到指定的URL地址
  2. redirect('/New/category/cate_id/2', 5, '頁面跳轉中...')
Redirect方法的第一個參數是一個URL地址。

14. 獲取系統變量

  1. $this->方法名("變量名",["過濾方法"],["默認值"])
方法名可以支持: 方法名含義 _get 獲取GET參數 _post 獲取POST參數 _param 自動判斷請求類型獲取GET、POST或者PUT參數(3.1新增) _request 獲取REQUEST 參數 _put 獲取PUT 參數 _session 獲取 $_SESSION 參數 _cookie 獲取 $_COOKIE 參數 _server 獲取 $_SERVER 參數 _globals 獲取 $GLOBALS參數 變量名:(必須)是要獲取的系統變量的名稱
過濾方法:(可選)可以用任何的內置函數或者自定義函數名,如果沒有指定的話,采用默認的htmlspecialchars函數進行安全過濾(由DEFAULT_FILTER 參數配置),參數就是前面方法名獲取到的值,也就是說如果調用:
  1. $this->_get("name");
最終調用的結果就是 htmlspecialchars($_GET["name"]),如果要改變過濾方法,可以使用:
  1. $this->_get("name","strip_tags");
默認值:(可選)是要獲取的參數變量不存在的情況下設置的默認值,例如:
  1. $this->_get("id","strip_tags",0);
如果$_GET["id"] 不存在的話,會返回0。

如果沒有設置任何默認值的話,系統默認返回NULL。

也可以支持多函數過濾。
例如,可以設置:

  1.  'DEFAULT_FILTER'=>'htmlspecialchars,strip_tags'

那麼在控制器類如果調用

  1. $this->_get('id');

的話,會依次對$_GET['id'] 變量進行htmlspecialchars和strip_tags方法過濾後返回結果。
下面調用方式也同樣支持:

  1. $this->_get('id','htmlspecialchars,strip_tags',0);

其他變量獲取方法用法相同。
支持獲取全部變量,例如:

  1. $this->_get();

表示獲取$_GET變量值。

支持不過濾處理

如果不希望過濾某個參數,可以使用

  1. $this->_get('id',false);
  2. $this->_post('id',false);
  3.  //或者
  4. $this->_get('id','');
  5. $this->_post('id','');

第二個參數使用false或者空字符串則表示不作任何過濾處理,即使我們有配置默認的過濾方法。
如果我們忽略第二個參數調用的話

  1. $this->_get('id');
  2. $this->_post('id');

則表示調用默認的過濾方法(由DEFAULT_FILTER參數進行配置)。

3.1版本開始,Action類增加_param方法,可以自動根據當前請求類型(例如GET POST)獲取參數。
例如:

  1. $this->_param('id');

當前為get方式提交的時候,就是獲取$_GET['id'](進行默認過濾後)的值
當前為post方式提交的時候,就是獲取$_POST['id'](進行默認過濾後)的值

還可以用_param方法獲取URL中的參數

  1. $this->_param(0); // 獲取PATHINFO地址中的第一個參數
  2. $this->_param(2); // 獲取PATHINFO地址中的第3個參數

15. 判斷請求類型

系統Action類內置了一些判斷方法用於判斷請求類型,包括: 方法說明 isGet 判斷是否是GET方式提交 isPost 判斷是否是POST方式提交 isPut 判斷是否是PUT方式提交 isDelete 判斷是否是DELETE方式提交 isHead 判斷是否是HEAD提交 使用舉例如下:
  1. class UserAction extends Action{
  2.     public function update(){
  3.         if ($this->isPost()){
  4.             $User = M('User');
  5.             $User->create();
  6.             $User->save();
  7.             $this->success('保存完成');
  8.         }else{
  9.             $this->error('非法請求');
  10.         }
  11.     }
  12.  }
另外還提供了一個判斷當前是否屬於AJAX提交的方法
isAjax 是否屬於AJAX提交
需要注意的是,如果使用的是ThinkAjax或者自己寫的Ajax類庫的話,需要在表單裡面添加一個隱藏域,告訴後台屬於ajax方式提交,默認的隱藏域名稱是ajax(可以通過VAR_AJAX_SUBMIT配置),如果是JQUERY類庫的話,則無需添加任何隱藏域即可自動判斷。

16. 獲取URL參數

我們可以把URL地址 News/archive/2012/01/15 按照“/”分成多個參數,$_GET["_URL_"][0] 獲取的就是News,$_GET["_URL_"][1]獲取的就是archive,依次類推,可以通過數字索引獲取所有的URL參數。
3.0版開始支持URL地址中的PATH_INFO方式的URL的參數獲取方式,需要配置
VAR_URL_PARAMS參數,默認值是:
  1.     'VAR_URL_PARAMS'      => '_URL_', // PATHINFO URL參數變量
如果這個值不為空的話,就可以獲取URL地址裡面的PATH_INFO URL參數,例如
我們訪問
  1. http://serverName.com/index.php/Blog/read/2012/03
則可以在Blog控制器的read操作方法裡面采用 
$GET['_URL_'][2] 獲取參數,表示獲取PATH_INFO的URL參數
Blog/read/2012/03中的第3個參數(數組索引從0開始)
  1. $year = $GET['_URL_'][2]; // 2012
  2. $month = $GET['_URL_'][3]; //  03
3.1版本開始,建議使用_param方法獲取URL參數,_param方法方法是3.1新增的方法,可以自動根據當前請求類型獲取參數。
_param方法的用法同_get和_post等方法,區別在於,_param方法能夠自動根據當前請求類型自動獲取相應的參數,例如:
如果當前是get請求方式,
  1. $this->_param('id'); 
將會返回$_GET['id'] 的處理數據
當采用POST請求方式的時候,同樣的代碼將會返回$_POST['id']的處理數據
如果采用的是PUT請求,那麼會自動返回PUT的處理數據,而無需開發人員進行判斷。
並且需要注意的是,無論是什麼方式的請求,系統都可以支持URL參數的獲取,如果C('VAR_URL_PARAMS')設置不為空的話,就可以使用:
  1. $this->_param(1);
  2. $this->_param(2);
來獲取URL地址中的某個參數。
  1. $year = $this->_param(2);
  2. $month = $this->_param(3);
的方式來獲取。
這樣的好處是可以不需要使用路由功能就可以獲取某個不規則的URL地址中的參數。

17. AJAX返回

系統支持任何的AJAX類庫,Action類提供了ajaxReturn方法用於AJAX調用後返回數據給客戶端。並且支持JSON、XML和EVAL三種方式給客戶端接受數據,通過配置DEFAULT_AJAX_RETURN進行設置,默認配置采用JSON格式返回數據,在選擇不同的AJAX類庫的時候可以使用不同的方式返回數據。要使用ThinkPHP的ajaxReturn方法返回數據的話,需要遵守一定的返回數據的格式規范。 ThinkPHP返回的數據格式包括: status 操作狀態 info 提示信息 data 返回數據 調用示例:
  1. $this->ajaxReturn(返回數據,提示信息,操作狀態);
返回數據data可以支持字符串、數字和數組、對象,返回客戶端的時候根據不同的返回格式進行編碼後傳輸。如果是JSON格式,會自動編碼成JSON字符串,如果是XML方式,會自動編碼成XML字符串,如果是EVAL方式的話,只會輸出字符串data數據,並且忽略status和info信息。
  1. $User = M("User"); // 實例化User對象
  2. $result = $User->add($data);
  3.  if ($result){
  4.     // 成功後返回客戶端新增的用戶ID,並返回提示信息和操作狀態
  5.     $this->ajaxReturn($result,"新增成功!",1);
  6.  }else{
  7.     // 錯誤後返回錯誤的操作狀態和提示信息
  8.     $this->ajaxReturn(0,"新增錯誤!",0);
  9.  }
注意,確保你是使用AJAX提交才使用ajaxReturn方法。
在客戶端接受數據的時候,根據使用的編碼格式進行解析即可。 如果需要改變Ajax返回的數據格式,可以在控制器Action中增加ajaxAssign方法定義,定義格式如下:
  1. public function ajaxAssign(&$result) {
  2.     // 返回數據中增加url屬性
  3.     $result['url'] = $this->url;
  4.  }
3.1版本以後,ajaxReturn方法可以更加靈活的進行ajax傳值,並且廢棄了ajaxAssign方法擴展。能夠完全定義傳值的數組和類型,例如:
  1. $data['status'] = 1;
  2. $data['info'] = 'info';
  3. $data['size'] = 9;
  4. $data['url'] = $url;
  5. $this->ajaxReturn($data,'JSON');
data傳值數組可以隨意定義。
改進後的ajaxReturn方法也兼容之前的寫法:
  1. $this->ajaxReturn($data,'info',1);
系統會自動把info和1兩個參數並入$data數組中,等同於賦值
  1. $data['info'] = 'info';
  2. $data['status'] = 1;

18. Action參數綁定

http://doc.thinkphp.cn/manual/action_param_bind.html

19. 多層控制器支持

3.1版本開始,控制器支持自定義分層。同時A方法增加第二個參數layer,用於設置控制器分層。
例如
  1. A('User','Event');
表示實例化Lib/Event/UserEvent.class.php。
UserEvent如果繼承Action類的話,可以使用Action類所有的功能。
  1. A('User','Api');
表示實例化Lib/Api/UserApi.class.php
分層控制器僅用於內部調用,URL訪問的控制器還是Action層,但是可以配置
DEFAULT_C_LAYER修改默認控制器層名稱(該參數默認值為Action)。 ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

模型:

模型類的命名規則是除去表前綴的數據表名稱,采用駝峰法命名,並且首字母大寫,然後加上模型類的後綴定義Model,例如: 模型名(類名)約定對應數據表(假設數據庫的前綴定義是 think_) UserModel think_user UserTypeModel think_user_type 如果你的規則和上面的系統約定不符合,那麼需要設置Model類的數據表名稱屬性。 在ThinkPHP的模型裡面,有幾個關於數據表名稱的屬性定義:
屬性說明 tableName 不包含表前綴的數據表名稱,一般情況下默認和模型名稱相同,只有當你的表名和當前的模型類的名稱不同的時候才需要定義。 trueTableName 包含前綴的數據表名稱,也就是數據庫中的實際表名,該名稱無需設置,只有當上面的規則都不適用的情況或者特殊情況下才需要設置。 dbName 定義模型當前對應的數據庫名稱,只有當你當前的模型類對應的數據庫名稱和配置文件不同的時候才需要定義。

1. 模型實例化

在ThinkPHP中,可以無需進行任何模型定義。只有在需要封裝單獨的業務邏輯的時候,模型類才是必須被定義的。 1、實例化基礎模型(Model) 類
在沒有定義任何模型的時候,我們可以使用下面的方法實例化一個模型類來進行操作:
  1. //實例化User模型
  2. $User = new Model('User');
  3.  //或者使用M()快捷方法實例化,和上面的方法是等效的
  4. $User = M('User');
  5.  //執行其他的數據操作
  6. $User->select();
這種方法最簡單高效,因為不需要定義任何的模型類,所以支持跨項目調用。缺點也是因為沒有自定義的模型類,因此無法寫入相關的業務邏輯,只能完成基本的CURD操作。 2、實例化其他公共模型類
  1. $User = new CommonModel('User');
模型類的實例化方法有三個參數,第一個參數是模型名稱,第二個參數用於設置數據表的前綴(留空則取當前項目配置的表前綴),第三個參數用於設置當前使用的數據庫連接信息(留空則取當前項目配置的數據庫連接信息),例如:
  1. $User = new CommonModel('User','think_','db_config');
用M方法實現的話,上面的方法可以寫成:
  1. $User = M('CommonModel:User','think_','db_config');
M方法默認是實例化Model類,第二個參數用於指定表前綴,第三個參數就可以指定其他的數據庫連接信息。 模型類CommonModel必須繼承Model。我們可以在CommonModel類裡面定義一些通用的邏輯方法,就可以省去為每個數據表定義具體的模型類。 3、實例化用戶自定義模型(×××Model)類
這種情況是使用的最多的,一個項目不可避免的需要定義自身的業務邏輯實現,就需要針對每個數據表定義一個模型類,例如UserModel 、InfoModel等等。
定義的模型類通常都是放到項目的Lib\Model目錄下面。例如,
  1. <?php
  2.     class UserModel extends Model{
  3.         public function getTopUser(){
  4.             //添加自己的業務邏輯
  5.              // ...
  6.         }
  7.     }
其實模型類還可以繼承一個用戶自定義的公共模型類,而不是只能繼承Model類。 要實例化自定義模型類,可以使用下面的方式:
  1. <?php
  2.     //實例化自定義模型
  3.     $User = new UserModel();
  4.     //或者使用D快捷方法
  5.     $User = D('User');
  6.     //執行具體的數據操作
  7.     $User->select();
D方法可以自動檢測模型類,如果存在自定義的模型類,則實例化自定義模型類,如果不存在,則會實例化Model基類,同時對於已實例化過的模型,不會重復去實例化。 D方法還可以支持跨項目和分組調用,需要使用:
  1. //實例化Admin項目的User模型
  2. D('Admin://User')
  3.  //實例化Admin分組的User模型
  4. D('Admin/User')

2. 字段定義

字段緩存保存在Runtime/Data/_fields/ 目錄下面,緩存機制是每個模型對應一個字段緩存文件(而並非每個數據表對應一個字段緩存文件),命名格式是:
數據庫名.模型名.php
字段緩存包括數據表的字段信息、主鍵字段和是否自動增長,如果開啟字段類型驗證的話還包括字段類型信息等等,無論是用M方法還是D方法,或者用原生的實例化模型類一般情況下只要是不開啟調試模式都會生成字段緩存(字段緩存可以單獨設置關閉)。從3.1版本開始,模型的字段緩存文件名全部轉換成小寫,避免重復生成。
可以通過設置DB_FIELDS_CACHE 參數來關閉字段自動緩存,如果在開發的時候經常變動數據庫的結構,而不希望進行數據表的字段緩存,可以在項目配置文件中增加如下配置:
  1. 'DB_FIELDS_CACHE'=>false
注意:調試模式下面由於考慮到數據結構可能會經常變動,所以默認是關閉字段緩存的。 如果需要顯式獲取當前數據表的字段信息,可以使用模型類的getDbFields方法來獲取當前數據對象的全部字段信息,例如:
  1. $fields = $User->getDbFields();
如果你在部署模式下面修改了數據表的字段信息,可能需要清空Data/_fields目錄下面的緩存文件,讓系統重新獲取更新的數據表字段信息,否則會發生新增的字段無法寫入數據庫的問題。
如果不希望依賴字段緩存或者想提高性能,也可以在模型類裡面手動定義數據表字段的名稱,可以避免IO加載的效率開銷,在模型類裡面添加fields屬性即可,定義格式如下:
  1. <?php
  2.     class UserModel extends Model{
  3.         protected $fields = array(
  4.             'id', 'username', 'email', 'age', '_pk' => 'id', '_autoinc' => true
  5.         );
  6.     }

3. 數據主鍵

ThinkPHP的默認約定每個數據表的主鍵名采用統一的id作為標識,並且是自動增長類型的。系統會自動識別當前操作的數據表的字段信息和主鍵名稱,所以即使你的主鍵不是id,也無需進行額外的設置,系統會自動識別。要在外部獲取當前數據對象的主鍵名稱,請使用下面的方法:
  1. $pk = $Model->getPk();
注意:目前不支持聯合主鍵的自動獲取和操作。

 4. 屬性訪問

ThinkPHP的模型對象實例本身也是一個數據對象,可以支持對象和數組兩種方式來訪問數據屬性,例如下面的方式采用數據對象的方式來訪問User模型的屬性:
  1. //實例化User模型
  2. $User = D('User');
  3.  //查詢用戶數據
  4. $User->find(1);
  5.  //獲取name屬性的值
  6. echo $User->name;
  7.  //設置name屬性的值
  8. $User->name = 'ThinkPHP';
除了find方法會產生數據對象屬性外,data方法和create方法也會產生數據對象,例如:
  1. $User = D('User');
  2. $User->create();
  3. echo $User->name;
還有一種屬性的操作方式是通過返回數組的方式:
  1. //實例化User模型
  2. $User = D('User');
  3.  //查詢用戶數據
  4. $data = $User->find(1);
  5.  //獲取name屬性的值
  6. echo $data['name'];
  7.  //設置name屬性的值
  8. $data['name'] = 'ThinkPHP';
兩種方式的屬性獲取區別是一個是對象的屬性,一個是數組的索引,開發人員可以根據自己的需要選擇什麼方式。

5. 跨庫操作

ThinkPHP可以支持模型的同一數據庫服務器的跨庫操作,跨庫操作只需要簡單配置一個模型所在的數據庫名稱即可,例如,假設UserModel對應的數據表在數據庫user下面,而InfoModel對應的數據表在數據庫info下面,那麼我們只需要進行下面的設置即可。
  1. class UserModel extends Model {
  2.     protected $dbName = 'user';
  3.  }
  4.  class InfoModel extends Model {
  5.     protected $dbName = 'info';
  6.  }
在進行查詢的時候,系統能夠自動添加當前模型所在的數據庫名。
  1. $User = D('User'); 
  2. $User->select();
  3. echo $User->getLastSql();
  4.  // 輸出的SQL語句為 select * from user.think_user 
模型的表前綴取的是項目配置文件定義的數據表前綴,如果跨庫操作的時候表前綴不是統一的,那麼我們可以在模型裡面單獨定義表前綴,例如:
  1. protected $tablePrefix = 'other_';
如果你沒有定義模型類,而是使用的M方法操作的話,也可以支持跨庫操作,例如:
  1. $User = M('user.User','other_'); 
表示實例化User模型,連接的是user數據庫的other_user表。

6. 連接數據庫

ThinkPHP內置了抽象數據庫訪問層,把不同的數據庫操作封裝起來,我們只需要使用公共的Db類進行操作,而無需針對不同的數據庫寫不同的代碼和底層實現,Db類會自動調用相應的數據庫驅動來處理。如果應用需要使用數據庫,必須配置數據庫連接信息,數據庫的配置文件有多種定義方式。 常用的配置方式是在項目配置文件中添加下面的參數:
  1. <?php
  2.     //項目配置文件
  3.     return array(
  4.         //數據庫配置信息
  5.         'DB_TYPE'   => 'mysql', // 數據庫類型
  6.         'DB_HOST'   => 'localhost', // 服務器地址
  7.         'DB_NAME'   => 'thinkphp', // 數據庫名
  8.         'DB_USER'   => 'root', // 用戶名
  9.         'DB_PWD'    => '', // 密碼
  10.         'DB_PORT'   => 3306, // 端口
  11.         'DB_PREFIX' => 'think_', // 數據庫表前綴 
  12.         //其他項目配置參數
  13.         // ...
  14.     );
或者采用如下配置
  1. 'DB_DSN' => 'mysql://username:password@localhost:3306/DbName'
如果兩種配置參數同時存在的話,DB_DSN配置參數優先。
注意:如果要設置分布式數據庫,暫時不支持DB_DSN方式配置。
如果采用PDO驅動的話,則必須首先配置DB_TYPE 為pdo,然後還需要單獨配置其他參數,例如:
  1. //PDO連接方式
  2.  'DB_TYPE'   => 'pdo', // 數據庫類型
  3.  'DB_USER'   => 'root', // 用戶名
  4.  'DB_PWD'    => '', // 密碼
  5.  'DB_PREFIX' => 'think_', // 數據庫表前綴 
  6.  'DB_DSN'    => 'mysql:host=localhost;dbname=thinkphp;charset=UTF-8'
注意:PDO方式的DB_DSN配置格式有所區別,根據不同的數據庫類型設置有所不同。
配置文件定義的數據庫連接信息一般是系統默認采用的,因為一般一個項目的數據庫訪問配置是相同的。該方法系統在連接數據庫的時候會自動獲取,無需手動連接。可以對每個項目和不同的分組定義不同的數據庫連接信息,如果開啟了調試模式的話,還可以在不同的應用狀態的配置文件裡面定義獨立的數據庫配置信息。 第二種 在模型類裡面定義connection屬性
如果在某個模型類裡面定義了connection屬性的話,則實例化該自定義模型的時候會采用定義的數據庫連接信息,而不是配置文件中設置的默認連接信息,通常用於某些數據表位於當前數據庫連接之外的其它數據庫,例如:
  1. //在模型裡單獨設置數據庫連接信息
  2.  protected $connection = array(
  3.     'db_type'  => 'mysql',
  4.     'db_user'  => 'root',
  5.     'db_pwd'   => '1234',
  6.     'db_host'  => 'localhost',
  7.     'db_port'  => '3306',
  8.     'db_name'  => 'thinkphp'
  9.  );
也可以采用DSN方式定義,例如:
  1. //或者使用DSN定義
  2.  protected $connection = 'mysql://root:1234@localhost:3306/thinkphp';
如果我們已經在配置文件中配置了額外的數據庫連接信息,例如:
  1. //數據庫配置1
  2.  'DB_CONFIG1' => array(
  3.     'db_type'  => 'mysql',
  4.     'db_user'  => 'root',
  5.     'db_pwd'   => '1234',
  6.     'db_host'  => 'localhost',
  7.     'db_port'  => '3306',
  8.     'db_name'  => 'thinkphp'
  9.  ),
  10.  //數據庫配置2
  11.  'DB_CONFIG2' => 'mysql://root:1234@localhost:3306/thinkphp';
那麼,我們可以把模型類的屬性定義改為:
  1. //調用配置文件中的數據庫配置1
  2.  protected $connection = 'DB_CONFIG1';
  3.  //調用配置文件中的數據庫配置2
  4.  protected $connection = 'DB_CONFIG2';
如果采用的是M方法實例化模型的話,也可以支持傳入不同的數據庫連接信息,例如:
  1. $User = M('User','other_','mysql://root:1234@localhost/demo'); 
表示實例化User模型,連接的是demo數據庫的other_user表,采用的連接信息是第三個參數配置的。如果我們在項目配置文件中已經配置了DB_CONFIG2的話,也可以采用:
  1. $User = M('User','other_','DB_CONFIG2'); 
如果你的個別數據表沒有定義任何前綴的話,可以在前綴參數中傳入NULL,例如:
  1. $User = M('User',Null,'DB_CONFIG2'); 
表示實例化User模型,連接的是demo數據庫的user表。需要注意的是,ThinkPHP的數據庫連接的惰性的,所以並不是在實例化的時候就連接數據庫,而是在有實際的數據操作的時候才會去連接數據庫(額外的情況是,在系統第一次實例化模型的時候,會自動連接數據庫獲取相關模型類對應的數據表的字段信息)。

7. 切換數據庫

只需要調用Model類的db方法,用法:
  1. Model->db("數據庫編號","數據庫配置");
數據庫編號用數字格式,對於已經調用過的數據庫連接,是不需要再傳入數據庫連接信息的,系統會自動記錄。對於默認的數據庫連接,內部的數據庫編號是0,因此為了避免沖突,請不要再次定義數據庫編號為0的數據庫配置。
數據庫配置的定義方式和模型定義connection屬性一樣,支持數組、字符串以及調用配置參數三種格式。
Db方法調用後返回當前的模型實例,直接可以繼續進行模型的其他操作,所以該方法可以在查詢的過程中動態切換,例如:
  1. $this->db(1,"mysql://root:123456@localhost:3306/test")->query("查詢SQL");
該方法添加了一個編號為1的數據庫連接,並自動切換到當前的數據庫連接。
當第二次切換到相同的數據庫的時候,就不需要傳入數據庫連接信息了,可以直接使用:
  1. $this->db(1)->query("查詢SQL");
如果需要切換到默認的數據庫連接,只需要調用:
  1. $this->db(0);
如果我們已經在項目配置中定義了其他的數據庫連接信息,例如:
  1. //數據庫配置1
  2.  'DB_CONFIG1' = array(
  3.     'db_type'  => 'mysql',
  4.     'db_user'  => 'root',
  5.     'db_pwd'   => '1234',
  6.     'db_host'  => 'localhost',
  7.     'db_port'  => '3306',
  8.     'db_name'  => 'thinkphp'
  9.  ),
  10.  //數據庫配置2
  11.  'DB_CONFIG2' => 'mysql://root:1234@localhost:3306/thinkphp';
我們就可以直接在db方法中調用配置進行連接了:
  1. $this->db(1,"DB_CONFIG1")->query("查詢SQL");
  2. $this->db(2,"DB_CONFIG2")->query("查詢SQL");
如果切換數據庫之後,數據表和當前不一致的話,可以使用table方法指定要操作的數據表:
  1. $this->db(1)->table("top_user")->find();
我們也可以直接用M方法切換數據庫,例如:
  1. M("User","think_","mysql://root:123456@localhost:3306/test")->query("查詢SQL");
或者
  1. M("User","think_","DB_CONFIG1")->query("查詢SQL");

8. 分布式數據庫

ThinkPHP內置了分布式數據庫的支持,包括主從式數據庫的讀寫分離,但是分布式數據庫必須是相同的數據庫類型。配置DB_DEPLOY_TYPE 為1 可以采用分布式數據庫支持。如果采用分布式數據庫,定義數據庫配置信息的方式如下:
  1. //在項目配置文件裡面定義
  2.  return array(
  3.     //分布式數據庫配置定義
  4.     'DB_TYPE'   => 'mysql', //分布式數據庫類型必須相同
  5.     'DB_HOST'   => '192.168.0.1,192.168.0.2',
  6.     'DB_NAME'   => 'thinkphp', //如果相同可以不用定義多個
  7.     'DB_USER'   => 'user1,user2',
  8.     'DB_PWD'    => 'pwd1,pwd2',
  9.     'DB_PORT'   => '3306',
  10.     'DB_PREFIX' => 'think_',
  11.     //其他配置參數
  12.     // ...
  13.  );
連接的數據庫個數取決於DB_HOST定義的數量,所以即使是兩個相同的IP也需要重復定義,但是其他的參數如果存在相同的可以不用重復定義。
還可以設置分布式數據庫的讀寫是否分離,默認的情況下讀寫不分離,也就是每台服務器都可以進行讀寫操作,對於主從式數據庫而言,需要設置讀寫分離,通過下面的設置就可以:
  1. 'DB_RW_SEPARATE'=>true,
在讀寫分離的情況下,默認第一個數據庫配置是主服務器的配置信息,負責寫入數據,如果設置了DB_MASTER_NUM參數,則可以支持多個主服務器寫入。其它的都是從數據庫的配置信息,負責讀取數據,數量不限制。每次連接從服務器並且進行讀取操作的時候,系統會隨機進行在從服務器中選擇。
CURD操作系統會自動判斷當前執行的方法的讀操作還是寫操作,如果你用的是原生SQL,那麼需要注意系統的默認規則:
寫操作必須用模型的execute方法,讀操作必須用模型的query方法,否則會發生主從讀寫錯亂的情況。

注意:主從數據庫的數據同步工作不在框架實現,需要數據庫考慮自身的同步或者復制機制。

 

9. 創建數據

在進行數據操作之前,我們往往需要手動創建需要的數據,例如對於提交的表單數據:

 

  1. // 獲取表單的POST數據
  2. $data['name'] = $_POST['name'];
  3. $data['email'] = $_POST['email'];
  4.  // 更多的表單數據值獲取
  5.  //……

很簡單的例子:

  1. // 實例化User模型
  2. $User = M('User');
  3.  // 根據表單提交的POST數據創建數據對象
  4. $User->create();
  5.  // 把創建的數據對象寫入數據庫
  6. $User->add();

Create方法支持從其它方式創建數據對象,例如,從其它的數據對象,或者數組等

  1. $data['name'] = 'ThinkPHP';
  2. $data['email'] = '[email protected]';
  3. $User->create($data);

甚至還可以支持從對象創建新的數據對象

  1. // 從User數據對象創建新的Member數據對象
  2. $User = M("User");
  3. $User->find(1);
  4. $Member = M("Member");
  5. $Member->create($User);
Create方法創建的數據對象是保存在內存中,並沒有實際寫入到數據庫中,直到使用add或者save方法才會真正寫入數據庫。
因此在沒有調用add或者save方法之前,我們都可以改變create方法創建的數據對象,例如:
  1. $User = M('User');
  2. $User->create(); //創建User數據對象
  3. $User->status = 1; // 設置默認的用戶狀態
  4. $User->create_time = time(); // 設置用戶的創建時間
  5. $User->add(); // 把用戶對象寫入數據庫
如果只是想簡單創建一個數據對象,並不需要完成一些額外的功能的話,可以使用data方法簡單的創建數據對象。
使用如下:
  1. // 實例化User模型
  2. $User = M('User');
  3.  // 創建數據後寫入到數據庫
  4. $data['name'] = 'ThinkPHP';
  5. $data['email'] = '[email protected]';
  6. $User->data($data)->add();
Data方法也支持傳入數組和對象,使用data方法創建的數據對象不會進行自動驗證和過濾操作,請自行處理。但在進行add或者save操作的時候,數據表中不存在的字段以及非法的數據類型(例如對象、數組等非標量數據)是會自動過濾的,不用擔心非數據表字段的寫入導致SQL錯誤的問題。 安全提示:
create方法如果沒有傳值,默認取$_POST數據,如果用戶提交的變量內容,含有可執行的html代碼,請進行手工過濾。
  1. $_POST['title'] = "<script>alert(1);</script>";
非法html代碼可以使用htmlspecialchars進行編碼,以防止用戶提交的html代碼在展示時被執行,以下是兩種安全處理方法。
  1. $_POST['title'] = htmlspecialchars($_POST['title']);
  2. M('User')->create();
  1. $data['title'] = $this->_post('title', 'htmlspecialchars');
  2. M('User')->create($data);
10. 字段映射 ThinkPHP的字段映射功能可以讓你在表單中隱藏真正的數據表字段,而不用擔心放棄自動創建表單對象的功能,假設我們的User表裡面有username和email字段,我們需要映射成另外的字段,定義方式如下:
  1. Class UserModel extends Model{
  2.     protected $_map = array(
  3.         'name' =>'username', // 把表單中name映射到數據表的username字段
  4.         'mail'  =>'email', // 把表單中的mail映射到數據表的email字段
  5.     );
  6.  }
這樣,在表單裡面就可以直接使用name和mail名稱作為表單數據提交了。在保存的時候會字段轉換成定義的實際數據表字段。字段映射還可以支持對主鍵的映射。 如果我們需要把數據庫中的數據顯示在表單中,並且也支持字段映射的話,需要對查詢的數據進行一下處理,處理方式是調用Model類的parseFieldsMap方法,例如:
  1. // 實例化User模型
  2. $User = M('User');
  3. $data = $User->find(3);
這個時候取出的data數據包含的是實際的username和email字段,為了方便便表單輸出,我們需要處理成字段映射顯示在表單中,就需要使用下面的代碼處理:
  1. $data = $User->parseFieldsMap($data);
這樣一來,data數據中就包含了name和mail字段數據了,而不再有username和email字段數據了。

 11. 連貫操作

ThinkPHP模型基礎類提供的連貫操作方法,可以有效的提高數據存取的代碼清晰度和開發效率,並且支持所有的CURD操作。使用也比較簡單, 假如我們現在要查詢一個User表的滿足狀態為1的前10條記錄,並希望按照用戶的創建時間排序 ,代碼如下:
  1. $User->where('status=1')->order('create_time')->limit(10)->select();
這裡的where、order和limit方法就被稱之為連貫操作方法,T除了select方法必須放到最後一個外(因為select方法並不是連貫操作方法),連貫操作T的方法調用順序沒有先後,例如,下面的代碼和上面的等效:
  1. $User->order('create_time')->limit(10)->where('status=1')->select();
如果不習慣使用連貫操作的話,還支持直接使用參數進行查詢的方式。例如上面的代碼可以改寫為:
  1. $User->select(array('order'=>'create_time','where'=>'status=1','limit'=>'10'));
使用數組參數方式的話,索引的名稱就是連貫操作的方法名稱。其實T不僅僅是查詢方法可以使用連貫操作,包括所有的CURD方法都可以使用,例如:
  1. $User->where('id=1')->field('id,name,email')->find(); 
  2. $User->where('status=1 and id=1')->delete();
連貫操作通常只有一個參數,並且僅在當此查詢或者操作有效,完成後會自動清空連貫操作的所有傳值(有個別特殊的連貫操作有多個參數,並且會記錄當前的傳值)。簡而言之,連貫操作的結果不會帶入以後的查詢。
系統支持的連貫操作方法有: 連貫操作作用支持的參數類型 where 用於查詢或者更新條件的定義 字符串、數組和對象 table 用於定義要操作的數據表名稱 字符串和數組 alias 用於給當前數據表定義別名 字符串 data 用於新增或者更新數據之前的數據對象賦值 數組和對象 field 用於定義要查詢的字段(支持字段排除) 字符串和數組 order 用於對結果排序 字符串和數組 limit 用於限制查詢結果數量 字符串和數字 page 用於查詢分頁(內部會轉換成limit) 字符串和數字 group 用於對查詢的group支持 字符串 having 用於對查詢的having支持 字符串 join* 用於對查詢的join支持 字符串和數組 union* 用於對查詢的union支持 字符串、數組和對象 distinct 用於查詢的distinct支持 布爾值 lock 用於數據庫的鎖機制 布爾值 cache 用於查詢緩存 支持多個參數 relation 用於關聯查詢(需要關聯模型支持) 字符串 所有的連貫操作都返回當前的模型實例對象(this),其中帶*標識的表示支持多次調用。 可參考:http://doc.thinkphp.cn/manual/continuous_operation.html

12. CURD操作

創建(Create)

在ThinkPHP中使用add方法新增數據到數據庫(而並不是create方法)。 使用示例如下:
  1. $User = M("User"); // 實例化User對象
  2. $data['name'] = 'ThinkPHP';
  3. $data['email'] = '[email protected]';
  4. $User->add($data);
或者使用data方法連貫操作
  1. $User->data($data)->add();
如果在add之前已經創建數據對象的話(例如使用了create或者data方法),add方法就不需要再傳入數據了。 使用create方法的例子:
  1. $User = M("User"); // 實例化User對象
  2.  // 根據表單提交的POST數據創建數據對象
  3. $User->create();
  4. $User->add(); // 根據條件保存修改的數據
如果你的主鍵是自動增長類型,並且如果插入數據成功的話,Add方法的返回值就是最新插入的主鍵值,可以直接獲取。

讀取(Read)

在ThinkPHP中讀取數據的方式很多,通常分為讀取數據和讀取數據集。 讀取數據集使用select方法(新版已經廢除原來的findall方法): 使用示例:
  1. $User = M("User"); // 實例化User對象
  2.  // 查找status值為1的用戶數據 以創建時間排序 返回10條數據
  3. $list = $User->where('status=1')->order('create_time')->limit(10)->select();
Select方法配合連貫操作方法可以完成復雜的數據查詢。而最復雜的連貫方法應該是where方法的使用。 讀取數據使用find方法: 讀取數據的操作其實和數據集的類似,select可用的所有連貫操作方法也都可以用於find方法,區別在於find方法最多只會返回一條記錄,因此limit方法對於find查詢操作是無效的。
  1. $User = M("User"); // 實例化User對象
  2.  // 查找status值為1name值為think的用戶數據 
  3. $User->where('status=1 AND name="think"')->find();
即使滿足條件的數據不止一條,find方法也只會返回第一條記錄。 如果要讀取某個字段的值,可以使用getField方法 示例如下:
  1. $User = M("User"); // 實例化User對象
  2.  // 獲取ID為3的用戶的昵稱 
  3. $nickname = $User->where('id=3')->getField('nickname');
當只有一個字段的時候,默認返回一個值。
如果需要返回數組,可以用:
  1. $this->getField('id',true); // 獲取id數組
如果傳入多個字段的話,默認返回一個關聯數組:
  1. $User = M("User"); // 實例化User對象
  2.  // 獲取所有用戶的ID和昵稱列表 
  3. $list = $User->getField('id,nickname');
返回的list是一個數組,鍵名是用戶的id, 鍵值是用戶的昵稱nickname。
如果傳入多個字段的名稱,例如:
  1. $list = $User->getField('id,nickname,email');
返回的是一個二維數組,類似select方法的返回結果,區別的是這個二維數組的鍵名是用戶的id(准確的說是getField方法的第一個字段名)。
如果我們傳入一個字符串分隔符:
  1. $list = $User->getField('id,nickname,email',':');
那麼返回的結果就是一個數組,鍵名是用戶id,鍵值是 nickname:email的輸出字符串。

getField方法的sepa參數還可以支持限制數量,例如:
  1. $this->getField('id,name',5); // 限制返回5條記錄
  2. $this->getField('id',3); // 獲取id數組 限制3條記錄
可以配合使用order方法使用。

更新(Update)

在ThinkPHP中使用save方法更新數據庫,並且也支持連貫操作的使用。
  1. $User = M("User"); // 實例化User對象
  2.  // 要修改的數據對象屬性賦值
  3. $data['name'] = 'ThinkPHP';
  4. $data['email'] = '[email protected]';
  5. $User->where('id=5')->save($data); // 根據條件保存修改的數據
為了保證數據庫的安全,避免出錯更新整個數據表,如果沒有任何更新條件,數據對象本身也不包含主鍵字段的話,save方法不會更新任何數據庫的記錄。
因此下面的代碼不會更改數據庫的任何記錄
  1. $User->save($data); 
除非使用下面的方式:
  1. $User = M("User"); // 實例化User對象
  2.  // 要修改的數據對象屬性賦值
  3. $data['id'] = 5;
  4. $data['name'] = 'ThinkPHP';
  5. $data['email'] = '[email protected]';
  6. $User->save($data); // 根據條件保存修改的數據
如果id是數據表的主鍵的話,系統自動會把主鍵的值作為更新條件來更新其他字段的值。
還有一種方法是通過create或者data方法創建要更新的數據對象,然後進行保存操作,這樣save方法的參數可以不需要傳入。
  1. $User = M("User"); // 實例化User對象
  2.  // 要修改的數據對象屬性賦值
  3. $data['name'] = 'ThinkPHP';
  4. $data['email'] = '[email protected]';
  5. $User->where('id=5')->data($data)->save(); // 根據條件保存修改的數據
使用create方法的例子:
  1. $User = M("User"); // 實例化User對象
  2.  // 根據表單提交的POST數據創建數據對象
  3. $User->create();
  4. $User->save(); // 根據條件保存修改的數據
如果只是更新個別字段的值,可以使用setField方法。
使用示例:
  1. $User = M("User"); // 實例化User對象
  2.  // 更改用戶的name值
  3. $User-> where('id=5')->setField('name','ThinkPHP');
setField方法支持同時更新多個字段,只需要傳入數組即可,例如:
  1. $User = M("User"); // 實例化User對象
  2.  // 更改用戶的name和email的值
  3. $data = array('name'=>'ThinkPHP','email'=>'[email protected]');
  4. $User-> where('id=5')->setField($data);
而對於統計字段(通常指的是數字類型)的更新,系統還提供了setInc和setDec方法。
  1. $User = M("User"); // 實例化User對象
  2. $User->where('id=5')->setInc('score',3); // 用戶的積分加3
  3. $User->where('id=5')->setInc('score'); // 用戶的積分加1
  4. $User->where('id=5')->setDec('score',5); // 用戶的積分減5
  5. $User->where('id=5')->setDec('score'); // 用戶的積分減1

刪除(Delete)

在ThinkPHP中使用delete方法刪除數據庫中的記錄。
示例如下:
  1. $User = M("User"); // 實例化User對象
  2. $User->where('id=5')->delete(); // 刪除id為5的用戶數據
  3. $User->where('status=0')->delete(); // 刪除所有狀態為0的用戶數據
delete方法可以用於刪除單個或者多個數據,主要取決於刪除條件,也就是where方法的參數,也可以用order和limit方法來限制要刪除的個數,例如:
  1. // 刪除所有狀態為0的5 個用戶數據 按照創建時間排序
  2. $User->where('status=0')->order('create_time')->limit('5')->delete(); 
可參見:http://doc.thinkphp.cn/manual/curd.html

13. ActiveRecord

ThinkPHP實現了ActiveRecords模式的ORM模型,采用了非標准的ORM模型:表映射到類,記錄映射到對象。 下面我們用AR模式來換一種方式重新完成CURD操作。 一、創建數據
  1. $User = M("User"); // 實例化User對象
  2.  // 然後直接給數據對象賦值
  3. $User->name = 'ThinkPHP';
  4. $User->email = '[email protected]';
  5.  // 把數據對象添加到數據庫
  6. $User->add();
如果使用了create方法創建數據對象的話,仍然可以在創建完成後進行賦值
  1. $User = D("User");
  2. $User->create(); // 創建User數據對象,默認通過表單提交的數據進行創建
  3.  // 增加或者更改其中的屬性
  4. $User->status = 1;
  5. $User->create_time = time();
  6.  // 把數據對象添加到數據庫
  7. $User->add(); 
二、查詢記錄 假如我們要查詢主鍵為8的某個用戶記錄,如果按照之前的方式:
  1. $User = M("User"); // 實例化User對象
  2.  // 查找id為8的用戶數據
  3. $User->where('id=8')->find();
用AR模式的話可以直接寫成:
  1. $User->find(8);
如果要根據某個字段查詢,例如查詢姓名為ThinkPHP的可以用:
  1. $User = M("User"); // 實例化User對象
  2. $User->getByName("ThinkPHP");
如果要查詢數據集,可以直接使用:
  1.  // 查找主鍵為1、3、8的多個數據
  2. $userList = $User->select('1,3,8'); 
三、更新記錄
在完成查詢後,可以直接修改數據對象然後保存到數據庫。
  1. $User->find(1); // 查找主鍵為1的數據
  2. $User->name = 'TOPThink'; // 修改數據對象
  3. $User->save(); // 保存當前數據對象
上面這種方式僅僅是示例,不代表保存操作之前一定要先查詢。因為下面的方式其實是等效的:
  1. $User->id = 1;
  2. $User->name = 'TOPThink'; // 修改數據對象
  3. $User->save(); // 保存當前數據對象
四、刪除記錄
可以刪除當前查詢的數據對象
  1. $User->find(2);
  2. $User->delete(); // 刪除當前的數據對象
或者直接根據主鍵進行刪除
  1. $User->delete(8); // 刪除主鍵為8的數據
  2. $User->delete('5,6'); // 刪除主鍵為5、6的多個數據

14. 自動驗證

大多數情況下面,數據對象是由表單提交的$_POST數據創建。需要使用系統的自動驗證功能,只需要在Model類裡面定義$_validate屬性,是由多個驗證因子組成的二維數組。
驗證因子格式:
  1. array(驗證字段,驗證規則,錯誤提示,[驗證條件,附加規則,驗證時間])
示例
  1. protected $_validate = array(
  2.     array('verify','require','驗證碼必須!'), //默認情況下用正則進行驗證
  3.     array('name','','帳號名稱已經存在!',0,'unique',1), // 在新增的時候驗證name字段是否唯一
  4.     array('value',array(1,2,3),'值的范圍不正確!',2,'in'), // 當值不為空的時候判斷是否在一個范圍內
  5.     array('repassword','password','確認密碼不正確',0,'confirm'), // 驗證確認密碼是否和密碼一致
  6.     array('password','checkPwd','密碼格式不正確',0,'function'), // 自定義函數驗證密碼格式
  7.  );
當使用系統的create方法創建數據對象的時候會自動進行數據驗證操作,代碼示例:
  1. $User = D("User"); // 實例化User對象
  2.  if (!$User->create()){
  3.     // 如果創建失敗 表示驗證沒有通過 輸出錯誤提示信息
  4.  exit($User->getError());
  5.  }else{
  6.     // 驗證通過 可以進行其他數據操作
  7.  }
通常來說,每個數據表對應的驗證規則是相對固定的,但是有些特殊的情況下面可能會改變驗證規則,我們可以動態的改變驗證規則來滿足不同條件下面的驗證:
  1. $User = D("User"); // 實例化User對象
  2. $validate = array(
  3.     array('verify','require','驗證碼必須!'), // 僅僅需要進行驗證碼的驗證
  4.  );
  5. $User-> setProperty("_validate",$validate);
  6. $result = $User->create();
  7.  if (!$result){
  8.     // 如果創建失敗 表示驗證沒有通過 輸出錯誤提示信息
  9.     exit($User->getError());
  10.  }else{
  11.     // 驗證通過 可以進行其他數據操作
  12.  }
多字段驗證
自動驗證功能中的function和callback規則可以支持多字段。
例子:
  1. protected $_validate = array(
  2.     array('user_id,good_id', 'checkIfOrderToday', '今天已經購買過,請明天再來', 1,'callback', 1),
  3.  );
  4.  protected function checkIfOrderToday($data){
  5.     $map = $data;
  6.     $map['ctime'] = array(array('gt',[開始時間]), array('lt', [結束時間]));
  7.     if($this->where($map)->find())
  8.         return false;
  9.     else
  10.         return true;
  11.  }
批量驗證
新版支持數據的批量驗證功能,只需要在模型類裡面設置patchValidate屬性為true( 默認為false),設置批處理驗證後,getError() 方法返回的錯誤信息是一個數組,返回格式是:
  1. array("字段名1"=>"錯誤提示1","字段名2"=>"錯誤提示2"... )
前端可以根據需要需要自行處理。 手動驗證
3.1版本開始,可以使用validate方法實現動態和批量手動驗證,例如:
  1. $this->validate($validate)->create();
其中$validate變量的規范和_validate屬性的定義規則一致,而且還可以支持函數調用(由於PHP本身的限制,在類的屬性定義中不能調用函數)。
通過這一改進,以前需要支持數據自動驗證,必須定義模型類的情況已經不再出現,你完全可以通過M方法實例化模型類後使用動態設置完成自動驗證操作。

另外還有一個check方法,用於對單個數據的手動驗證,支持部分自動驗證的規則,用法如下:
  1.  check('驗證數據','驗證規則','驗證類型') 
驗證類型支持 in between equal length regex expire ip_allow ip_deny,默認為regex 
結果返回布爾值
  1. $model->check($value,'email'); 
  2. $model->check($value,'1,2,3','in');
可參見:http://doc.thinkphp.cn/manual/auto_validate.html

15. 命名范圍

首先定義_scope屬性:
  1. class NewsModel extends Model {
  2.     protected $_scope = array(
  3.         // 命名范圍normal
  4.         'normal'=>array(
  5.             'where'=>array('status'=>1),
  6.         ),
  7.         // 命名范圍latest
  8.         'latest'=>array(
  9.             'order'=>'create_time DESC',
  10.             'limit'=>10,
  11.         ),
  12.     );
  13.  }
_scope屬性是一個數組,每個數組項表示定義一個命名范圍。 屬性定義完成後,接下來就是使用scope方法進行命名范圍的調用了,每調用一個命名范圍,就相當於執行了命名范圍中定義的相關操作選項。

調用某個命名范圍

最簡單的調用方式就直接調用某個命名范圍,例如:
  1. $Model->scope('normal')->select();
  2. $Model->scope('latest')->select();
生成的SQL語句分別是:
  1. SELECT * FROM think_news WHERE status=1
  2. SELECT * FROM think_news ORDER BY create_time DESC LIMIT 10

調用多個命名范圍

也可以支持同時調用多個命名范圍定義,例如:
  1. $Model->scope('normal')->scope('latest')->select();
或者簡化為:
  1. $Model->scope('normal,latest')->select();
生成的SQL都是:
  1. SELECT * FROM think_news WHERE status=1 ORDER BY create_time DESC LIMIT 10
如果兩個命名范圍的定義存在沖突,則後面調用的命名范圍定義會覆蓋前面的相同屬性的定義。

默認命名范圍

系統支持默認命名范圍功能,如果你定義了一個default命名范圍,例如:
  1.     protected $_scope = array(
  2.         // 默認的命名范圍
  3.         'default'=>array(
  4.             'where'=>array('status'=>1),
  5.             'limit'=>10,
  6.         ),
  7.     );
那麼調用default命名范圍可以直接使用:
  1. $Model->scope()->select();

命名范圍調整

如果你需要在normal命名范圍的基礎上增加額外的調整,可以使用:
  1. $Model->scope('normal',array('limit'=>5))->select();
生成的SQL語句是:
  1. SELECT * FROM think_news WHERE status=1 LIMIT 5
當然,也可以在兩個命名范圍的基礎上進行調整,例如:
  1. $Model->scope('normal,latest',array('limit'=>5))->select();
生成的SQL是:
  1. SELECT * FROM think_news WHERE status=1 ORDER BY create_time DESC LIMIT 5

自定義命名范圍

又或者,干脆不用任何現有的命名范圍,我直接傳入一個命名范圍:
  1. $Model->scope(array('field'=>'id,title','limit'=>5,'where'=>'status=1','order'=>'create_time DESC'))->select();
這樣,生成的SQL變成:
  1. SELECT id,title FROM think_news WHERE status=1 ORDER BY create_time DESC LIMIT 5

與連貫操作混合使用

命名范圍一樣可以和之前的連貫操作混合使用,例如定義了命名范圍_scope屬性:
  1. protected $_scope = array(
  2.     'normal'=>array(
  3.         'where'=>array('status'=>1),
  4.         'field'=>'id,title',
  5.         'limit'=>10,
  6.     ),
  7.  );
然後在使用的時候,可以這樣調用:
  1. $Model->scope('normal')->limit(8)->order('id desc')->select();
這樣,生成的SQL變成:
  1. SELECT id,title FROM think_news WHERE status=1 ORDER BY id desc LIMIT 8
如果定義的命名范圍和連貫操作的屬性有沖突,則後面調用的會覆蓋前面的。
如果是這樣調用:
  1. $Model->limit(8)->scope('normal')->order('id desc')->select();
生成的SQL則是:
  1. SELECT id,title FROM think_news WHERE status=1 ORDER BY id desc LIMIT 10
命名范圍功能的優勢在於可以一次定義多次調用,並且在項目中也能起到分工配合的規范,避免開發人員在寫CURD操作的時候出現問題,項目經理只需要合理的規劃命名范圍即可。

16. 自動完成

在Model類定義 $_auto 屬性,可以完成數據自動處理功能,用來處理默認值、數據過濾以及其他系統寫入字段。$_auto屬性是由多個填充因子組成的數組。
填充因子格式:
  1. array(填充字段,填充內容,[填充條件,附加規則])
示例
  1. protected $_auto = array ( 
  2.     array('status','1'),  // 新增的時候把status字段設置為1
  3.     array('password','md5',1,'function') , // 對password字段在新增的時候使md5函數處理
  4.     array('name','getName',1,'callback'), // 對name字段在新增的時候回調getName方法
  5.     array('create_time','time',2,'function'), // 對create_time字段在更新的時候寫入當前時間戳
  6.  );
使用自動填充可能會覆蓋表單提交項目。其目的是為了防止表單非法提交字段。使用Model類的create方法創建數據對象的時候會自動進行表單數據處理。 和自動驗證一樣,自動完成機制需要使用create方法才能生效。並且,也可以在操作方法中動態的更改自動完成的規則。
  1. $auto = array ( 
  2.     array('password','md5',1,'function') // 對password字段在新增的時候使md5函數處理
  3.  );
  4. $User-> setProperty("_auto",$auto);
  5. $User->create();
動態設置自動完成規則
還可以使用auto方法動態設置自動完成規則,例如:
  1. $this->auto($auto)->create();
其中$auto變量的規范和_auto屬性的定義規則一致,而且還可以支持函數調用(由於PHP本身的限制,在類的屬性定義中不能調用函數)。
通過這一改進,以前需要支持數據自動完成,必須定義模型類的情況已經不再出現,你完全可以通過M方法實例化模型類後使用動態設置完成自動完成操作。

17. 查詢語言

查詢方式

ThinkPHP可以支持直接使用字符串作為查詢條件,但是大多數情況推薦使用索引數組或者對象來作為查詢條件,因為會更加安全。
一、使用字符串作為查詢條件
這是最傳統的方式,但是安全性不高,例如:

  1. $User = M("User"); // 實例化User對象
  2. $User->where('type=1 AND status=1')->select(); 

最後生成的SQL語句是
SELECT * FROM think_user WHERE type=1 AND status=1

二、使用數組作為查詢條件
  1. $User = M("User"); // 實例化User對象
  2. $condition['name'] = 'thinkphp';
  3. $condition['status'] = 1;
  4.  // 把查詢條件傳入查詢方法
  5. $User->where($condition)->select(); 
最後生成的SQL語句是
SELECT * FROM think_user WHERE `name`='thinkphp' AND status=1
如果進行多字段查詢,那麼字段之間的默認邏輯關系是 邏輯與 AND,但是用下面的規則可以更改默認的邏輯判斷,通過使用 _logic 定義查詢邏輯:
  1. $User = M("User"); // 實例化User對象
  2. $condition['name'] = 'thinkphp';
  3. $condition['account'] = 'thinkphp';
  4. $condition['_logic'] = 'OR';
  5.  // 把查詢條件傳入查詢方法
  6. $User->where($condition)->select(); 
最後生成的SQL語句是
SELECT * FROM think_user WHERE `name`='thinkphp' OR `account`='thinkphp' 三、使用對象方式來查詢 (這裡以stdClass內置對象為例)
  1. $User = M("User"); // 實例化User對象
  2.  // 定義查詢條件
  3. $condition = new stdClass(); 
  4. $condition->name = 'thinkphp'; 
  5. $condition->status= 1; 
  6. $User->where($condition)->select(); 
最後生成的SQL語句和上面一樣
SELECT * FROM think_user WHERE `name`='thinkphp' AND status=1
使用對象方式查詢和使用數組查詢的效果是相同的,並且是可以互換的,大多數情況下,我們建議采用數組方式更加高效,後面我們會以數組方式為例來講解具體的查詢語言用法。

表達式查詢

上面的查詢條件僅僅是一個簡單的相等判斷,可以使用查詢表達式支持更多的SQL查詢語法,並且可以用於數組或者對象方式的查詢(下面僅以數組方式為例說明),查詢表達式的使用格式:
$map['字段名'] = array('表達式','查詢條件');
表達式不分大小寫,支持的查詢表達式有下面幾種,分別表示的含義是: 表達式含義 EQ 等於(=) NEQ 不等於(<>) GT 大於(>) EGT 大於等於(>=) LT 小於(<) ELT 小於等於(<=) LIKE 模糊查詢 [NOT] BETWEEN (不在)區間查詢 [NOT] IN (不在)IN 查詢 EXP 表達式查詢,支持SQL語法

快捷查詢

新版增加了快捷查詢方式,可以進一步簡化查詢條件的寫法,例如:
一、實現不同字段相同的查詢條件
  1. $User = M("User"); // 實例化User對象
  2. $map['name|title'] = 'thinkphp';
  3.  // 把查詢條件傳入查詢方法
  4. $User->where($map)->select(); 
查詢條件就變成 name= 'thinkphp' OR title = 'thinkphp'
二、實現不同字段不同的查詢條件
  1. $User = M("User"); // 實例化User對象
  2. $map['status&title'] =array('1','thinkphp','_multi'=>true);
  3.  // 把查詢條件傳入查詢方法
  4. $User->where($map)->select(); 
'_multi'=>true必須加在數組的最後,表示當前是多條件匹配,這樣查詢條件就變成 status= 1 AND title = 'thinkphp' ,查詢字段支持更多的,例如:
$map['status&score&title'] =array('1',array('gt','0'),'thinkphp','_multi'=>true);
查詢條件就變成 status= 1 AND score >0 AND title = 'thinkphp'
注意:快捷查詢方式中“|”和“&”不能同時使用。

區間查詢

ThinkPHP支持對某個字段的區間查詢,例如:
  1. $map['id'] = array(array('gt',1),array('lt',10)) ;
得到的查詢條件是: (`id` > 1) AND (`id` < 10)
  1. $map['id'] = array(array('gt',3),array('lt',10), 'or') ;
得到的查詢條件是: (`id` > 3) OR (`id` < 10)
  1. $map['id']  = array(array('neq',6),array('gt',3),'and'); 
得到的查詢條件是:(`id` != 6) AND (`id` > 3)
最後一個可以是AND、 OR或者 XOR運算符,如果不寫,默認是AND運算。

組合查詢

組合查詢的主體還是采用數組方式查詢,只是加入了一些特殊的查詢支持,包括字符串模式查詢(_string)、復合查詢(_complex)、請求字符串查詢(_query),混合查詢中的特殊查詢每次查詢只能定義一個,由於采用數組的索引方式,索引相同的特殊查詢會被覆蓋。 一、字符串模式查詢(采用_string 作為查詢條件)
數組條件還可以和字符串條件混合使用,例如:
  1. $User = M("User"); // 實例化User對象
  2. $map['id'] = array('neq',1);
  3. $map['name'] = 'ok';
  4. $map['_string'] = 'status=1 AND score>10';
  5. $User->where($map)->select(); 
最後得到的查詢條件就成了:
( `id` != 1 ) AND ( `name` = 'ok' ) AND ( status=1 AND score>10 ) 二、請求字符串查詢方式
請求字符串查詢是一種類似於URL傳參的方式,可以支持簡單的條件相等判斷。
  1. $map['id'] = array('gt','100');
  2. $map['_query'] = 'status=1&score=100&_logic=or';
得到的查詢條件是:`id`>100 AND (`status` = '1' OR `score` = '100') 三、復合查詢
  1. $where['name']  = array('like', '%thinkphp%');
  2. $where['title']  = array('like','%thinkphp%');
  3. $where['_logic'] = 'or';
  4. $map['_complex'] = $where;
  5. $map['id']  = array('gt',1);
查詢條件是 
( id > 1) AND ( ( name like '%thinkphp%') OR ( title like '%thinkphp%') )

統計查詢

方法說明 Count 統計數量,參數是要統計的字段名(可選) Max 獲取最大值,參數是要統計的字段名(必須) Min 獲取最小值,參數是要統計的字段名(必須) Avg 獲取平均值,參數是要統計的字段名(必須) Sum 獲取總分,參數是要統計的字段名(必須) 用法示例:
  1. $User = M("User"); // 實例化User對象
獲取用戶數:
  1. $userCount = $User->count();
或者根據字段統計:
  1. $userCount = $User->count("id");
獲取用戶的最大積分:
  1. $maxScore = $User->max('score');
獲取積分大於0的用戶的最小積分:
  1. $minScore = $User->where('score>0')->min('score');
獲取用戶的平均積分:
  1. $avgScore = $User->avg('score');
統計用戶的總成績:
  1. $sumScore = $User->sum('score');
並且所有的統計查詢均支持連貫操作的使用。

定位查詢

ThinkPHP支持定位查詢,但是要求當前模型必須繼承高級模型類才能使用,可以使用getN方法直接返回查詢結果中的某個位置的記錄。例如: 
 獲取符合條件的第3條記錄:
  1. $User->where('score>0')->order('score desc')->getN(2);
 獲取符合條件的最後第二條記錄:
  1. $User-> where('score>80')->order('score desc')->getN(-2);
 獲取第一條記錄:
  1. $User->where('score>80')->order('score desc')->first();
 獲取最後一條記錄:
  1. $User->where('score>80')->order('score desc')->last();

SQL查詢

1、query方法 query  執行SQL查詢操作 用法 query($sql,$parse=false) 參數 query(必須):要查詢的SQL語句
parse(可選):是否需要解析SQL 返回值

如果數據非法或者查詢錯誤則返回false

否則返回查詢結果數據集(同select方法)

使用示例:
  1. $Model = new Model() // 實例化一個model對象 沒有對應任何數據表
  2. $Model->query("select * from think_user where status=1");
如果你當前采用了分布式數據庫,並且設置了讀寫分離的話,query方法始終是在讀服務器執行,因此query方法對應的都是讀操作,而不管你的SQL語句是什麼。 2、execute方法 execute用於更新和寫入數據的sql操作 用法 execute($sql,$parse=false) 參數 query(必須):要執行的SQL語句
parse(可選):是否需要解析SQL 返回值 如果數據非法或者查詢錯誤則返回false 
否則返回影響的記錄數 使用示例:
  1. $Model = new Model() // 實例化一個model對象 沒有對應任何數據表
  2. $Model->execute("update think_user set name='thinkPHP' where status=1");
如果你當前采用了分布式數據庫,並且設置了讀寫分離的話,execute方法始終是在寫服務器執行,因此execute方法對應的都是寫操作,而不管你的SQL語句是什麼。 3、其他技巧
自動獲取當前表名
通常使用原生SQL需要手動加上當前要查詢的表名,如果你的表名以後會變化的話,那麼就需要修改每個原生SQL查詢的sql語句了,針對這個情況,系統還提供了一個小的技巧來幫助解決這個問題。
例如:
  1. $model = M("User");
  2. $model->query('select * from __TABLE__ where status>1');
我們這裡使用了__TABLE__ 這樣一個字符串,系統在解析的時候會自動替換成當前模型對應的表名,這樣就可以做到即使模型對應的表名有所變化,仍然不用修改原生的sql語句。 支持連貫操作和SQL解析
新版對query和execute兩個原生SQL操作方法增加第二個參數支持, 表示是否需要解析SQL (默認為false 表示直接執行sql ),如果設為true 則會解析SQL中的特殊字符串 (需要配合連貫操作)。
例如,支持 如下寫法:
  1. $model->table("think_user")
  2.       ->where(array("name"=>"thinkphp"))
  3.       ->field("id,name,email")
  4.       ->query('select %FIELD% from %TABLE% %WHERE%',true);
其中query方法中的%FIELD%、%TABLE%和%WHERE%字符串會自動替換為同名的連貫操作方法的解析結果SQL,支持的替換字符串包括: 替換字符串對應連貫操作方法 %FIELD% field %TABLE% table %DISTINCT% distinct %WHERE% where %JOIN% join %GROUP% group %HAVING% having %ORDER% order %LIMIT% limit %UNION% union

動態查詢

借助PHP5語言的特性,ThinkPHP實現了動態查詢,包括下面幾種: 方法名說明舉例 getBy 根據某個字段的值查詢數據 例如,getByName,getByEmail getFieldBy 根據某個字段查詢並返回某個字段的值 例如,getFieldByName top 獲取前多少條記錄(需要高級模型支持) 例如,top8,top12 一、getBy動態查詢
該查詢方式針對數據表的字段進行查詢。例如,User對象擁有id,name,email,address 等屬性,那麼我們就可以使用下面的查詢方法來直接根據某個屬性來查詢符合條件的記錄。
  1. $user = $User->getByName('liu21st');
  2. $user = $User->getByEmail('[email protected]');
  3. $user = $User->getByAddress('中國深圳');
暫時不支持多數據字段的動態查詢方法,請使用find方法和select方法進行查詢。 二、getFieldBy動態查詢
針對某個字段查詢並返回某個字段的值,例如
  1. $user = $User->getFieldByName('liu21st','id');
表示根據用戶的name獲取用戶的id值。 三、top動態查詢
ThinkPHP還提供了另外一種動態查詢方式,就是獲取符合條件的前N條記錄(和定位查詢一樣,也要求當前模型類必須繼承高級模型類後才能使用)。例如,我們需要獲取當前用戶中積分大於0,積分最高的前5位用戶 :
  1. $User-> where('score>80')->order('score desc')->top5();
要獲取積分的前8位可以改成:
  1. $User-> where('score>80')->order('score desc')->top8();

子查詢

新版新增了子查詢支持,有兩種使用方式:
1、使用select方法
當select方法的參數為false的時候,表示不進行查詢只是返回構建SQL,例如:
  1. // 首先構造子查詢SQL 
  2. $subQuery = $model->field('id,name')->table('tablename')->group('field')->where($where)->order('status')->select(false); 
2、使用buildSql方法
  1. $subQuery = $model->field('id,name')->table('tablename')->group('field')->where($where)->order('status')->buildSql(); 
調用buildSql方法後不會進行實際的查詢操作,而只是生成該次查詢的SQL語句(為了避免混淆,會在SQL兩邊加上括號),然後我們直接在後續的查詢中直接調用。
  1. // 利用子查詢進行查詢 
  2. $model->table($subQuery.' a')->where()->order()->select() 
構造的子查詢SQL可用於TP的連貫操作方法,例如table where等。

18. 查詢鎖定

ThinkPHP支持查詢或者更新的鎖定,只需要在查詢或者更新之前使用lock方法即可。
查詢鎖定使用:
  1. $list = $User->lock(true)->where('status=1')->order('create_time')->limit(10)->select();
更新鎖定使用:
  1. $list = $User->lock(true)->where('status=1')->data($data)->save();

19. 字段排除

當使用下面的字段排除方式查詢的時候
  1. $Model->field('create_time,read_count,comment_count',true); 
第二個參數表示field方法采用的是排除機制,因此實際查詢的字段是除create_time,read_count,comment_count之外的其他數據表所有字段,最終要查詢的字段根據實際的數據表字段有所不同。
生成的SQL語句就變成了SELECT id,name,title,status FROM article

20. 事務支持

ThinkPHP提供了單數據庫的事務支持,如果要在應用邏輯中使用事務。事務是針對數據庫本身的,所以可以跨模型操作的 。 例如:
  1. //  在User模型中啟動事務
  2. $User->startTrans();
  3.  // 進行相關的業務邏輯操作
  4. $Info = M("Info"); // 實例化Info對象
  5. $Info->save($User); // 保存用戶信息
  6.  if (操作成功){
  7.     // 提交事務
  8.     $User->commit(); 
  9.  }else{
  10.    // 事務回滾
  11.    $User->rollback(); 
  12.  }
注意:系統提供的事務操作方法必須有數據庫本身的支持,如果你的數據庫或者數據表類型不支持事務,那麼系統的事務操作是無效的。

21. 高級模型

http://doc.thinkphp.cn/manual/adv_model.html

22. 視圖模型

http://doc.thinkphp.cn/manual/view_model.html

23. 關聯模型

http://doc.thinkphp.cn/manual/relation_model.html

24. Mongo模型

http://doc.thinkphp.cn/manual/mongo_model.html

25. 動態模型

你可以從基本模型切換到高級模型或者視圖模型,而當前的數據不會丟失,並可以控制要傳遞的參數和動態賦值。 要切換模型,可以使用:
  1. $User = M("User"); // 實例化User對象 是基礎模型類的實例
  2.  // 動態切換到高級模型類 執行top10查詢操作
  3. $User->switchModel("Adv")->top10();
上面的寫法也可以改成
  1. $User = M("AdvModel:User"); // 實例化User對象 是基礎模型類的實例
  2. $User->top10();
如果要傳遞參數,可以使用:
  1. $User = D("User"); // 實例化User對象 是基礎模型類的實例
  2.  // 動態切換到視圖模型類 並傳入viewFields屬性
  3. $UserView = $User->switchModel("View",array("viewFields"));
如果要動態賦值,可以使用:
  1. $User = M("User"); // 實例化User對象 是基礎模型類的實例
  2.  // 動態切換到關聯模型類 並傳入data屬性
  3. $advUser = $User->switchModel("Relation");
  4.  // 或者在切換模型後再動態賦值給新的模型
  5. $advUser->setProperty("_link",$link);
  6.  // 查找關聯數據
  7. $user = $advUser->relation(true)->find(1);

26. 虛擬模型

有些時候,我們建立模型類但又不需要進行數據庫操作,僅僅是借助模型類來封裝一些業務邏輯,那麼可以借助虛擬模型來完成。虛擬模型不會自動連接數據庫,因此也不會自動檢測數據表和字段信息,有兩種方式可以定義虛擬模型:
第一種:繼承Model類
  1. Class UserModel extends Model {
  2.     Protected $autoCheckFields = false;
  3.  }
設置autoCheckFields屬性為false後,就會關閉字段信息的自動檢測,因為ThinkPHP采用的是惰性數據庫連接,只要你不進行數據庫查詢操作,是不會連接數據庫的。 1 第二種:不繼承Model類
  1. Class UserModel { }
這種方式下面自定義模型類就是一個單純的業務邏輯類,不能再使用模型的CURD操作方法,但是可以實例化其他的模型類進行相關操作,也可以在需要的時候直接實例化Db類進行數據庫操作。 3.1版本開始,模型層(M)支持自定義分層。並且D方法,增加layer參數,具體分層的M類仍然繼承Model類,用法示例:
實例化UserModel類(默認的情況)
文件位於項目的Lib/Model/UserModel.class.php
  1. D('User');
實例化UserLogic類 實現Logic分層
文件位於項目的Lib/Logic/UserLogic.class.php
  1. D('User','Logic');
實例化UserService類,實現Service分層
文件位於項目的Lib/Service/UserService.class.php
  1. D('User','Service');
可以配置DEFAULT_M_LAYER修改默認的模型層名稱(該參數默認值為Model) ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

視圖:

ThinkPHP的視圖有兩個部分組成:View類和模板文件。Action控制器直接和View視圖類打交道,把要輸出的數據通過模板變量賦值的方式傳遞到視圖類,而具體的輸出工作則交由View視圖類來進行,同時視圖類還和模板引擎進行接口,包括完成布局渲染、輸出替換、頁面Trace等功能。

1. 模板定義

為了對模板文件更加有效的管理,ThinkPHP對模板文件進行目錄劃分,默認的模板文件定義規則是:
模板目錄/[分組名/][模板主題/]模塊名/操作名+模板後綴
模板目錄默認是項目下面的Tpl, 當定義分組的情況下,會按照分組名分開子目錄,新版模板主題默認是空(表示不啟用模板主題功能),模板主題功能是為了多模板切換而設計的,如果有多個模板主題的話,可以用DEFAULT_THEME參數設置默認的模板主題名。
在每個模板主題下面,是以項目的模塊名為目錄,然後是每個模塊的具體操作模板文件,例如:
User模塊的add操作 對應的模板文件就應該是:
  1. Tpl/User/add.html 
模板文件的默認後綴的情況是.html,也可以通過TMPL_TEMPLATE_SUFFIX來配置成其他的。
如果項目啟用了模塊分組功能(假設User模塊屬於Home分組),那麼默認對應的模板文件可能變成 :
  1. Tpl/Home/User/add.html 
當然,分組功能也提供了TMPL_FILE_DEPR參數來配置簡化模板的目錄層次。
例如 TMPL_FILE_DEPR如果配置成“_”的話,默認的模板文件就變成了:
  1. Tpl/Home/User_add.html
正是因為系統有這樣一種模板文件自動識別的規則,所以通常的display方法無需帶任何參數即可輸出對應的模板。

2. 模板賦值

要在模板中輸出變量,必須在在Action類中把變量傳遞給模板,視圖類提供了assign方法對模板變量賦值,無論何種變量類型都統一使用assign賦值。
  1. $this->assign('name',$value);
  2.  // 下面的寫法是等效的
  3. $this->name = $value;
系統只會輸出設定的變量,其它變量不會輸出,一定程度上保證了變量的安全性。 如果要同時輸出多個模板變量,可以使用下面的方式:
  1. $array['name']    =    'thinkphp';
  2. $array['email']    =    '[email protected]';
  3. $array['phone']    =    '12335678';
  4. $this->assign($array);
模板變量賦值後,怎麼在模板文件中輸出,需要根據選擇的模板引擎來用不同的方法,如果使用的是內置的模板引擎,請參考後面的模板指南部分。如果你使用的是PHP本身作為模板引擎的話 ,就可以直接在模板文件裡面輸出了,如下:
  1. <?php 
  2.     echo $name.'['.$email.''.$phone.']';
如果要獲得全部的模板變量,可以調用View類的get方法支持獲取全部模板變量的值,例如:
  1. $this->get('name'); // 獲取name模板變量的值
  2. $this->get(); // 獲取所有模板賦值變量的值

3. 模板輸出

模板變量賦值後就需要調用模板文件來輸出相關的變量,模板調用通過display方法來實現。我們在操作方法的最後使用:
  1. $this->display();
就可以輸出模板。

一、調用當前模塊的其他操作模板

格式:display('操作名')
例如,假設當前操作是User模塊下面的read操作,我們需要調用User模塊的edit操作模版,使用:
  1. $this->display('edit'); 
不需要寫模板文件的路徑和後綴。

二、調用其他模塊的操作模板

格式:display('模塊名:操作名')
例如,當前是User模塊,我們需要調用Member模塊的read操作模版 ,使用:
  1. $this->display('Member:read'); 

三、調用其他主題的操作模板

格式:display('主題名:模塊名:操作名')
例如我們需要 調用Xp主題的User模塊的edit操作模版,使用:
  1. $this->display('Xp:User:edit'); 
這種方式需要指定模塊和操作名

四、直接全路徑輸出模板

格式:display('模板文件名')
例如,我們直接輸出當前的Public目錄下面的menu.html模板文件,使用: 
  1. $this->display('./Public/menu.html');
這種方式需要指定模板路徑和後綴,這裡的Public目錄是位於當前項目入口文件位置下面。如果是其他的後綴文件,也支持直接輸出,例如:
  1. $this->display('./Public/menu.tpl');
只要./Public/menu.tpl是一個實際存在的模板文件。如果使用的是相對路徑的話,要注意當前位置是相對於項目的入口文件,而不是模板目錄。

五、直接解析內容

Action類的display方法如果傳入第四個參數,表示不讀取模板文件而是直接解析內容。例如:
  1. $this->assign('foo','ThinkPHP'); 
  2. $this->show('Hello, {$foo}!');
會在頁面輸出: Hello,ThinkPHP! 
直接輸出的內容仍然支持模板布局功能。
show方法也可以支持指定編碼和輸出格式,例如:
  1. $this->show($content, 'utf-8', 'text/xml'); 
事實上,display方法還有其他的參數和用法。
有時候某個模板頁面我們需要輸出指定的編碼,而不是默認的編碼,可以使用:
  1. $this->display('Member:read', 'gbk'); 
或者輸出的模板文件不是text/html格式的,而是XML格式的,可以用:
  1. $this->display('Member:read', 'utf-8', 'text/xml'); 
如果你的網站輸出編碼不是默認的編碼,可以使用:
  1. 'DEFAULT_CHARSET'=> 'gbk' 
如果要輸出XML格式的,可以用:
  1. 'TMPL_CONTENT_TYPE'=> 'text/xml'

4. 模板替換

在進行模板輸出之前,系統還會對渲染的模板結果進行一些模板的特殊字符串替換操作,也就是實現了模板輸出的替換和過濾。模板替換適用於所有的模板引擎,包括原生的PHP模板。這個機制可以使得模板文件的定義更加方便,默認的替換規則有:
../Public: 會被替換成當前項目的公共模板目錄 通常是 /項目目錄/Tpl/當前主題/Public/ 
__TMPL__: 會替換成項目的模板目錄 通常是 /項目目錄/Tpl/當前主題/
(注:為了部署安全考慮,../Public和__TMPL__不再建議使用)
__PUBLIC__:會被替換成當前網站的公共目錄 通常是 /Public/
__ROOT__: 會替換成當前網站的地址(不含域名) 
__APP__: 會替換成當前項目的URL地址 (不含域名)
__GROUP__:會替換成當前分組的URL地址 (不含域名)
__URL__: 會替換成當前模塊的URL地址(不含域名)
__ACTION__:會替換成當前操作的URL地址 (不含域名)
__SELF__: 會替換成當前的頁面URL

注意這些特殊的字符串是嚴格區別大小寫的,並且這些特殊字符串的替換規則是可以更改或者增加的,我們只需要在項目配置文件中配置TMPL_PARSE_STRING就可以完成。如果有相同的數組索引,就會更改系統的默認規則。例如:
  1. 'TMPL_PARSE_STRING'  =>array(
  2.      '__PUBLIC__' => '/Common', // 更改默認的/Public 替換規則
  3.      '__JS__' => '/Public/JS/', // 增加新的JS類庫路徑替換規則
  4.      '__UPLOAD__' => '/Uploads', // 增加新的上傳路徑替換規則
  5.  )
有了模板替換規則後,頁面上所有的__PUBLIC__ 字符串都會被替換,那如果確實需要輸出__PUBLIC__ 字符串到模板呢,我們可以通過增加替換規則的方式,例如:
  1. 'TMPL_PARSE_STRING'  =>array(
  2.      '--PUBLIC--' => '__PUBLIC__', // 采用新規則輸出/Public字符串
  3.  )
這樣增加替換規則後,如果我們要輸出__PUBLIC__ 字符串,只需要在模板中添加--PUBLIC--,其他替換字符串的輸出方式類似。

5. 獲取內容

有些時候我們不想直接輸出模板內容,而是希望對內容再進行一些處理後輸出,就可以使用fetch方法來獲取解析後的模板內容,在Action類裡面使用:
  1. $content = $this->fetch();
fetch的參數用法和Display方法基本一致,也可以使用:
  1. $content = $this->fetch('Member:read'); 
區別就在於display方法直接輸出模板文件渲染後的內容,而fetch方法是返回模板文件渲染後的內容。如何對返回的結果content進行處理,完全由開發人員自行決定了。這是模板替換的另外一種高級方式,比較靈活,而且不需要通過配置的方式。
注意,fetch方法仍然會執行上面的模板替換操作。

6. 模板引擎

系統支持原生的PHP模板,而且本身內置了一個基於XML的高效的編譯型模板引擎,系統默認使用的模板引擎是內置模板引擎,關於這個模板引擎的標簽詳細使用可以參考模板指南部分。
內置的模板引擎也可以直接支持在模板文件中采用PHP原生代碼和模板標簽的混合使用,如果需要完全使用PHP本身作為模板引擎,可以配置:
  1. 'TMPL_ENGINE_TYPE' =>'PHP'
可以達到最佳的效率。
如果你使用了其他的模板引擎,只需要設置TMPL_ENGINE_TYPE參數為相關的模板引擎名稱即可。 ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

模板引擎:

ThinkPHP內置了一個基於XML的性能卓越的模板引擎 ThinkTemplate,這是一個專門為ThinkPHP服務的內置模板引擎。ThinkTemplate是一個使用了XML標簽庫技術的編譯型模板引擎,支持兩種類型的模板標簽,使用了動態編譯和緩存技術,而且支持自定義標簽庫。
每個模板文件在執行過程中都會生成一個編譯後的緩存文件,其實就是一個可以運行的PHP文件。模板緩存默認位於項目的Runtime/Cache目錄下面,以模板文件的md5編碼作為緩存文件名保存的。如果在模板標簽的使用過程中發現問題,可以嘗試通過查看模板緩存文件找到問題所在。
內置的模板引擎支持普通標簽和XML標簽方式兩種標簽定義,分別用於不同的目的: 普通標簽 主要用於輸出變量和做一些基本的操作 XML標簽 主要完成一些邏輯判斷、控制和循環輸出,並且可擴展

1. 變量輸出

如果我們在Action中賦值了一個name模板變量:
  1. $name = 'ThinkPHP';
  2. $this->assign('name',$name);
使用內置的模板引擎輸出變量,只需要在模版文件使用:
  1. {$name}
模板編譯後的結果就是
  1. <?php echo($name);?>
注意模板標簽的{和$之間不能有任何的空格,否則標簽無效。 普通標簽默認開始標記是 {,結束標記是 }。也可以通過設置TMPL_L_DELIM和TMPL_R_DELIM進行更改。例如,我們在項目配置文件中定義:
  1. 'TMPL_L_DELIM'=>'<{',
  2.  'TMPL_R_DELIM'=>'}>',
那麼,上面的變量輸出標簽就應該改成:
  1. <{$name}>
如果TMPL_VAR_IDENTIFY設置為array,那麼
{$user.name}和{$user['name']}等效,也就是輸出數組變量。
如果TMPL_VAR_IDENTIFY設置為obj,那麼
{$user.name}和{$user:name}等效,也就是輸出對象的屬性。
如果TMPL_VAR_IDENTIFY留空的話,系統會自動判斷要輸出的變量是數組還是對象,這種方式會一定程度上影響效率,而且只支持二維數組和兩級對象屬性。 如果TMPL_VAR_IDENTIFY留空的話,系統會自動判斷要輸出的變量是數組還是對象,這種方式會一定程度上影響效率,而且只支持二維數組和兩級對象屬性。
如果是多維數組或者多層對象屬性的輸出,可以使用下面的定義方式:
  1. {$user.sub.name}// 使用點語法輸出
或者使用
  1. {$user['sub']['name']}// 輸出三維數組的值
  2.  {$user:sub:name}// 輸出對象的多級屬性

2. 系統變量

除了常規變量的輸出外,模板引擎還支持系統變量和系統常量、以及系統特殊變量的輸出。它們的輸出不需要事先賦值給某個模板變量。系統變量的輸出必須以$Think.打頭,並且仍然可以支持使用函數。常用的系統變量輸出包括下面:

用法含義例子 $Think.server 獲取$_SERVER {$Think.server.php_self} $Think.get 獲取$_GET {$Think.get.id} $Think.post 獲取$_POST {$Think.post.name} $Think.request 獲取$_REQUEST {$Think.request.user_id} $Think.cookie 獲取$_COOKIE {$Think.cookie.username} $Think.session 獲取$_SESSION {$Think.session.user_id} $Think.config 獲取系統配置參數 {$Think.config.app_status} $Think.lang 獲取系統語言變量 {$Think.lang.user_type} $Think.const 獲取系統常量 {$Think.const.app_name}或{$Think.APP_NAME} $Think.env 獲取環境變量 {$Think.env.HOSTNAME} $Think.version 獲取框架版本號 {$Think.version} $Think.now 獲取當前時間 {$Think.now} $Think.template 獲取當前模板 {$Think.template} $Think.ldelim 獲取模板左界定符 {$Think.ldelim} $Think.rdelim 獲取模板右界定符 {$Think.rdelim} 1、系統變量:包括server、session、post、get、request、cookie 2、系統常量:使用$Think.const 輸出 3、特殊變量:由ThinkPHP系統內部定義的常量 4、配置參數:輸出項目的配置參數值
  1. {$Think.config.db_charset}
輸出的值和C('db_charset') 的返回結果是一樣的。
也可以輸出二維的配置參數,例如:
  1. {$Think.config.user.user_name}
5、語言變量:輸出項目的當前語言定義值
  1. {$Think.lang.page_error}
輸出的值和L('page_error')的返回結果是一樣的。

3. 使用函數

用於模板標簽的函數可以是PHP內置函數或者是用戶自定義函數,和smarty不同,用於模板的函數不需要特別的定義。 模板變量的函數調用格式為:
  1. {$varname|function1|function2=arg1,arg2,### }
說明: { 和 $ 符號之間不能有空格 ,後面參數的空格就沒有問題,###表示模板變量本身的參數位置 ,支持多個函數,函數之間支持空格 ,支持函數屏蔽功能,在配置文件中可以配置禁止使用的函數列表 ,支持變量解析緩存功能,重復變量字串不多次解析。
  1. {$webTitle|md5|strtoupper|substr=0,3}
編譯後的PHP代碼就是:
  1. <?php echo (substr(strtoupper(md5($webTitle)),0,3)); ?>
注意函數的定義和使用順序的對應關系,通常來說函數的第一個參數就是前面的變量或者前一個函數調用的返回結果,如果你的變量並不是函數的第一個參數,需要使用定位符號,例如:
  1. {$create_time|date="y-m-d",###}
編譯後的PHP是:
  1. <?php echo (date("y-m-d",$create_time)); ?>
函數的使用沒有個數限制,但是可以允許配置TMPL_DENY_FUNC_LIST定義禁用函數列表,系統默認禁用了exit和echo函數,以防止破壞模板輸出,我們也可以增加額外的定義,例如:
  1. TMPL_DENY_FUNC_LIST=>"echo,exit,halt"
多個函數之間使用半角逗號分隔即可。 並且還提供了在模板文件中直接調用函數的快捷方法,這種方式更加直接明了,而且無需通過模板變量,包括兩種方式:
1、執行函數並輸出返回值
格式:{:function(…)} 
例如,輸出U函數的返回值:
  1. {:U('User/insert')}
編譯後的PHP代碼是
  1. <?php echo U('User/insert');?>
2、執行函數但不輸出
格式:{~function(…)} 
例如,調用say_hello函數:
  1. {~say_hello('ThinkPHP')}
編譯後的PHP代碼是:
  1. <?php say_hello('ThinkPHP');?>

4. 默認值輸出

如果輸出的模板變量沒有值,但是我們需要在顯示的時候賦予一個默認值的話,可以使用default語法,格式:
{$變量|default="默認值"}
這裡的default不是函數,而是系統的一個語法規則,例如:
  1. {$user.nickname|default="這家伙很懶,什麼也沒留下"}
對系統變量的輸出也可以支持默認值,例如:
  1. {$Think.post.name|default="名稱為空"}
默認值支持Html語法。

5. 使用運算符

內置模板引擎包含了運算符的支持,包括對“+”“ –” “*” “/”和“%”的支持。 在使用運算符的時候,不再支持點語法和常規的函數用法,例如:
  1. {$user.score+10} 是錯誤的
  2.  {$user['score']+10} 是正確的
  3.  {$user['score']*$user['level']} 正確的
  4.  {$user['score']|myFun*10} 錯誤的
  5.  {$user['score']+myFun($user['level'])} 正確的

6. 內置標簽

系統內置標簽庫的所有標簽無需引入標簽庫即可直接使用。XML標簽有兩種,包括閉合標簽和開放標簽,一個標簽在定義的時候就已經決定了是否是閉合標簽還是開放標簽,不可混合使用,例如:
閉合標簽:
  1. <include file="read" />
開放標簽:
  1. <gt name="name" value="5">value</gt>
內置支持的標簽和屬性列表如下: 標簽名作用包含屬性 include 包含外部模板文件(閉合) file import 導入資源文件(閉合 包括js css load別名) file,href,type,value,basepath volist 循環數組數據輸出 name,id,offset,length,key,mod foreach 數組或對象遍歷輸出 name,item,key for For循環數據輸出 name,from,to,before,step switch 分支判斷輸出 name case 分支判斷輸出(必須和switch配套使用) value,break default 默認情況輸出(閉合 必須和switch配套使用) 無 compare 比較輸出(包括eq neq lt gt egt elt heq nheq等別名) name,value,type range 范圍判斷輸出(包括in notin between notbetween別名) name,value,type present 判斷是否賦值 name notpresent 判斷是否尚未賦值 name empty 判斷數據是否為空 name notempty 判斷數據是否不為空 name defined 判斷常量是否定義 name notdefined 判斷常量是否未定義 name define 常量定義(閉合) name,value assign 變量賦值(閉合) name,value if 條件判斷輸出 condition elseif 條件判斷輸出(閉合  必須和if標簽配套使用) condition else 條件不成立輸出(閉合 可用於其他標簽) 無 php 使用php代碼 無

7. 包含文件

可以使用Include標簽來包含外部的模板文件,使用方法如下: include標簽(包含外部模板文件) 閉合 閉合標簽 屬性 file(必須):要包含的模板文件,支持變量 1、 使用完整文件名包含
格式:<include file="完整模板文件名" />
  1. <include file="./Tpl/default/Public/header.html" />
2、包含當前模塊的其他操作模板文件
格式:<include file="操作名" />
  1. <include file="read" />
操作模板無需帶後綴。 4、包含其他模板主題的模塊操作模板
格式:<include file="主題名:模塊名:操作名" />
  1. <include file="blue:User:read" />
5、 用變量控制要導入的模版
格式:<include file="$變量名" />
  1. <include file="$tplName" />
給$tplName賦不同的值就可以包含不同的模板文件,變量的值的用法和上面的用法相同。 無論你使用什麼方式包含外部模板,Include標簽支持在包含文件的同時傳入參數,注意:由於模板解析的特點,從入口模板開始解析,如果外部模板有所更改,模板引擎並不會重新編譯模板,除非在調試模式下或者緩存已經過期。如果部署模式下修改了包含的外部模板文件後,需要把模塊的緩存目錄清空,否則無法生效。 3.1版本開始,include標簽支持導入多個模板,用逗號分割即可,例如:
  1. <include file='file1,file2' />

8. 導入文件

import標簽(包含外部模板文件) 閉合 閉合標簽 屬性 file(必須):要包含的模板文件,支持變量
  1. <import type='js' file="Js.Util.Array" />
Type屬性默認是js。還可以支持多個文件批量導入,例如:
  1. <import file="Js.Util.Array,Js.Util.Date" />
導入外部CSS文件必須指定type屬性的值,例如:
  1. <import type='css' file="Css.common" />
上面的方式默認的import的起始路徑是網站的Public目錄,如果需要指定其他的目錄,可以使用basepath屬性,例如:
  1. <import file="Js.Util.Array"  basepath="./Common" />
load標簽(采用url方式引入資源文件) 閉合 閉合標簽 屬性 href(必須):要引入的資源文件url地址,支持變量
  1. <load href="/Book/Tpl/Home/Public/Js/Common.js" />
  2.  <load href="/Book/Tpl/Home/Public/Css/common.css" />
系統還提供了兩個標簽別名js和css 用法和load一致,例如:
  1. <js href="/Public/Js/Common.js" />
  2.  <css href="/Book/Tpl/Home/Public/Css/common.css" />

9. Volist標簽

Volist標簽主要用於在模板中循環輸出數據集或者多維數組。 volist標簽(循環輸出數據) 閉合 非閉合標簽 屬性

name(必須):要輸出的數據模板變量

id(必須):循環變量

offset(可選):要輸出數據的offset

length(可選):輸出數據的長度

key(可選):循環的key變量,默認值為i

mod(可選):對key值取模,默認為2

empty(可選):如果數據為空顯示的字符串

通常模型的select方法返回的結果是一個二維數組,可以直接使用volist標簽進行輸出。
在Action中首先對模版賦值:
  1. $User = M('User');
  2. $list = $User->select();
  3. $this->assign('list',$list);
在模版定義如下,循環輸出用戶的編號和姓名:
  1. <volist name="list" id="vo">
  2. {$vo.id}
  3. {$vo.name}
  4.  </volist>
輸出循環變量
  1. <volist name="list" id="vo" key="k" >
  2. {$k}.{$vo.name}
  3.  </volist>
如果沒有指定key屬性的話,默認使用循環變量i,例如:
  1. <volist name="list" id="vo"  >
  2. {$i}.{$vo.name}
  3.  </volist>
如果要輸出數組的索引,可以直接使用key變量,和循環變量不同的是,這個key是由數據本身決定,而不是循環控制的,例如:
  1. <volist name="list" id="vo"  >
  2. {$key}.{$vo.name}
  3.  </volist>
從2.1版開始允許在模板中直接使用函數設定數據集,而不需要在控制器中給模板變量賦值傳入數據集變量,如:
  1. <volist name=":fun('arg')" id="vo">{$vo.name}</volist>

10. Foreach標簽

foreach標簽也是用於循環輸出 foreach標簽(循環輸出數據) 閉合 非閉合標簽 屬性 name(必須):要輸出的數據模板變量
item(必須):循環單元變量
key(可選):循環的key變量,默認值為key
  1. <foreach name="list" item="vo">
  2.     {$vo.id}
  3.     {$vo.name}
  4.  </foreach>
Foreach標簽相對比volist標簽簡潔,沒有volist標簽那麼多的功能。優勢是可以對對象進行遍歷輸出,而volist標簽通常是用於輸出數組。

11. For標簽

For標簽用於實現for循環,格式為: for標簽(循環輸出數據) 閉合 非閉合標簽 屬性 start(必須):循環變量開始值
end(必須):循環變量結束值
name(可選):循環變量名,默認值為i
step(可選):步進值,默認值為1
comparison(可選):判斷條件,默認為lt
  1. <for start="開始值" end="結束值" comparison="" step="步進值" name="循環變量名" >
  2.  </for>
  1. <for start="1" end="100">
  2. {$i}
  3.  </for>
解析後的代碼是
  1. for ($i=1;$i<100;$i+=1){
  2.     echo $i;
  3.  }

12. Switch標簽

  1. <switch name="變量" >
  2.  <case value="值1" break="0或1">輸出內容1</case>
  3.  <case value="值2">輸出內容2</case>
  4.  <default />默認情況
  5.  </switch>
其中name屬性可以使用函數以及系統變量,例如:
  1. <switch name="Think.get.userId|abs">
  2.     <case value="1">admin</case>
  3.     <default />default
  4.  </switch>
對於case的value屬性可以支持多個條件的判斷,使用”|”進行分割,例如:
  1. <switch name="Think.get.type">
  2.     <case value="gif|png|jpg">圖像格式</case>
  3.     <default />其他格式
  4.  </switch>
Case標簽還有一個break屬性,表示是否需要break,默認是會自動添加break,如果不要break,可以使用:
  1. <switch name="Think.get.userId|abs">
  2.     <case value="1" break="0">admin</case>
  3.     <case value="2">admin</case>
  4.     <default />default
  5.  </switch>
也可以對case的value屬性使用變量,例如:
  1. <switch name="User.userId">
  2.     <case value="$adminId">admin</case>
  3.     <case value="$memberId">member</case>
  4.     <default />default
  5.  </switch>
使用變量方式的情況下,不再支持多個條件的同時判斷。

13. 比較標簽

  1. <比較標簽 name="變量" value="值">內容</比較標簽>
系統支持的比較標簽以及所表示的含義分別是: eq或者 equal 等於 neq 或者notequal 不等於 gt 大於 egt 大於等於 lt 小於 elt 小於等於 heq 恆等於 nheq 不恆等於 例如,要求name變量的值等於value就輸出,可以使用:
  1. <eq name="name" value="value">value</eq>
當name變量的值不小於5就輸出
  1. <egt name="name" value="5">value</egt>
比較標簽中的變量可以支持對象的屬性或者數組,甚至可以是系統變量:
當vo對象的屬性(或者數組,或者自動判斷)等於5就輸出
  1. <eq name="vo.name" value="5">{$vo.name}</eq>
而且還可以支持對變量使用函數 
當vo對象的屬性值的字符串長度等於5就輸出
  1. <eq name="vo:name|strlen" value="5">{$vo.name}</eq>
變量名可以支持系統變量的方式,例如:
  1. <eq name="Think.get.name" value="value">相等<else/>不相等</eq>

14. 三元運算

模板可以支持三元運算符,例如:
  1. {$status?'正常':'錯誤'}
  2.  {$info['status']?$info['msg']:$info['error']}
注意:三元運算符中暫時不支持點語法。

15. 范圍判斷標簽

Range標簽用於判斷某個變量是否在某個范圍之內: 范圍判斷標簽(包括innotinbetween notbetween) 閉合 非閉合標簽 屬性 name(必須):變量名
value(必須):要比較的范圍值,支持變量 可以使用in標簽來判斷模板變量是否在某個范圍內,例如:
  1. <in name="id"value="1,2,3">輸出內容1</in>
如果判斷不再某個范圍內,可以使用:
  1. <notin name="id"value="1,2,3">輸出內容2</notin>
可以把上面兩個標簽合並成為:
  1. <in name="id"value="1,2,3">輸出內容1<else/>輸出內容2</in>
可以使用between標簽來判斷變量是否在某個區間范圍內,可以使用:
  1. <between name="id"value="1,10">輸出內容1</between>
可以使用notbetween標簽來判斷變量不在某個范圍內:
  1. <notbetween name="id"value="1,10">輸出內容1</notbetween>
當使用between標簽的時候,value只需要一個區間范圍,也就是只支持兩個值,後面的值無效。 所有的范圍判斷標簽的value屬性都可以使用變量,例如:
  1. <in name="id"value="$var">輸出內容1</in>
變量的值可以是字符串或者數組,都可以完成范圍判斷。
也可以直接使用range標簽,替換in和notin的用法:
  1. <range name="id"value="1,2,3"type="in">輸出內容1</range>
其中type屬性的值可以用in或者notin。

16. Present標簽和Empty標簽

可以使用present標簽來判斷模板變量是否已經賦值, present標簽和notpresent標簽 閉合 非閉合標簽 屬性 name(必須):變量名 配合 可以結合else標簽一起使用
  1. <present name="name">name已經賦值</present>
如果判斷沒有賦值,可以使用:
  1. <notpresent name="name">name還沒有賦值</notpresent>
可以把上面兩個標簽合並成為:
  1. <present name="name">name已經賦值<else /> name還沒有賦值</present>
可以使用empty標簽判斷模板變量是否為空, empty標簽和notempty標簽 閉合 非閉合標簽 屬性 name(必須):變量名 配合 可以結合else標簽一起使用
  1. <empty name="name">name為空值</empty>
如果判斷沒有賦值,可以使用:
  1. <notempty name="name">name不為空</notempty>
可以把上面兩個標簽合並成為:
  1. <empty name="name">name為空<else /> name不為空</empty>

17. Defined標簽和Define標簽

可以使用defined標簽判斷常量是否已經有定義: defined標簽和notdefined標簽 閉合 非閉合標簽 屬性 name(必須):變量名
  1. <defined name="NAME">NAME常量已經定義</defined>
如果判斷沒有被定義,可以使用:
  1. <notdefined name="NAME">NAME常量未定義</notdefined>
可以把上面兩個標簽合並成為:
  1. <defined name="NAME">NAME常量已經定義<else /> NAME常量未定義</defined>
可以使用define標簽進行常量定義: defined標簽和notdefined標簽 閉合 閉合標簽 屬性 name(必須):常量名
value(必須):常量值,支持變量 配合 可以結合else標簽一起使用
  1. <define name="MY_DEFINE_NAME"value="3"/>
在運行模板的時候 定義了一個MY_DEFINE_NAME的常量。

18. Assign標簽

可以使用assign標簽進行賦值: assign標簽(在模板中給變量賦值) 閉合 閉合標簽 屬性 name(必須):模板變量名
value(必須):變量值,支持變量
  1. <assign name="var" value="123" />
在運行模板的時候 賦值了一個var的變量,值是123。

19. IF標簽

用法示例:
  1. <if condition="($name eq 1) OR ($name gt 100) "> value1
  2.  <elseif condition="$name eq 2"/>value2
  3.  <else /> value3
  4.  </if>
除此之外,我們可以在condition屬性裡面使用php代碼,例如:
  1. <if condition="strtoupper($user['name']) neq 'THINKPHP'">ThinkPHP
  2.  <else /> other Framework
  3.  </if>
condition屬性可以支持點語法和對象語法,例如:
自動判斷user變量是數組還是對象
  1. <if condition="$user.name neq 'ThinkPHP'">ThinkPHP
  2.  <else /> other Framework
  3.  </if>
或者知道user變量是對象
  1. <if condition="$user:name neq 'ThinkPHP'">ThinkPHP
  2.  <else /> other Framework
  3.  </if>
由於if標簽的condition屬性裡面基本上使用的是php語法,盡可能使用判斷標簽和Switch標簽會更加簡潔,原則上來說,能夠用switch和比較標簽解決的盡量不用if標簽完成。因為switch和比較標簽可以使用變量調節器和系統變量。如果某些特殊的要求下面,IF標簽仍然無法滿足要求的話,可以使用原生php代碼或者PHP標簽來直接書寫代碼。

20. 標簽嵌套

模板引擎支持標簽的多層嵌套功能,可以對標簽庫的標簽指定可以嵌套。
系統內置的標簽中,volist、switch、if、elseif、else、foreach、compare(包括所有的比較標簽)、(not)present、(not)empty、(not)defined等標簽都可以嵌套使用。例如:
  1. <volist name="list" id="vo">
  2.     <volist name="vo['sub']" id="sub">
  3.         {$sub.name}
  4.     </volist>
  5.  </volist>
上面的標簽可以用於輸出雙重循環。默認的嵌套層次是3級,所以嵌套層次不能超過3層,如果需要更多的層次可以指定TAG_NESTED_LEVEL配置參數,例如:
  1. 'TAG_NESTED_LEVEL' =>5
可以改變循環嵌套級別為5級。

21. 使用PHP代碼

  1. <php>echo 'Hello,world!';</php>
注意:php標簽或者php代碼裡面就不能再使用標簽(包括普通標簽和XML標簽)了,因此下面的幾種方式都是無效的:
  1. <php><eq name='name'value='value'>value</eq></php>
簡而言之,在PHP標簽裡面不能再使用PHP本身不支持的代碼。
如果設置了TMPL_DENY_PHP參數為true,就不能在模板中使用原生的PHP代碼,但是仍然支持PHP標簽輸出。

22. 模板布局

第一種方式是 以布局模板為入口的方式
該方式需要配置開啟LAYOUT_ON 參數(默認不開啟),並且設置布局入口文件名LAYOUT_NAME(默認為layout)。
開啟LAYOUT_ON後,我們的模板渲染流程就有所變化,例如:
  1. Class UserAction extends Action {
  2.     Public function add() {
  3.     $this->display('add');
  4.     }
  5.  }
在不開啟LAYOUT_ON布局模板之前,會直接渲染Tpl/User/add.html 模板文件,開啟之後,首先會渲染Tpl/layout.html 模板,布局模板的寫法和其他模板的寫法類似,本身也可以支持所有的模板標簽以及包含文件,區別在於有一個特定的輸出替換變量{__CONTENT__},例如,下面是一個典型的layout.html模板的寫法:
  1. {__CONTENT__}
讀取layout模板之後,會再解析User/add.html 模板文件,並把解析後的內容替換到layout布局模板文件的{__CONTENT__} 特定字符串。
采用這種布局方式的情況下,一旦User/add.html 模板文件或者layout.html布局模板文件發生修改,都會導致模板重新編譯。
如果項目需要使用不同的布局模板,可以動態的配置LAYOUT_NAME參數實現。
如果某些頁面不需要使用布局模板功能,可以在模板文件開頭加上 {__NOLAYOUT__} 字符串。
如果上面的User/add.html 模板文件裡面包含有{__NOLAYOUT__},則即使當前開啟布局模板,也不會進行布局模板解析。 第二種方式是以當前輸出模板為入口的方式
以前面的輸出模板為例,這種方式的入口還是在User/add.html 模板,但是我們可以修改下add模板文件的內容,在頭部增加下面的布局標簽:
  1. <layout name="layout" />
表示當前模板文件需要使用layout.html 布局模板文件,而布局模板文件的寫法和上面第一種方式是一樣的。當渲染User/add.html 模板文件的時候,如果讀取到layout標簽,則會把當前模板的解析內容替換到layout布局模板的{__CONTENT__} 特定字符串。
如果需要使用其他的布局模板,可以改變layout的name屬性,例如:
  1. <layout name="new_layout" />
由於所有include標簽引入的文件都支持layout標簽,所以,我們可以借助layout標簽和include標簽相結合的方式實現布局模板的嵌套。例如,上面的例子
  1. <include file="Public:header" />
  2.  <div id="main" class="main" >
  3. {__CONTENT__}
  4.  </div>
  5.  <include file="Public:bottom" /> 
在引入的header和footer模板文件中也可以添加layout標簽,例如header模板文件的開頭添加如下標簽:
  1. <layout name="menu" />
這樣就實現了在頭部模板中引用了menu布局模板。
也可以采用兩種布局方式的結合,可以實現更加復雜的模板布局以及嵌套功能。

23. 模板繼承

模板繼承的優勢其實是設計基礎模板中的區塊(block)和子模板中替換這些區塊。每個區塊由<block></block>標簽組成,並且不支持block標簽的嵌套。 下面就是基礎模板中的一個典型的區塊設計(用於設計網站標題):
  1. <block name="title"><title>網站標題</title></block>
block標簽必須指定name屬性來標識當前區塊的名稱,這個標識在當前模板中應該是唯一的,block標簽中可以包含任何模板內容,包括其他標簽和變量,例如:
  1. <block name="title"><title>{$web_title}</title></block>
你甚至還可以在區塊中加載外部文件:
  1. <block name="include"><include file="Public:header" /></block>
在子模板中,可以對基礎模板中的區塊進行重載定義,如果沒有重新定義的話,則表示沿用基礎模板中的區塊定義,如果定義了一個空的區塊,則表示刪除基礎模板中的該區塊內容。

24. 原樣輸出

literal標簽(保持原樣輸出) 閉合 非閉合標簽 屬性 無 可以使用literal標簽來防止模板標簽被解析,例如:
  1. <literal>
  2.     <if condition="$name eq 1 "> value1
  3.     <elseif condition="$name eq 2"/>value2
  4.         <else /> value3
  5.     </if>
  6.  </literal>
上面的if標簽被literal標簽包含,因此if標簽裡面的內容並不會被模板引擎解析,而是保持原樣輸出。 Literal標簽還可以用於頁面的JS代碼外層,確保JS代碼中的某些用法和模板引擎不產生混淆。
總之,所有可能和內置模板引擎的解析規則沖突的地方都可以使用literal標簽處理。

25. 模板注釋

模板支持注釋功能,該注釋文字在最終頁面不會顯示,僅供模板制作人員參考和識別。
  1. {// 這是模板注釋內容 }
  2.  {/* 這是模板
  3. 注釋內容*/ }
模板注釋支持多行,模板注釋在生成編譯緩存文件後會自動刪除,這一點和Html的注釋不同。

26. 引入標簽庫

格式:<tagLib name="標簽庫1[,標簽庫2,…]"/>
可以同時導入多個標簽庫,用逗號分隔,例如:
  1. <tagLib name="html"/>
表示在當前模板文件需要引入html標簽庫。要引入標簽庫必須確保有Html標簽庫的定義文件和解析類庫(如何擴展這種方式請參考前面的標簽庫擴展部分)。 引入後,html標簽庫的所有標簽在當前模板頁面中都可以使用了。外部導入的標簽庫必須使用標簽庫前綴的xml標簽,避免兩個不同的標簽庫中存在同名的標簽定義,例如(假設Html標簽庫中已經有定義select和link標簽):
  1. <html:select options='name' selected='value' />
  2.  <html:link href='/path/to/common.js' />
標簽庫使用的時候忽略大小寫,因此下面的方式一樣有效:
  1. <HTML:LINK HREF='/path/to/common.js' />
如果你的每個模板頁面都需要加載Html標簽庫的話,也可以通過配置直接預先加載Html標簽庫。
  1.  'TAGLIB_PRE_LOAD' => 'html' ,
如果有多個標簽庫需要預先加載的話,用逗號分隔。定義之後,每個模板頁面都可以直接使用:
  1. <html:select options='name' selected='value' />
而不需手動引入Html標簽庫。
假設你確信Html標簽庫無論在現在還是將來都不會和系統內置的標簽庫存在相同的標簽,那麼可以配置TAGLIB_BUILD_IN的值把Html標簽庫作為內置標簽庫引入,例如:
  1. 'TAGLIB_BUILD_IN' => 'cx,html' ,
這樣,也無需在模板文件頁面引入Html標簽庫了,並且可以不帶前綴直接使用Html標簽庫的標簽:
  1. <select options='name' selected='value' />
注意,cx標簽庫是系統內置標簽庫,不能刪除定義。

27. 修改定界符

要更改普遍模板的起始標簽和結束標簽,請使用下面的配置參數:
  1. TMPL_L_DELIM  //模板引擎普通標簽開始標記 
  2. TMPL_R_DELIM    //模板引擎普通標簽結束標記
例如在項目配置文件中增加下面的配置:
  1. 'TMPL_L_DELIM'=>'',
普通模板標簽主要用於模板變量輸出和模板注釋。如果要使用其它功能,請使用XML模板標簽。XML模板標簽可以用於模板變量輸出、文件包含、條件控制、循環輸出等功能,而且完全可以自己擴展功能。如果你覺得XML標簽無法在正在使用的編輯器裡面無法編輯,還可以更改XML標簽庫的起始和結束標簽,請修改下面的配置參數:
  1. TAGLIB_BEGIN    //標簽庫標簽開始標簽 
  2. TAGLIB_END    //標簽庫標簽結束標記
例如在項目配置文件中增加下面的配置:
  1. 'TAGLIB_BEGIN'=>'[',
  2.  'TAGLIB_END'=>']',
注意:XML標簽和普通標簽的定界符不能沖突,否則會導致解析錯誤。如果你定制了普通表情的定界符,而且默認跳轉頁面用的是系統默認的話,記得修改下默認跳轉模板中的變量定界符。

28. 避免JS混淆

如果使用內置的模板引擎,而且采用默認的標簽設置的話,在某些情況下,如果不注意,{$('name').value} 這樣的JS代碼很容易被內置模板引擎誤解析。
有三個方法可以解決類似的混淆問題:
1、{$('name').value}改成{ $('name').value}
因為內置模板引擎的解析規則是"{"後面緊跟"$"符號才會解析變量 因此只要在"{" 和"$"之間添加空格就不會被誤解析了
2、使用內置的literal標簽包含JS代碼
<literal>JS代碼</literal> 包含在literal標簽中的代碼將會直接輸出,不進行任何解析
3、定制模板引擎標簽的定界符
例如:'TMPL_L_DELIM'=>'<{','TMPL_R_DELIM'=>'}>'這樣就和JS代碼區別開來了。
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

日志:

日志的處理工作是由系統自動進行的,在開啟日志記錄的情況下,會記錄下允許的日志級別的所有日志信息。其中,為了性能考慮,SQL日志級別必須在調試模式開啟下有效,否則就不會記錄。
系統的日志記錄由核心的Log類完成,提供了多種方式記錄了不同的級別的日志信息。

1. 日志級別

EMERG 嚴重錯誤,導致系統崩潰無法使用 ALERT 警戒性錯誤, 必須被立即修改的錯誤 CRIT 臨界值錯誤, 超過臨界值的錯誤 ERR 一般性錯誤 WARN 警告性錯誤, 需要發出警告的錯誤 NOTICE 通知,程序可以運行但是還不夠完美的錯誤 INFO 信息,程序輸出信息 DEBUG 調試,用於調試信息 SQL SQL語句,該級別只在調試模式開啟時有效 要開啟日志記錄,必須在配置中開啟LOG_RECORD參數,以及可以在項目配置文件中配置需要記錄的日志級別,例如:
  1. 'LOG_RECORD' => true, // 開啟日志記錄
  2.  'LOG_LEVEL'  =>'EMERG,ALERT,CRIT,ERR', // 只記錄EMERG ALERT CRIT ERR 錯誤

2. 記錄方式

記錄方式說明常量標識 SYSTEM 日志發送到PHP的系統日志記錄 0 MAIL 日志通過郵件方式發送 1 FILE 日志通過文件方式記錄(默認方式) 3 SAPI 日志通過SAPI方式記錄 4 日志的記錄格式:記錄時間 訪問URL | 日志級別:日志信息
其中的時間顯示可以動態配置,默認是采用 [ c ],例如我們可以改成:
  1. Log::$format = '[ Y-m-d H:i:s ]';
其格式定義和date函數的用法一致,默認情況下具體的日志信息類似於下面的內容:
  1. [2012-01-15T18:09:22+08:00] /Index/index|NOTIC: [8] Undefined variable: verify PublicAction.class.php 第 162 行.
  2.  [2012-01-15T18:09:22+08:00] /Index/index | SQL:  RunTime:0.214238s SQL = SHOW COLUMNS FROM think_user
  3.  [2012-01-15T18:09:22+08:00] /Index/index | SQL:  RunTime:0.039159s SQL = SELECT * FROM `think_user` WHERE ( `account` = 'admin' ) AND ( `status` > 0 ) LIMIT 1
默認采用文件方式記錄日志信息,日志文件的命名格式是:年(簡寫)_月_日.log,例如:
09_10_01.log 表示2009年10月1日的日志文件
可以設置LOG_FILE_SIZE參數來限制日志文件的大小,超過大小的日志會形成備份文件。備份文件的格式是在當前文件名前面加上備份的時間戳,例如:
1189571417-07_09_12.log 備份的日志文件 如果需要使用其他方式記錄日志,可以設置LOG_TYPE參數,例如下面設置了采用郵件方式發送日志記錄:
  1. 'LOG_TYPE' =>1, //  采用郵件方式記錄日志
  2.  'LOG_DEST' =>'[email protected]', // 要發送日志的郵箱
  3.  'LOG_EXTRA' =>'From: [email protected]', // 郵件的發件人設置

3. 手動記錄

http://doc.thinkphp.cn/manual/log_record.html ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

錯誤:

1. 異常處理

和PHP默認的異常處理不同,ThinkPHP拋出的不是單純的錯誤信息,而是一個人性化的錯誤頁面。
只有在調試模式下面才能顯示具體的錯誤信息,如果在部署模式下面,你可能看到的是一個統一錯誤的提示文字,如果你試圖在部署模式下訪問一個不存在的模塊或者操作,會發送404錯誤。
調試模式下面一旦系統發生嚴重錯誤會自動拋出異常,也可以用ThinkPHP定義的throw_exception方法手動拋出異常。
  1. throw_exception('新增失敗');
  2. throw_exception('信息錄入錯誤','InfoException');
同樣也可以使用throw 關鍵字來拋出異常,下面的寫法是等效的:
  1. throw new ThinkException('新增失敗');
  2.  throw new InfoException('信息錄入錯誤');
如果需要,我們建議在項目的類庫目錄下面增加Exception目錄用於專門存放異常類庫,以更加精確地定位異常。

2. 異常模板

系統內置的異常模板在系統目錄的Tpl/think_exception.tpl,可以通過修改系統模板來修改異常頁面的顯示。也通過設置TMPL_EXCEPTION_FILE 配置參數來修改系統默認的異常模板文件, 例如:
  1. 'TMPL_EXCEPTION_FILE' => APP_PATH.'/Public/exception.tpl'
異常模板中可以使用的異常變量有:
$e['file']異常文件名
$e['line'] 異常發生的文件行數
$e['message'] 異常信息
$e['trace'] 異常的詳細Trace信息
因為異常模板使用的是原生PHP代碼,所以還可以支持任何的PHP方法和系統變量使用。

3. 異常顯示

拋出異常後通常會顯示具體的錯誤信息,如果不想讓用戶看到具體的錯誤信息,可以設置關閉錯誤信息的顯示並設置統一的錯誤提示信息,例如:
  1. 'SHOW_ERROR_MSG' =>false,
  2.  'ERROR_MESSAGE' =>'發生錯誤!'
設置之後,所有的異常頁面只會顯示“發生錯誤!”這樣的提示信息,但是日志文件中仍然可以查看具體的錯誤信息。新版如果關閉調試模式的話,為了安全起見,默認就是關閉異常信息提示。
另外一種方式是配置ERROR_PAGE參數,把所有異常和錯誤都指向一個統一頁面,從而避免讓用戶看到異常信息,通常在部署模式下面使用。ERROR_PAGE參數必須是一個完整的URL地址,例如:
  1. 'ERROR_PAGE' =>'/Public/error.html'
如果不在當前域名,還可以指定域名:
  1. 'ERROR_PAGE' =>'http://www.myDomain.com/Public/error.html'
注意ERROR_PAGE所指向的頁面不能再使用異常的模板變量了。 ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved