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/CoreThink.Core核心類庫包
Lib/BehaviorThink.Behavior內置行為類庫包
Lib/DriverThink.Driver內置驅動類庫包
Lib/TemplateThink.Template內置模板引擎類庫包
核心類庫包下面包含下面核心類庫:
類名說明
Action系統基礎控制器類
App系統應用類
Behavior系統行為基礎類
Cache系統緩存類
Db系統抽象數據庫類
DispatcherURL調度類
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/read@blog.thinkphp.cn','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_)
UserModelthink_user
UserTypeModelthink_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']?=?'ThinkPHP@gmail.com';
  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']?=?'ThinkPHP@gmail.com';
  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']?=?'ThinkPHP@gmail.com';
  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']?=?'ThinkPHP@gmail.com';
  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']?=?'ThinkPHP@gmail.com';
  6. $User->save($data);?//?根據條件保存修改的數據
如果id是數據表的主鍵的話,系統自動會把主鍵的值作為更新條件來更新其他字段的值。
還有一種方法是通過create或者data方法創建要更新的數據對象,然后進行保存操作,這樣save方法的參數可以不需要傳入。
  1. $User?=?M("User");?//?實例化User對象
  2. ?//?要修改的數據對象屬性賦值
  3. $data['name']?=?'ThinkPHP';
  4. $data['email']?=?'ThinkPHP@gmail.com';
  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'=>'ThinkPHP@gmail.com');
  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?=?'ThinkPHP@gmail.com';
  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('liu21st@gmail.com');
  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']????=????'liu21st@gmail.com';
  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
forFor循環數據輸出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調試,用于調試信息
SQLSQL語句,該級別只在調試模式開啟時有效
要開啟日志記錄,必須在配置中開啟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'?=>'admin@domain.com',?//?要發送日志的郵箱
  3. ?'LOG_EXTRA'?=>'From:?webmaster@example.com',?//?郵件的發件人設置

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所指向的頁面不能再使用異常的模板變量了。
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

?

轉載于:https://www.cnblogs.com/echohao/p/4715421.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/273067.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/273067.shtml
英文地址,請注明出處:http://en.pswp.cn/news/273067.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

java 判斷域密碼到期提醒,Exchange Server 2010下,檢測用戶密碼到期通知提醒腳本...

#############################################Author:wangtingdong#For:檢測AD密碼過期時間并郵件通知#Version:1.0##############################################Import-Module Activedirectory#1和2選擇一個執行#1檢索出指定OU里不包含設置了永不過期及禁用的賬戶#$allad…

php中定義css樣式的好處,CSS的優點和缺點分別是什么

CSS的優點有&#xff1a;豐富的樣式定義、易于修改、結構清晰、多頁面使用等&#xff1b;CSS的缺點&#xff1a;瀏覽器支持不一樣具有兼容性、不能明確指定繼承性CSS的主要哦作用是為HTML頁面添加樣式&#xff0c;使得頁面更加美觀。接下來在文章中將為大家詳細介紹CSS的優點與…

前端工具整理

代碼的規范】 http://www.css88.com/doc/codeguide/ 【Viewport Sizes尺寸查詢】 http://viewportsizes.com/?filter 【在線小工具】 http://www.xueui.cn/design/online-tools 【px,em,rem單位轉換工具】 http://pxtoem.com/ 【json格式化】 http://jsonlint.com/ 【在線…

mysql無法本地連接,本地連接騰訊云Mysql失敗問題

騰訊云主機中MySQL無法遠程連接的解決辦法在遠程主機上&#xff0c;我開啟了 mysql服務&#xff0c;用 phpmyadmin 可以打開&#xff0c;比如說用戶名為 root&#xff0c;密碼為 123456。不過用 Mysql 客戶端遠程連接時卻報了錯誤&#xff0c;比如 Mysql-Front 報了如下Access …

python getcwd 轉義,Python os.getcwd() 方法

Python os.getcwd() 方法概述os.getcwd() 方法用于返回當前工作目錄。語法getcwd()方法語法格式如下&#xff1a;os.getcwd()參數無返回值返回當前進程的工作目錄。實例以下實例演示了 getcwd() 方法的使用&#xff1a;#!/usr/bin/python# -*- coding: UTF-8 -*-import os, sys…

函數_方法_的四種調用方式

class Program{/// <summary>/// 無參數&#xff0c;無返回值/// </summary>/// <param name"args"></param>static void n1(){Console.WriteLine("這是第一種方法");}/// <summary>/// 有參數&#xff0c;無返回值/// <…

php_cawler_html嵌套標簽清洗

主要處理 嵌套 div&#xff0c;正則無法很好的處理清洗 比如文本&#xff1a; 想要移除 class quizPutTag 的div &#xff0c;內部可能嵌套的還有未知層級的div【前提是html文本段是閉合標簽的】 這是<div>test<div class"quizPutTag">test</div>…

php添加項目,thinkphp添加一個項目

假如我們想新建一個app項目&#xff0c;創建一個app文件夾&#xff0c;在app目錄下 新建一個index.php文件加上入口文件引用define(APP_DEBUG,TRUE);require_once(../ThinkPHP.php);訪問你剛建立的文件&#xff0c;這時候app目錄里面自動新建了所需的文件app/conf/config.php這…

PHP中的mb_convert_encoding與iconv函數介紹

iconv函數庫能夠完成各種字符集間的轉換&#xff0c;是php編程中不可缺少的基礎函數庫。 1、下載libiconv函數庫http://ftp.gnu.org/pub/gnu/libiconv/libiconv-1.9.2.tar.gz&#xff1b; 2、解壓縮tar -zxvf libiconv-1.9.2.tar.gz; 3、安裝libiconv &#xff03;confi…

php遍歷視頻文件,php使用glob函數遍歷文件和目錄詳解

php glob()函數返回匹配指定模式的文件名或目錄。因此我們可以使用glob函數來查找文件&#xff0c;也可以實現目錄的遍歷。函數說明&#xff1a;array glob ( string $pattern [, int $flags ] )功能&#xff1a;尋找與模式匹配的文件路徑,返回包含匹配文件(目錄)的數組(注&…

GitHub 建立遠程倉庫

終端所有信息: Last login: Fri Aug 14 08:58:01 on console wuxiaoyuan:~ lan$ ls -al ~/.ssh ls: /Users/lan/.ssh: No such file or directory wuxiaoyuan:~ lan$ mkdir .ssh wuxiaoyuan:~ lan$ cd /Users/lan/.ssh wuxiaoyuan:.ssh lan$ ssh-keygen -t rsa -b 4096 -C &qu…

朱建輝php,朱建輝/laravel-bjyblog

鏈接簡介這個項目是把 thinkphp-bjyblog 用 laravel 框架重構后的產物&#xff1b;下圖中的白俊遙博客即是使用 laravel-bjyblog 開發的個人博客安裝使用可以通過以下兩種命令安裝&#xff1b;composer create-project baijunyao/laravel-bjyblog blog && cdblog &…

matlab 棍,雙足機器人行走棍圖怎么用MATLAB畫出來

匿名用戶1級2016-05-25 回答The following is a function I wrote to generate a stick diagram of robot motion. Hope it is helpful to you all.function stick(filename,user_frame_per_second,max_step)global robotfoot2;mov_traj load(filename);dt mov_traj(2,1) - …

設計模式學習筆記-觀察者模式

1. 概述 有時被稱作發布/訂閱模式&#xff0c;觀察者模式定義了一種一對多的依賴關系&#xff0c;讓多個觀察者對象同時監聽某一個主題對象。這個主題對象在狀態發生變化時&#xff0c;會通知所有觀察者對象&#xff0c;使它們能夠自動更新自己。 2. 解決的問題 將一個系統分割…

Handler與多線程

1、Handler介紹 在Android開發中&#xff0c;我們常會使用單獨的線程來完成某些操作&#xff0c;比如用一個線程來完成從網絡上下的圖片&#xff0c;然后顯示在一個ImageView上&#xff0c;在多線程操作時&#xff0c;Android中必須保證以下兩點&#xff1a; &#xff08;1&…

oracle read only 事務,oracle set transaction read only與dbms_transaction實現事務transaction控制...

SQL> show userUser is "SYS"SQL> set transaction read only;Transaction setSQL> insert into t_table values(3);1 row insertedSQL> commit;Commit complete---sys用戶 set transaction read only不生效SQL> select * from t_table;A------------…

oracle report builder 6i下載,oracle report builder 6i - 數據模型中的SQL查詢代碼

我是Vijetha&#xff0c;我正在研究報告6i&#xff0c;我很陌生 . 我有以下查詢 .在front_end中&#xff0c;在Reports Parameter中&#xff0c;當用戶單擊“運行”按鈕時&#xff0c;它將詢問START_DATE和END_DATE輸入 .如果用戶提供START_DATE和END_DATE或者不提供輸入&#…

HTML/CSS/JavaScript學習總結(轉)

HTML 網站開發的主要原則是&#xff1a; – 用標簽元素HTML描述網頁的內容結構&#xff1b; – 用CSS描述網頁的排版布局&#xff1b; – 用JavaScript描述網頁的事件處理&#xff0c;即鼠標或鍵盤在網頁元素上的動作后的程序 HTML&#xff08;Hyper Text Mark-up Language 超文…

oracle引用vs,VS2013中使用oracle,有關引用哪個.dll

Oracle、Microsoft 和第三方供應商都提供了針對 Oracle 產品進行了優化的數據供應程序。 Oracle 和 Microsoft 免費提供其 Oracle 數據供應程序。訪問 Oracle 的操作有些類似于對 Sql Server 的操作。對Oracle 的訪問有以下幾種數據提供程序。 Microsoft.NET Oracle 提供程序 O…

貪心方法

1.背包問題 按效益值/重量 進行排序輸入 2.帶限期的作用排序 按效益值進行排序輸入 3 最小生成樹&#xff1a; 貪心方法&#xff1a;每次計入成本最小的邊 原樹T&#xff0c; 欲構造的最小生成樹T Prim&#xff1a; 從T中選與T中結點相連的成本最小的邊。 且&#xff1a;邊之前…