查詢 XML
在 Zittme 中不直接撰寫 SQL,而是以 XML 宣告資料表與查詢。即使資料庫種類不同,同一份程式碼也能執行;值的繫結是自動完成的,因此不必擔心 SQL 注入。
資料表宣告:schemas/*.xml
檔案名稱就是資料表名稱。安裝模組時,核心會建立資料表。
<table name="mymodule_item">
<column name="item_srl" type="bigint" notnull="notnull" primarykey="primarykey" />
<column name="member_srl" type="bigint" notnull="notnull" index="idx_member_srl" />
<column name="title" type="varchar" size="250" notnull="notnull" />
<column name="content" type="bigtext" />
<column name="status" type="varchar" size="10" notnull="notnull" default="open" index="idx_status" />
<column name="regdate" type="char" size="14" index="idx_regdate" />
</table>- 主要型別:
number/bigint/varchar(必須指定 size)/char/text/bigtext/date/float - 日期依慣例以
YYYYMMDDHHIISS字串儲存在char(14)中(date('YmdHis'))。 - 唯一編號以
getNextSequence()取得,放入*_srl欄位。
上線運作中新增欄位:核心不會自動在已建立的資料表上加入新欄位。必須同時做兩件事:修改結構描述 XML,並在 Install 的 moduleUpdate 中呼叫 addColumn()。
查詢宣告:queries/*.xml
SELECT
<query id="getItems" action="select">
<tables>
<table name="mymodule_item" />
</tables>
<columns>
<column name="*" />
</columns>
<conditions>
<condition operation="equal" column="status" var="status" default="open" />
<condition operation="like" column="title" var="s_title" pipe="and" />
<condition operation="more" column="regdate" var="start_regdate" pipe="and" />
</conditions>
<navigation>
<index var="sort_index" default="item_srl" order="desc" />
<list_count var="list_count" default="20" />
<page var="page" default="1" />
</navigation>
</query>var是從 PHP 傳入的值的名稱。若未傳入值,該條件會整個被省略。 必要條件請加上notnull="notnull"。default是沒有值時的預設值。pipe是與前一個條件的連接方式(and/or)。第一個條件不使用。
可用的 operation 一覽:
| operation | SQL | 意義 |
|---|---|---|
equal | = | 等於 |
notequal (=not_equal) | != | 不等於 |
more (=gte) | >= | 大於等於 |
excess (=gt) | > | 大於 |
less (=lte) | <= | 小於等於 |
below (=lt) | < | 小於 |
like / notlike | LIKE '%value%' | 包含 / 不包含 |
like_prefix (=like_head) | LIKE 'value%' | 以~開頭 |
like_tail (=like_suffix) | LIKE '%value' | 以~結尾 |
search | 逐詞 LIKE '%value%' | 將搜尋字詞拆成單字後分別比對 |
in / notin | IN (...) | 包含於列表 / 排除 |
between | BETWEEN | 範圍 |
null / notnull | IS (NOT) NULL | 檢查 NULL |
regexp / notregexp | REGEXP | 正規表示式 |
- 在
<navigation>中設定list_count與page後,executeQueryArray的結果會一併附上分頁資訊(page_navigation)。
INSERT / UPDATE / DELETE
<query id="insertItem" action="insert">
<tables>
<table name="mymodule_item" />
</tables>
<columns>
<column name="item_srl" var="item_srl" notnull="notnull" filter="number" />
<column name="title" var="title" notnull="notnull" />
<column name="content" var="content" />
<column name="status" var="status" default="open" />
<column name="regdate" var="regdate" />
</columns>
</query><query id="updateItemStatus" action="update">
<tables>
<table name="mymodule_item" />
</tables>
<columns>
<column name="status" var="status" notnull="notnull" />
</columns>
<conditions>
<condition operation="equal" column="item_srl" var="item_srl" filter="number" notnull="notnull" />
</conditions>
</query>filter="number"是只允許數字通過的驗證。- delete 只需在
action="delete"中放 conditions。為避免出現沒有條件的 delete/update,請務必在關鍵條件上加上 notnull。
在 PHP 中執行
$args = new \stdClass;
$args->status = 'open';
$args->page = (int)\Context::get('page') ?: 1;
$output = executeQueryArray('mymodule.getItems', $args);
if (!$output->toBool())
{
return $output; // DB 오류
}
$items = $output->data; // 항상 배열
$paging = $output->page_navigation; // navigation 선언 시executeQuery適合單筆結果,executeQueryArray則一律以陣列回傳結果。列表請使用 Array 版本。- 需要聯結時,可在
<tables>中放入兩個資料表並在<conditions>中串接,或使用<table type="left join">與<conditions>子宣告。複雜的聯結最快的方法是參考核心模組(board、document)的查詢 XML。
最常見的陷阱
未列在 <columns> 中的欄位會被默默丟棄。 即使在 PHP 中傳入 $args->new_column = 'value',若 insert/update 查詢 XML 的 <columns> 中沒有宣告該欄位,就會在沒有任何錯誤的情況下被忽略。如果「明明有寫入,資料庫裡卻沒有」,十之八九就是這個原因。
新增欄位時,請同時修改以下三處。
- 在
schemas/table.xml中新增欄位 - 在 Install 的 moduleUpdate 中呼叫 addColumn(供既有安裝的網站使用)
- 在相關 insert/update 查詢 XML 的
<columns>中新增