程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 數據庫知識 >> DB2數據庫 >> DB2教程 >> 使用 DB2 pureXML 和 PHP 構建 Support Knowledge Base(上)

使用 DB2 pureXML 和 PHP 構建 Support Knowledge Base(上)

編輯:DB2教程

開始之前

本教程面向那些希望開發由 IBM DB2 數據庫驅動的應用程序的 Web 應用程序開發人員。要跟隨本教程的學習,您應當熟悉基本的 Html、CSS 和 PHP 代碼。您還應當具有使用數據庫管理系統和 SQL 語言的經驗。

關於本教程

IBM DB2 中最有用、最具通用性的技術之一就是它通過 pureXML 特性對 XML 數據的原生支持。PureXML 允許您使用與處理關系數據的相同方式同時存儲、檢索和操作 XML 數據。這使您能夠開發可以同時利用關系數據庫和 XML 數據的應用程序。這類應用程序在需要處理大量 XML 數據時十分有用,因為您無需將這些數據首先轉換為關系結構。

本教程給出了使用 PHP 創建 Support Knowledge Base 系統的逐步指導,該系統同時使用傳統的 DB2 關系列和 pureXML 列存儲數據。應用程序利用 SQL/XML 的功能將 XML 數據映射為關系列。這允許您使用 PHP 檢索數據,就好象這些數據是以關系方式存儲的一樣。

教程指導的前提是您在一個運行 Windows® XP 並安裝了 DB2 Express-C、apache HTTP Server 和 PHP 的本地開發服務器上創建應用程序。您應當能夠將這些指導步驟應用於其他系統和配置,但是這一點未進行過測試。

應用程序布局

應用程序的目錄結構十分簡單。您將應用程序存儲在 Apache 安裝的 htdocs 目錄的 kbase 子目錄中。在 Windows 系統上,這個目錄通常位於 C:\Program Files\Apache Software Foundation\apache 2.2\htdocs 中。在 kbase 目錄下應當有 4 個子目錄:

classes — 包含用於將數據從數據庫推入到應用程序所需的 PHP 類。

css — 包含用於定義 Web 應用程序的用戶界面感觀的 CSS 樣式表文件。

includes — 包含用於應用程序的標題、導航側欄和腳注(footer)的代碼文件。PHP 腳本在每個頁面的開始和結束部分包含這些文件,這樣您就不需要將相同的代碼復制到每個頁面的源文件中。

sql — 包含用於創建 DB2 數據庫和表的數據庫 SQL 腳本。您將在本教程的下一小節使用這些腳本。

應用程序包含的 Web 頁面可以分為以下類別:

管理視圖 — 包含允許您創建和管理類別、文章和評論的頁面。

客戶機(最終用戶)視圖 — 包含應用程序主頁面、搜索結果頁面,以及用於查看文章類別和細節的頁面。

先決條件

要跟隨本教程給出的步驟,您需要安裝以下軟件:

IBM 軟件下載:IBM DB2 Express-C 9.5

apache HTTP Server 2.2

PHP 5.2

developerWorks 系列文章 “Leveraging pureXML in a Flex microblogging application”介紹了有關安裝和配置以上軟件的詳細指導。該系列的第 1 部分介紹如何安裝 DB2 Express-C。第 3 部分深入介紹了安裝 apache HTTP Server 和 PHP 的步驟。

創建 DB2 數據庫

在本節中,您將為 Knowledge Base 應用程序創建數據庫。您需要事先安裝 IBM DB2 Express-C 並訪問 DB2 Command Editor 工具。應用程序的數據庫包含 4 個表,每個表各自包含了一些常規列和 XML 列。

使用 SQL 和 DB2 Command Editor 創建數據庫

創建應用程序的第一步是創建一個新的 IBM DB2 數據庫。在 Windows 中,啟動 DB2 Command Editor(圖 1),如下所示:

Start > Programs > IBM DB2 > DB2COPY1 (Default) > Command Line Tools > Command Editor

圖 1. DB2 Command Editor
使用 DB2 pureXML 和 PHP 構建 Support Knowledge Base(上)

Command Editor 的主界面分為兩個部分。上面的部分是編輯區。可以在此區域輸入希望對數據庫發出的命令。底部是結果區。這個區域將顯示在編輯區執行完語句後數據庫的響應消息。

將清單 1 中的代碼復制並粘貼到 DB2 Command Editor 的編輯區。單擊編輯區中的工具欄左側的綠色箭頭按鈕,執行代碼。需要等待一段時間,因為代碼執行需要大概一分鐘左右的時間才能完成。

清單 1. database.sql

CREATE DATABASE kbase USING CODESET UTF-8 TERRITORY us; 
CONNECT TO kbase; 
 
CREATE TABLE category ( 
  id    int not null generated by default as identity, 
  data      XML not null, 
  primary key(id) 
); 
 
CREATE TABLE article ( 
  id    int not null generated by default as identity, 
  date_created  timestamp not null, 
  date_modifIEd  timestamp not null, 
  vIEw_count    int, 
  category_id    int not null, 
  data      XML not null, 
  primary key(id), 
  foreign key(category_id) references category(id) 
    on delete cascade 
); 
 
CREATE TABLE comment ( 
  id    int not null generated by default as identity, 
  date_left    timestamp not null, 
  approved    smallint not null, 
  article_id    int not null, 
  data      XML not null, 
  primary key(id), 
  foreign key(article_id) references article(id) 
    on delete cascade 
); 
 
CREATE TABLE rating ( 
  id     int not null generated by default as identity, 
  date_rated    timestamp not null, 
  article_id    int not null, 
  data      XML not null, 
  primary key(id), 
  foreign key(article_id) references article(id) 
    on delete cascade 
); 

代碼創建了一個名為 kbase 的新數據庫,其中包含 4 個數據庫表:category、article、comment 和 rating。每個表都有一個 id 列和一個數據列。其中一些表還包括 Meta 列,比如 date_created 和外鍵。每個表中的 id 列被用作惟一標識符,用來識別表中的數據。數據列包含 XML 類型的數據,並以 XML 格式保存大部分數據。您稍後會了解到 XML 數據如何在每個表中使用。

執行完 database.sql 代碼後,您應當收到來自 DB2 服務器的表示成功的響應,如圖 2 所示。

圖 2. 執行 database.sql 的結果
使用 DB2 pureXML 和 PHP 構建 Support Knowledge Base(上)

創建了數據庫後,您現在可以繼續創建 Knowledge Base 應用程序本身了。在下一小節中,您將創建一個 PHP 類來連接到 IBM DB2 數據庫。

數據庫連接 PHP 類

DB2 針對 PHP 的擴展包括一系列允許您連接到 IBM DB2 並與其中的數據交互的函數。在本小節中,您將創建一個 PHP 類來封裝這些函數。這允許您使用更少的代碼來與數據庫交互。其他 PHP 類可以使用這個數據庫類。您可以調用它的對象方法來初始化數據庫連接,轉義存在潛在危險的字符串,執行數據庫查詢,以及返回這些查詢的結果。

定義 DB 類

在本節中,您將創建一個名為 DB 的 PHP 類來處理與 DB2 數據庫的連接。將數據庫連接代碼與應用程序的其余部分分開保存始終是一個好的想法。通過這種方式,如果您需要修改連接主機名、用戶名、密碼甚至是數據庫驅動程序,那麼您只需在一個位置執行修改。

創建一個名為 db.PHP 的文件,將清單 2 中的代碼復制到其中,然後將文件保存到項目的 classes 子目錄下。

清單 2. db.PHP

<?PHP 
class DB { 
  private $conn; 
 
  function __construct() { 
    $database = "kbase"; 
    $hostname = "localhost"; 
    $port = 50000; 
    $user = "username"; 
    $password = "passWord"; 
 
    $db_connect_string = "DRIVER={IBM DB2 ODBC DRIVER};" 
      . "DATABASE=$database;" 
      . "HOSTNAME=$hostname;PORT=$port;PROTOCOL=TCPIP;" 
      . "UID=$user;PWD=$passWord;"; 
 
    $this->conn = db2_connect($db_connect_string, '', ''); 
 
    if(!$this->conn) { 
      dIE(db2_conn_errormsg($this->conn)); 
    } 
  } 
 
  function safe_no_Html($string, $include_quotes=true) { 
    HtmlentitIEs($string, ENT_QUOTES, 'utf-8'); 
 
    if($include_quotes) 
return "'".db2_escape_string($string)."'"; 
    else return db2_escape_string($string); 
  } 
 
  function query($sql) { 
    $result = db2_exec($this->conn, $sql); 
 
    if(!$result) { 
      dIE(db2_stmt_errormsg()); 
    } else { 
      return $result; 
    } 
  } 
 
  function get_row($result) { 
    return db2_fetch_array($result); 
  } 
} 
?> 

這個類包含一個單獨的私有成員變量 $conn,用於保存數據庫連接。類構造函數(__construct 函數)包含數據庫設置。在類構造函數中,使用用於 DB2 安裝的正確的值替換 $user 和 $passWord 變量。這些設置用於形成一個數據庫連接字符串,後者又用於通過 db2_connect 函數連接到數據庫。

DB 類還包含三個函數,可用於處理數據庫中的數據:

safe_no_Html — 接受一個字符串並返回一個可以安全插入到數據庫中的值。它使用 db2_escape_string 函數轉義任何有可能引起數據庫錯誤的字符,並使用 htmlentitIEs 函數處理任何可能有害的 Html 或 JavaScript 代碼。第二個參數 $include_quotes 是可選參數,如果您不要求返回值包含用於包圍結果的單引號,那麼可以將其設置為 false。

query — 包含一個參數 $sql,應當包含在將要在數據庫中執行的 SQL 語句中。它使用 db2_exec 函數對 DB2 運行語句,並且如果成功的話,將返回一個 $result 資源。如果發生了一個錯誤,應用程序將停止處理,而 DB2 錯誤消息被輸出給用戶。

注意:在開發應用程序時,將數據庫消息輸出給用戶將非常方便,但是在生產環境中永遠也不應該這樣做,因為這會將重要的細節洩漏給潛在的攻擊者。同樣,當您在生產環境中工作並且通過 PHP 執行語句時,您應當使用准備好的語句,而不是使用簡單的 SQL 字符串。

get_row — 使用 $result 資源作為參數,並使用 db2_fetch_array 函數返回下一個結果行。

在下一小節中,您將創建兩個使用 DB 類與 DB2 服務器進行通信的應用程序類。

應用程序類

Knowledge Base 應用程序有兩個主要的組件:類別和文章。每篇文章屬於(且僅屬於)一個類別。並且,每篇文章可能有零到多個評論,以及零到多個相關的評分。在本節中,您將創建兩個 PHP 類(Category 和 Article),這兩個類將包含可對這些組件執行的所有函數。

創建 Category 類

要創建的第一個類是 Category 類。這個類包含用於創建 Category 對象的實例變量和方法。每個類別有一個 $id 變量,保存在數據庫的 int 列中,以及一個 name 變量,保存在名為 data 的 XML 列中。對於數據庫中類別表的每一行,都有一個 XML 文檔存儲在數據列中,並使用如清單 3 所示的格式。

清單 3. 針對類別表中的行的 XML

<category> 
  <name>Category Name</name> 
</category> 

注意:您可能想知道為什麼這裡需要外部的 <category> 標記。在本例中其實不需要使用它們,但是如果您想在以後添加更多數據字段,那麼您需要將其包含到 <category> 標記中,因為 DB2 pureXML 要求在其 XML 文檔中包含一個外部節點。

Category 類有多個函數。它使用 getter 和 setter 方法處理類中已定義的屬性,用 save 函數將新的和現有的類別保存到數據庫,用 delete 函數刪除類別,以及用其他函數檢索有關一個或多個類別的信息。

清單 4 包含來自 category.php 文件的一個片段,定義了 Category 類。要獲得完整的類定義,可從 下載 部分下載源代碼。將 category.PHP 文件保存到項目的 classes 子目錄中。

清單 4. category.PHP 片段

... 
  function save() { 
    $sql = ""; 
    if($this->id) 
      $sql = "UPDATE category SET data = (XMLparse(document 
 '<category><name>".$this->db->safe_no_Html($this->name, 
false)."</name></category>')) WHERE id = $this->id"; 
else $sql = "INSERT INTO category(data) VALUES((XMLparse(document 
'<category><name>".$this->db->safe_no_Html($this->name, 
false)."</name></category>')))"; 
 
    $result = $this->db->query($sql); 
    if($result) return true; 
    else return false; 
  } 
 
  function delete() { 
    $sql = "DELETE FROM category WHERE id = $this->id"; 
    $result = $this->db->query($sql); 
    if($result) return true; 
    else return false; 
  } 
 
  function getAllCategorIEs() { 
    $sql = 'SELECT c.id, x.name FROM category c, ' 
      .' XMLTABLE(\'$d/category\' PASSING c.data AS "d" ' 
      .' COLUMNS name VARCHAR(200) PATH \'name\') AS x ' 
      .' ORDER BY x.name'; 
    $result = $this->db->query($sql); 
    $rows = array(); 
    if($result) { 
      while($row = $this->db->get_row($result)) { 
        $rows[] = array($row[0], $row[1]); 
      } 
    } 
    return $rows; 
  } 
...   

該類包含三個變量。$db 變量用於實例化與數據庫的連接。每當類需要檢索、保存或刪除數據時,都將使用數據庫連接。當 Category 類被創建時,$db 變量將作為一個新的 DB 對象得到調用。DB 類的方法隨後可以通過下面的語法用於 Category 類:

$this->db->method_name()

您將在處理數據庫的函數中使用該語法。例如:

$this->db->query($sql)

save 函數非常聰明,因為它可以同時處理新記錄的創建和現有記錄的更新。它將檢查對象是否為 $id 屬性設置了一個值。如果設置了的話,它將執行一條 UPDATE 語句,如果不是的話,它將使用 INSERT 語句來創建新記錄。

SQL/XML 簡介

如果您熟悉 SQL 並且開發由關系數據庫驅動的應用程序,那麼您可能已經注意到 Category 類中的 SQL 語句使用了不尋常的函數。這些函數是對 IBM DB2 中可用 SQL 標准的特殊擴展。這種擴展後的 SQL 被稱為 SQL/XML。

SQL/XML 的第一處應用體現在 save 函數中。該函數使用 XMLPARSE 函數將傳遞給它的文本解析為 XML。這一點在本例中表現良好,因為您處理的 XML 數據都是非常基本的數據。然後,在創建 Article 類時,您會看到一個更為高級的 XML 解析需求。

SQL/XML 通過 XMLTABLE 函數在 getAllCategories、load 和 getCategoryNameById 中也得到了應用。讓我們看看清單 5 的 getAllCategorIEs 函數中的 SELECT 語句,進一步了解詳情。考慮到可讀性,所有 PHP 字符串格式都被從清單中移除。

清單 5. getAllCategorIEs 函數中的 SELECT 語句

SELECT c.id, x.name 
FROM category c, XMLTABLE('$d/category' PASSING c.data AS "d" 
  COLUMNS name VARCHAR(200) PATH 'name') AS x 
ORDER BY x.name 

這個 SELECT 語句中的所有數據均檢索自類別表。該表包含一個 XML 類型的名為 data 的列。清單 6 從該列獲取 <name> XML 標記並將其映射為一個關系列名。結果被放到一個名為 x 的虛構表中,這使結果可以被查詢的其余部分訪問。

清單 6. 將 <name> 標記映射為一個關系列名

XMLTABLE('$d/category' PASSING c.data AS "d" 
  COLUMNS name VARCHAR(200) PATH 'name') AS x 

這允許您訪問 <name> 標記的內容,就好象您有一個典型的關系數據庫列一樣。

創建 Article 類

另一個需要創建的應用程序類是 Article 類。清單 7 包含了 article.php 文件的一個片段,其中定義了 Article 類。要獲得完整的類定義,可以從 下載 部分下載源代碼。將您的 article.PHP 文件保存到項目的 classes 子目錄中。

清單 7. 來自 article.PHP 文件的片段

... 
  function save() { 
    $sql = ""; 
 
    if($this->id) { 
      $sql = "UPDATE article SET data = (XMLDOCUMENT(XMLELEMENT(NAME 
\"article\", XMLCONCAT(" 
        . " XMLELEMENT(NAME \"title\", ".$this->db->safe_no_Html 
($this->title)."), " 
        . " XMLELEMENT(NAME \"content\", ".$this->db->safe_no_Html 
($this->content)."))))), " 
        . " category_id = $this->category_id, date_modifIEd = CURRENT 
TIMESTAMP WHERE id = $this->id"; 
    } else { 
      $sql = "INSERT INTO article(date_created, date_modified, vIEw_count, 
category_id, data) VALUES ( " 
        . " CURRENT TIMESTAMP, CURRENT TIMESTAMP, 0, $this->category_id, 
(XMLDOCUMENT(XMLELEMENT(NAME \"article\", XMLCONCAT(" 
        . " XMLELEMENT(NAME \"title\", ".$this->db->safe_no_Html 
($this->title)."), " 
        . " XMLELEMENT(NAME \"content\", ".$this->db->safe_no_Html 
($this->content)."))))))"; 
    } 
    $result = $this->db->query($sql); 
    if($result) return true; 
    else return false; 
  } 
... 
 
  function getArticlesBySearchTerm($search_term) { 
    $search_term = strtolower(trim($search_term)); 
    $sql = 'SELECT a.id, x.title, a.date_created, a.vIEw_count FROM article a, ' 
      . ' XMLTABLE(\'$d/article\' passing a.data as "d" ' 
      . ' COLUMNS title VARCHAR(200) PATH \'title\', ' 
      . ' content VARCHAR(4000) PATH \'content\') as x ' 
      . ' WHERE LOWER(x.title) LIKE \'%'.$this->db->safe_no_Html 
($search_term, false).'%\' ' 
      . ' OR LOWER(x.content) LIKE \'%'.$this->db->safe_no_Html 
($search_term, false).'%\' ' 
      . ' ORDER BY x.title'; 
    $result = $this->db->query($sql); 
    $rows = array(); 
    while($row = $this->db->get_row($result)) { 
      $rows[] = array($row[0], $row[1], $row[2], $row[3]); 
    } 
    return $rows; 
  } 
... 
  function addComment($name, $comments, $ip_address) { 
    $sql = 'INSERT INTO comment(date_left, article_id, approved, data) VALUES 
(CURRENT TIMESTAMP, ' 
      . $this->getId().', 0, (XMLDOCUMENT(XMLELEMENT(NAME "comment", 
XMLCONCAT(' 
      . ' XMLELEMENT(NAME "name", \''.$this->db->safe_no_Html 
($name, false).'\'), ' 
      . ' XMLELEMENT(NAME "ip_address", \''.$ip_address.'\'), ' 
      . ' XMLELEMENT(NAME "content", \''.$this->db->safe_no_Html 
($comments,false).'\'))))))'; 
    $result = $this->db->query($sql); 
    if($result) return true; 
    else return false; 
  } 
... 

注意這個類要比 Category 類長一些。它包含 Article 類的 getters 和 setters 方法;用於檢索、保存和刪除文章的函數;用於檢索和創建評論與評分的函數;以及用於管理文章評論的函數。許多函數都彼此相似,因此讓我們看看這個類中引入的一些比較重要的概念。

第一個比較感興趣的函數是 save 函數。清單 8 展示了該函數中的 INSERT 和 UPDATE 語句,其中不包括 PHP 字符串格式。

清單 8. save 函數中的 INSERT 和 UPDATE 語句

INSERT INTO article(date_created, date_modified, vIEw_count, 
  category_id, data) 
VALUES (CURRENT TIMESTAMP, CURRENT TIMESTAMP, 0, $this->category_id, 
  (XMLDOCUMENT(XMLELEMENT(NAME "article", XMLCONCAT( 
    XMLELEMENT(NAME "title", $this->title), 
    XMLELEMENT(NAME "content", $this->content) 
  )))) 
) 
 
UPDATE article 
SET data = (XMLDOCUMENT(XMLELEMENT(NAME "article", XMLCONCAT( 
  XMLELEMENT(NAME "title", $this->title), 
  XMLELEMENT(NAME "content", $this->content) 
)))), 
category_id = $this->category_id, 
date_modifIEd = CURRENT TIMESTAMP 
WHERE id = $this->id 

這兩個語句的大部分都很相似。在 INSERT 語句中,除了被插入到數據列的值外,其余所有部分都是標准的關系 SQL。類似地,在 UPDATE 語句中,只有 data 列使用不同的數據進行了更新。在這兩種情況下,被插入到 data 列的值由清單 9 中的代碼生成。

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