개발자 가이드

쿼리 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 전체:

operationSQL
equal=같음
notequal (=not_equal)!=다름
more (=gte)>=이상
excess (=gt)>초과
less (=lte)<=이하
below (=lt)<미만
like / notlikeLIKE '%값%'포함 / 미포함
like_prefix (=like_head)LIKE '값%'~로 시작
like_tail (=like_suffix)LIKE '%값'~로 끝남
search단어별 LIKE '%값%'검색어를 단어로 쪼개 각각 매칭
in / notinIN (...)목록 포함 / 제외
betweenBETWEEN범위
null / notnullIS (NOT) NULL널 검사
regexp / notregexpREGEXP정규식
  • <navigation>list_countpage 를 두면 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> 에 추가