wiki:DataAccessArchitecture

MetaBBS의 데이터 액세스 구조

(아래 내용은 아직 구현되지 않은 것도 포함합니다. 그리고 나중에 바뀔 수도 있습니다.)

  • 모델 객체: 컨트롤러에서는 모델 객체만을 사용하여 데이터에 접근합니다. Model 클래스를 상속하여 모델 클래스를 선언합니다. 모델 객체 내부에서는 쿼리 API를 사용합니다.
  • 관계 객체: 모델 간의 관계를 나타내는 객체로, 1:1, 1:N, M:N 구조를 표현합니다.
  • 쿼리 API: SELECT, INSERT, UPDATE, DELETE 등의 SQL 쿼리문을 간단하게 추상화합니다. 데이터베이스 추상화 레이어를 사용하여 구현합니다.
  • 스키마(마이그레이션?) API: DB 구조의 생성 및 변경을 지원하는 API입니다. 이 또한 데이터베이스 추상화 레이어를 사용하여 구현합니다.
  • 데이터베이스 추상화 레이어: PHP의 DB 확장 함수 (mysql_*, sqlite_*, ...)를 공통 인터페이스로 사용할 수 있도록 추상화합니다. 또한 DBMS에 따라 달라질 수 있는 일부 SQL 문도 처리합니다. (LIMIT이나 escaping/quoting, 테이블 생성 등)

모델 객체의 사용 예

<?php
class Post extends Model { // Post 모델을 선언합니다.
  var $model = 'post'; // 이 모델이 post라는 것을 명시적으로 선언합니다.

  // 모델의 속성과 기본값을 지정합니다.
  var $title;
  var $body;
  var $notice = FALSE;

  // PHP 5.2 이하 버전에서는 Late Static Binding을 지원하지 않는 관계로, 스태틱 메소드를 다시 구현해야 합니다.
  // static 키워드에 주석 처리를 한 이유는 PHP 4에서 지원하지 않기 때문입니다.
  /*static*/ function find($id) {
    return Model::find('post', $id);
  }

  // create, update, delete 메소드는 Model 클래스로부터 상속되어 내려오므로 다시 구현할 필요가 없습니다. 필요한 경우 덮어 씌울 수 있습니다.
}

print_r(Post::find(42)); // ID를 42로 가지는 Post 객체의 구조를 표시합니다.
?>

관계 예제

<?php
class Board extends Model {
  function Board() {
    // 1:N association mapping
    $this->posts = $this->has_many('post');
  }

  function get_posts() {
    return $this->posts->find_all();
  }

  function get_post_count() {
    return $this->posts->count();
  }
}
?>

쿼리 API

find($model, $id)
$id를 ID로 가지는 모델 객체를 돌려줍니다.
find_by($model, $key, $value)
$key 필드가 $value인 모델 객체를 돌려줍니다.
find_all($model[, $condition[, $order[, $limit[, $offset]]]])
모델 객체의 배열을 돌려줍니다. $condition은 WHERE 절에 붙는 조건, $order는 ORDER BY 절에 붙는 정렬 조건, $limit은 레코드 개수, $offset은 시작 위치입니다.
count_all($model[, $condition])
$condition을 만족하는 행 개수를 돌려줍니다. 조건을 지정하지 않으면, 전체 행의 개수를 돌려줍니다.
delete_all($model[, $condition])
$condition을 만족하는 모든 행을 삭제합니다. 조건이 없을 때는 모든 행이 삭제되므로 조심해서 사용해야 합니다.
insert($model, $data)
$data를 가지고 행을 만듭니다.
update_all($model, $data[, $condition])
$data를 가지고 $condition을 만족하는 모든 행을 갱신합니다. 조건이 없으면 모든 행에 적용됩니다.

여러 행이 영향을 받을 수 있는 경우 함수 이름 뒤에 _all을 붙였습니다. count_all, delete_all의 경우 내장 함수와 충돌하기 때문에 붙인 것이기도 합니다.

첫번째 인자로 오는 모델 이름으로부터 테이블 이름을 알아냅니다.

스키마 API

테이블 생성 예제

<?php
$t = new Table('post');
$t->string('title', 255);
$t->text('body');
$t->timestamp('date');
$t->boolean('notice');
$t->index('notice');
$t->create();
?>

DB 추상화 레이어

DB 엔진 등록

register_driver($id, $class, $description)

<?php
class MySQLConnection extends BaseConnection {
  // ...
}

register_driver('mysql', 'MySQLConnection', 'MySQL Database');
?>

연결 클래스

BaseConnection? 클래스를 상속하여 구현합니다.

open($info)
데이터베이스 연결 정보의 배열을 인자로 받고 DB 핸들을 엽니다.
close()
데이터베이스 핸들을 닫습니다.
execute($query[, $params])
쿼리를 실행합니다. 오류가 발생하면 오류를 표시하고 스크립트 실행을 중단합니다. $params가 지정되어 있으면 쿼리에 bind_params 메소드를 적용하여 실행합니다.
query($query[, $params])
쿼리를 실행한 뒤 결과 객체를 돌려줍니다. 오류가 발생하면 오류를 표시하고 스크립트 실행을 중단합니다. $params가 지정되어 있으면 쿼리에 bind_params 메소드를 적용하여 실행합니다.
last_insert_id($sequence_name)
가장 최근 실행된 INSERT 쿼리에서의 시퀀스 ID를 돌려줍니다. $sequence_name은 사용하지 않더라도 반드시 적어줘야 합니다 (PostgreSQL을 위해서.)
bind_params($query, $params)
$query의 모든 물음표('?')를 $params 배열의 각 원소로 치환합니다. 이 때 $params 배열의 각 원소는 quote 메소드로 처리해야 합니다.
escape($string)
문자열을 이스케이프 합니다.
quote($value)
주어진 값을 타입에 따라 SQL 리터럴(?)로 변환합니다.
quote_identifier($id)
식별자를 이스케이프 합니다. 식별자는 테이블이나 필드 이름이 될 수 있습니다.

결과 클래스

BaseResultSet? 클래스를 상속하여 구현합니다.

fetch()
현재 행을 연관 배열로 돌려주고 커서 위치를 다음 행으로 옮깁니다.
fetch_column()
현재 행의 첫 열을 돌려주고 커서 위치를 다음 행으로 옮깁니다.
count()
행 개수를 돌려줍니다.