쿼리 XML
Zittme에서는 SQL을 직접 쓰지 않고, 테이블과 쿼리를 XML로 선언합니다. DB 종류가 달라도 같은 코드가 돌고, 값 바인딩이 자동이라 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 - 날짜는 관례상
char(14)에YYYYMMDDHHIISS문자열로 저장합니다 (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 '%값%' | 포함 / 미포함 |
like_prefix (=like_head) | LIKE '값%' | ~로 시작 |
like_tail (=like_suffix) | LIKE '%값' | ~로 끝남 |
search | 단어별 LIKE '%값%' | 검색어를 단어로 쪼개 각각 매칭 |
in / notin | IN (...) | 목록 포함 / 제외 |
between | BETWEEN | 범위 |
null / notnull | IS (NOT) 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 = '값' 을 넘겨도, insert/update 쿼리 XML의 <columns> 에 그 칼럼이 선언되어 있지 않으면 오류 없이 무시됩니다. "분명히 넣었는데 DB에 없다"면 십중팔구 이 경우입니다.
칼럼을 추가할 때는 세 곳을 함께 고치세요.
1. schemas/테이블.xml 에 칼럼 추가
2. Install의 moduleUpdate에 addColumn (기존 설치 사이트용)
3. 관련 insert/update 쿼리 XML의 <columns> 에 추가