天天看點

《PHP精粹:編寫高效PHP代碼》——2.4節PHP資料庫對象

本節書摘來自華章社群《php精粹:編寫高效php代碼》一書中的第2章,第2.4節php資料庫對象,作者:(美)  davey shafik,更多章節内容可以通路雲栖社群“華章社群”公衆号檢視

2.4 php資料庫對象

如果之前你使用過php和mysql,你可能用過mysql或mysqli類庫連接配接到資料庫,如使用mysql_connect()函數。多年來,這是連接配接到mysql資料庫的标準方式,并且對于其他資料庫平台也使用同樣的方式。

這些類庫可以直接使用,并形成了無數php應用程式類庫和架構的基礎。這種方式的缺點是每個擴充都與其他稍有不同,是以使得代碼在資料庫平台之間輕松轉移變得複雜。雖然這些資料庫特定類庫依然活躍并且運轉良好,但是本章中仍将專注講解更先進的pdo擴充。建立的pdo擴充提供了一組統一功能與各種資料庫平台的對話。php 5采用的就是面向對象的擴充,當時人們将它的很多特性引入了php語言。

了解oop

如果你不熟悉面向對象編碼,并且已仔細閱讀第1章,現在你正好可以了解使用oop的更多内容。

然而,pdo并沒有解決問題,這個問題是出現在不同資料庫平台之間的sql文法差異;是以,乍看起來似乎這個擴充并不完全是最有效的手段。pdo可以和各種各樣的資料庫平台連接配接和對話,但是為了建立真正獨立于平台的應用程式,我們不得不改寫發送的sql語句。

pdo是一個抽象層,這表明它建立在php以及php連接配接資料庫的方式之間。pdo提供了非常簡潔的功能來執行查詢和周遊資料集,讓我們深入研究如何使用pdo的技術細節吧。

2.4.1 使用pdo連接配接到mysql

要使用pdo連接配接到資料庫,需要執行個體化一個新的pdo對象并且傳遞一個dsn,如果需要,還要加上使用者名和密碼。dsn(data source name, 資料源名稱)由描述實際連接配接的資料結構組成。若要連接配接到建立(資料庫名稱recipes,使用localhost作為主機名)的資料庫,将使用下面的php代碼生成連接配接:

《PHP精粹:編寫高效PHP代碼》——2.4節PHP資料庫對象
《PHP精粹:編寫高效PHP代碼》——2.4節PHP資料庫對象

2.4.2 從表中選擇資料

建立了pdo對象之後,就可以檢索資料了。首先,在資料庫中會有怎樣的食譜清單呢?當用pdo選擇資料時,要建立一個pdostatement對象。它代表查詢,并使我們擷取結果。對于一個基本的查詢,可以使用pdo::query()方法:

《PHP精粹:編寫高效PHP代碼》——2.4節PHP資料庫對象

使用order對結果排序

當我們像這樣從mysql中選擇資料時,将得到以未定義的順序傳回的記錄;通常這些記錄是按照插入的順序排序的。為成為更完美的應用,可以在查詢的末尾添加這樣的指令:order by created desc。将按時間的降序傳回結果,即總是先看到最新的食譜。

以上示例利用了pdostatement::fetch()方法,此方法能處理大量提取資料的模式。

2.4.3 資料提取模式

在前面的示例中,我們看到了如何用pdostatement對象來表示查詢及其資料集。每次調用fetch()方法,都将從結果集中接收到另外一行。還可以使用fetchall()方法一次檢索所有的行。這兩種方法都接受fetch_style參數,這個參數定義如何格式化結果集。

pdo提供了便于使用的常量:

pdo::fetch_assoc完成了以前你在while循環中看到的,它使用鍵組傳回數組到列名。

pdo::fetch_num也傳回數組,但這次使用數字鍵。

pdo::fetch_both(預設值)結合了pdo::fetch_assoc和pdo::fetch_num以提供一個每個值出現兩次的數組,一次使用其列名,一次使用數字索引。

pdo::fetch_class傳回一個已命名的類的對象而不是數組,這些值以列的名字命名設定到屬性中。

為了看到由pdo::fetch_assoc傳回的結果,可以輸入下列代碼:

《PHP精粹:編寫高效PHP代碼》——2.4節PHP資料庫對象

這裡有一些正在運作,讓我們依次看看它們。

首先,通過傳遞sql語句進入prepare()方法建立了pdostatement。仔細觀察這條sql語句,你可能會看到一些奇怪的東西。在:recipe_id前面的冒号表示這是一個占位符(placeholder)。在實際運作這個查詢之前,會用真正的值來替換這個占位符。

然後,execute()這個查詢。必須為字元串中的每個占位符傳入值,而且還要将這些字元串傳入prepare()方法中。因為使用指定的占位符,是以要建立一個由與這些占位符數量相同的元素組成的數組。每個占位符都有一個與之比對的數組元素,數組元素的名字作為鍵值,然後要用它的實際值來替換鍵值。

既然已知道隻能傳回一行,可以通過調用一次fetch()方法來代替循環。

生成sql語句

在之前的示例中,定義了一個單獨的$sql變量來儲存這個字元串并傳入pdo::prepare。這種方法可以讓代碼更易于閱讀,并且在需要建立一個更複雜的查詢時提供幫助。這種方式也可以幫助我們進行調試,你可以不費力地檢查有哪些東西傳入了prepare()。

占位符不需要名字,你也可以使用??符号為變量保留一個位置作為沒有命名的占位符。此外,在sql語句中有很多這樣的占位符,用它們來建立pdostatement,而且作為數組把這些值傳入execute()中,但在這個例子中,我們必須将這些值按順序排列在查詢語句中。用下面的示例很容易說明這些:

如果查詢變得很龐大或很複雜,命名占位符可使你更容易儲存代碼。将數組中命名的鍵值傳入execute(),比起應付一個巨大的用數字作為索引的數組,這種方式讓你更容易看出哪個值屬于哪個參數。

預處理語句使我們清楚地辨別出查詢中哪些部分是資料庫語言,哪些包含可變資料。你會聽說“安全咒語”:“過濾輸入,避免輸出”(如果你還未聽說,在第5章将很快看到)。當使用資料庫時,必須溢出已經發送到資料庫的值(也就是說删除不需要的字元)。你可能見過像mysql_escape_string()這樣的mysql功能。當使用預處理語句時,為占位符傳入的值已經溢出,因為mysql知道這些都是可能改變的值。這種額外的安全保障是将pdo及預處理語句作為規範令人信服的一個原因。

2.4.5 綁定值和預處理語句的變量

既然mysql已經準備了一個查詢,那麼使用不同的值再次運作這個查詢時隻會有很小的系統開銷。我們已經知道了如何傳遞變量到pdostatement的execute()方法中。在本節中,我們将看到如何綁定值甚至變量到語句中,以便在每次執行查詢時都使用這些值或變量。

解釋概念的簡單例子

這些例子可能看起來确實沒有什麼意義,但舉例說明資料集裡更多先進的技術的确是種樂趣!如果你問自己:“為什麼我要嘗試這些呢?”要記住這些都是适用于你自己項目的技術(可能用在更複雜的設定中)。

雖然這是事實,但總的來說,我們最好用盡可能少的步驟從資料庫中檢索資料,有時你使用的查詢類型意味着這些步驟不能組合使用。當使用不同的值重複調用相同的查詢時,可以設定一些元素用于每次查詢。

例如,如果我們總想使用同樣的chef值,可以使用pdostatement::bindvalue():

《PHP精粹:編寫高效PHP代碼》——2.4節PHP資料庫對象

最後這兩個例子表明了在調用execute()方法之前如何設定值或變量到pdostatement對象。不管你是使用bindvalue()方法、bindparam()方法或是傳入值到execute()方法本身,預處理語句都是極為有用的!如果我們多次運作這個語句,這種方式不僅能夠提高代碼的性能,而且也可以毫無疑問地溢出占位符。

2.4.6 插入一行并擷取id

前面已經深入研究了select語句的選項,但是insert和update語句又怎麼樣呢?實際上這幾個語句看起來确實很相似,即我們預處理然後再執行一條語句。接下來以插入一些新的食譜作為示例:

《PHP精粹:編寫高效PHP代碼》——2.4節PHP資料庫對象

rowcount()是pdostatement對象的一個方法,它會指出有多少行由于查詢而發生改變。

2.4.8 删除資料

和插入或更新資料一樣,我們以相同的方式删除資料,即對查詢進行預處理然後再執行查詢。如果想删除“starter”這一類别(因為它未使用),可以這樣做:

《PHP精粹:編寫高效PHP代碼》——2.4節PHP資料庫對象

我們會再次使用rowcount()方法檢查删除行,檢視其數量是否和預期的一樣多(很多缺失的或錯誤的where子句比預期會造成更大損害)。