最近更新: 2010-01-04

PHP - Schema-Database

新增內容: Database Row 簡介

Schema-Database 是一個針對小型應用系統或嵌入式系統所設計的抽象資料庫存取層函數庫。 它提供一組 Schema class 用於組態資料庫表格與欄位,強化資料內容的型態檢查、格式檢查、解碼與編碼動作。 同時設計了兩種 class, Database_Query 和 Database_Row ,提供基礎的 Active Record 與 ORM 能力。這兩種 class 運用 PDO class 與 Schema class ,簡化資料查詢、存取和資料內容檢查動作。

我已經將此軟體放置到 Google Code 上。專案入口: PHP Schema and Database library

日前看到 jaceju 在他的網誌上發表了一套小型的 PHP framework, GoEz Framework。他提到: 目前正在開發中的是通用型的 SQL 語法產生器。我想到我手上也有那麼一套用於小型或嵌入式系統的函數庫提供了這一方面的功能,只是失業年餘,也就暫停維護了。正好看到 jaceju 有這個構想,於是我這個週末把我那套函數庫找出來,整理之後,放置到 Google code 繼續維護。

Wiki quick index

  1. 快速使用說明
  2. Schema簡介
  3. Database Query 簡介
  4. Database Row 簡介

查詢範例

它的查詢動作如下:

$results = $query->from('Test')->select();

$results = $query->from('Test')->select('id');

$query->from('Test')
      ->where(array('id' => 10))
      ->select(array('id', 'name'));

$query->from('Test')
      ->where(array('password' => '123')) //it will encode '123' by your schema.
      ->select();

$total_qty_expr = $query->prepare('SUM(:qty)', array('qty' => ''));

$query->from('Order')
      ->where(array('qty >' => 30))
      ->group_by('id')
      ->order_by('total_qty')
      ->select(array('id', 'total_qty' => $total_qty_expr));

如果你覺得這種語法很像 LINQ ,不用懷疑,我就是抄 LINQ 的。

資料更新範例

它也提供了基本的 ORM 功能,例如 :

$data = array(
    'id'  => 1,
    'name'    => 'admin',
    'password' => '123',
    'permission'    => 0,
    'email'     => 'admin@abc.com'
);

$row->assign($data);

$row->insert();

//You can use array index to access field.
echo $row['name'];

//Also you can use object property to access field.
echo $row->permission;

$row->email = 'rock@abc.com';

$row->update();

$id = $row->id;

$result = $row->get($id);

$this->assertEquals('rock@abc.com', $result->name);

$row->delete();

$result = $row->get($id);

$this->assertTrue( empty($result) );

我將資料庫的操作動作劃分成兩個類別,一個主要負責查詢,另一個負責新增、更新與刪除。

在應用系統中,大約有八成以上的資料庫操作動作是查詢。Database_Query 針對這種情形 提供了簡便的查詢語法,產生查詢句並執行之,取代我們自行組織 SQL 查詢句。 Database_Row 的查詢結果集只是一組含有記錄個體的普通陣列。我們讀取這些結果集的方法, 與使用其他 Database library 時的方式並無差異。

當我們需要更新或刪除查詢結果集的其中一筆記錄時,這時就輪到 Database_Row 發揮功用。 我們取出這筆記錄包入 Database_Row,利用它的方法異動此筆記錄。 在小型應用系統中,我們幾乎不會批次異動整個資料結果集,Database_Row 只提供恰到好處的處理功能。

Database_Query 與 Database_Row 是互補的類別,同時也不干涉你原本的程式框架。

功能限制

這套 library 功能並不多。我覺得 PDO 已經夠抽象了,沒有必要再加一層,所以這套 library 只用 PDO 作為資料存取層,而不另行設計。但是它特別加上了 Schema 類別,強化資料內容的型態檢查、格式檢查、解碼與編碼動作。一但你將格式(pattern)、編碼(encoder)、解碼(decoder)動作組織到 Schema 後,它就會在必要之處運用它們,你不必再自己處理。

它雖然提供了類似 Active Record 或 LINQ 的查詢語法,但不支援聯結。它雖然提供了基本的 ORM 能力,但不支援會期(session)與交易(transaction)。這套 library 原本預期發揮作用的場合,是在小型或嵌入式系統。所以它不打算提供這種環境下用不到的資料庫功能。 如果你在極少數情況用到這套函數庫做不到的功能時,你也可以自行透過 PDO 去存取,這完全不會妨礙到 Schema-Database library 運作。

在適當的場合使用適當的工具。當你需要的資料庫操作功能遠遠超出這個函數庫所能支持者時,你應該尋找功能更完整的大型資料庫函數庫或開發框架。


2009-12-16

對於來自 Java 世界的朋友們,如果你習慣了重度的 ORM 操作,你也可以結合 Query 與 Row 來做事。

class PowerQuery extends Database_Query {
    public function select($args, $decode) {
        $table = $this->statments['from'];
        $row = new Database_Row(array(
            'db' => $this->db,
            'schema' => $this->schema,
            'table' => $table
        ));

        $results = parent::select($args, $decode);

        $rows = array();
        foreach ($results as $result) {
            $rows[] = $row->factory($result);
        }
        return $rows; //現在回傳的將會是一個 Row object 陣列。
    }
}

$results = $query->from($table)->select();

$results[0]->update();
$results[1]->update();
...

不幸的是,這會造成低落的效能; Java 環境差別不大,PHP環境差異較高。在實務上,我們查詢出來的資料項,超過九成以上不會被異動,我們只使用了它們的取值功能(getter)。就此結果而言,我們只是把包覆資料的 model object 當成一個比較高級的結構(struct)或雜湊表(hash table, assocation array)。在 PHP 的世界中,我們還是保持簡單的作法吧。

相關文章
樂多舊網址: http://blog.roodo.com/rocksaying/archives/11056367.html

樂多舊回應
未留名 (#comment-20182379)
Tue, 15 Dec 2009 11:07:34 +0800
這個讚!我下載回來研究~ 大感恩 :D
未留名 (#comment-20184599)
Tue, 15 Dec 2009 23:21:05 +0800
一年前我在寫這個套件時,還有不少想法沒有實現。不過因為被裁員,就擱下了。

而我目前工作的公司,主力開發環境是 Java 以及一些 Perl (奇怪,那我被雇用的原因是什麼??)。似乎暫時不會用 PHP 。除非我再度失業,否則我大概會有一段時間不會用 PHP 進行實務開發工作。

在沒有實務經驗支持下進行的功能擴充,很容易陷入擴張陷阱。而 jaceju 的工作則是以 PHP 為主,由他基於實務經驗所提出改進內容,有助於改進功能又不會增加幾乎用不著的功能。

所以我個人非常期待 jacuju 能夠回饋改進意見或是一起來改善這個套件。
未留名 (#comment-20188207)
Wed, 16 Dec 2009 21:37:43 +0800
好的,這陣子手邊的東西一件一件都正在進行中, SQL 這部份目前也進行到了 Alpah 版,我將會參考版主的 Project 來做一些改進,屆時會提供我的想法與意見 :)
chiu0602@gmail.com(chiu) (#comment-22855318)
Wed, 24 Apr 2013 11:10:24 +0800
想請教一下, schema-database能否實踐以下SQL query:
1. order by xxx ASC / DESC
2. LIMIT
3. where yyy IS NULL / NOT NULL
4. where lower(table.uid)=lower(:uid)

謝謝好套件!
未留名 (#comment-22855574)
Wed, 24 Apr 2013 14:12:31 +0800
1. order by 固定用預設順序 (通常是 ASC)。
目前沒有實作 DESC 。

2. LIMIT 沒有實作。
附帶一提, LIMIT 是少數 SQL 產品才有的項目,如 MySQL 。
因為各家 SQL 對這種需求的作法都不相同,所以我不實作這類跟特定 SQL 產品綁定的項目。

3, 4. 兩項,目前沒有實作。
因為我建資料庫表格時,都會指定 default ,不留 NULL ,所以我沒實作 IS NULL 。
至於 lower() 這類查詢,雖然我的 DatabaseQuery::prepare() 也能做,但還不如直接用 PDO::prepare() 。

一、我到目前為止所處理的專案,還沒有使用過第4項所提的查詢句,所以我一直沒有擴充 DatabaseQuery 支援的項目。

二、schema-database 並不全面,只是作為 PDO 的其中一種資料存取層,所以有些項目必須和 PDO 混著用。DatabaseQuery 有支援的方法就用它,沒提供的就直接用 PDO 的方法。這就是為何 schema-database 不自己配置 PDO instance ,而是外放給使用者配置好後傳給它。因為 schema-database 認為有些場合下,使用者還是要自己直接用 PDO 方法,所以它不打算將 PDO 隱蔽起來。