XOOPS 쿼리 빌더
XOOPS 쿼리 빌더는 SQL 쿼리 작성을 위한 현대적이고 유연한 인터페이스를 제공합니다. 이는 SQL 주입을 방지하고, 가독성을 향상시키며, 여러 데이터베이스 시스템에 대한 데이터베이스 추상화를 제공합니다.
쿼리 빌더 아키텍처
섹션 제목: “쿼리 빌더 아키텍처”graph TD A[QueryBuilder] -->|builds| B[SELECT Queries] A -->|builds| C[INSERT Queries] A -->|builds| D[UPDATE Queries] A -->|builds| E[DELETE Queries]
F[Table] -->|chains| G[select] F -->|chains| H[where] F -->|chains| I[orderBy] F -->|chains| J[limit]
G -->|chains| K[join] G -->|chains| H H -->|chains| I I -->|chains| J
L[Execute Methods] -->|returns| M[Results] L -->|returns| N[Count] L -->|returns| O[First/Last]쿼리빌더 클래스
섹션 제목: “쿼리빌더 클래스”유창한 인터페이스를 갖춘 기본 쿼리 빌더 클래스입니다.
클래스 개요
섹션 제목: “클래스 개요”namespace Xoops\Database;
class QueryBuilder{ protected string $table = ''; protected string $type = 'SELECT'; protected array $selects = []; protected array $joins = []; protected array $wheres = []; protected array $orders = []; protected int $limit = 0; protected int $offset = 0; protected array $bindings = [];}정적 메소드
섹션 제목: “정적 메소드”테이블
섹션 제목: “테이블”테이블에 대한 새 쿼리 빌더를 만듭니다.
public static function table(string $table): QueryBuilder매개변수:
| 매개변수 | 유형 | 설명 |
|---|---|---|
$table | 문자열 | 테이블 이름(접두사 포함 또는 제외) |
반환: QueryBuilder - 쿼리 빌더 인스턴스
예:
$query = QueryBuilder::table('users');$query = QueryBuilder::table('xoops_users'); // With prefix선택 쿼리
섹션 제목: “선택 쿼리”선택할 열을 지정합니다.
public function select(...$columns): self매개변수:
| 매개변수 | 유형 | 설명 |
|---|---|---|
...$columns | 배열 | 열 이름 또는 표현식 |
반환: self - 메소드 체이닝의 경우
예:
// Simple selectQueryBuilder::table('users') ->select('id', 'username', 'email') ->get();
// Select with aliasesQueryBuilder::table('users') ->select('id as user_id', 'username as name') ->get();
// Select all columnsQueryBuilder::table('users') ->select('*') ->get();
// Select with expressionsQueryBuilder::table('orders') ->select('id', 'COUNT(*) as total_items') ->groupBy('id') ->get();어디서
섹션 제목: “어디서”WHERE 조건을 추가합니다.
public function where(string $column, string $operator = '=', mixed $value = null): self매개변수:
| 매개변수 | 유형 | 설명 |
|---|---|---|
$column | 문자열 | 열 이름 |
$operator | 문자열 | 비교 연산자 |
$value | 혼합 | 비교할 값 |
반환: self - 메소드 체이닝의 경우
운영자:
| 운영자 | 설명 | 예 |
|---|---|---|
= | 같음 | ->where('status', '=', 'active') |
!= 또는 <> | 같지 않음 | ->where('status', '!=', 'deleted') |
> | 보다 큼 | ->where('price', '>', 100) |
< | 미만 | ->where('price', '<', 100) |
>= | 크거나 같음 | ->where('age', '>=', 18) |
<= | 작거나 같음 | ->where('age', '<=', 65) |
LIKE | 패턴 일치 | ->where('name', 'LIKE', '%john%') |
IN | 목록에 | ->where('status', 'IN', ['active', 'pending']) |
NOT IN | 목록에 없음 | ->where('id', 'NOT IN', [1, 2, 3]) |
BETWEEN | 범위 | ->where('age', 'BETWEEN', [18, 65]) |
IS NULL | null임 | ->where('deleted_at', 'IS NULL') |
IS NOT NULL | null이 아님 | ->where('deleted_at', 'IS NOT NULL') |
예:
// Single conditionQueryBuilder::table('users') ->select('*') ->where('status', '=', 'active') ->get();
// Multiple conditions (AND)QueryBuilder::table('users') ->select('*') ->where('status', '=', 'active') ->where('age', '>=', 18) ->get();
// IN operatorQueryBuilder::table('products') ->select('*') ->where('category_id', 'IN', [1, 2, 3]) ->get();
// LIKE operatorQueryBuilder::table('users') ->select('*') ->where('email', 'LIKE', '%@example.com') ->get();
// NULL checkQueryBuilder::table('users') ->select('*') ->where('deleted_at', 'IS NULL') ->get();또는어디
섹션 제목: “또는어디”OR 조건을 추가합니다.
public function orWhere(string $column, string $operator = '=', mixed $value = null): self예:
QueryBuilder::table('users') ->select('*') ->where('status', '=', 'active') ->orWhere('premium', '=', 1) ->get(); // SELECT * FROM users WHERE status = 'active' OR premium = 1어디에 / 어디에 NotIn
섹션 제목: “어디에 / 어디에 NotIn”IN/NOT IN에 대한 편의 메서드입니다.
public function whereIn(string $column, array $values): selfpublic function whereNotIn(string $column, array $values): self예:
QueryBuilder::table('posts') ->select('*') ->whereIn('status', ['published', 'scheduled']) ->get();
QueryBuilder::table('comments') ->select('*') ->whereNotIn('spam_score', [8, 9, 10]) ->get();whereNull / whereNotNull
섹션 제목: “whereNull / whereNotNull”NULL 검사를 위한 편리한 방법.
public function whereNull(string $column): selfpublic function whereNotNull(string $column): self예:
QueryBuilder::table('users') ->select('*') ->whereNotNull('verified_at') ->get();어디 사이에
섹션 제목: “어디 사이에”값이 두 값 사이에 있는지 확인합니다.
public function whereBetween(string $column, array $values): self예:
QueryBuilder::table('products') ->select('*') ->whereBetween('price', [10, 100]) ->get();
QueryBuilder::table('orders') ->select('*') ->whereBetween('created_at', ['2024-01-01', '2024-12-31']) ->get();INNER JOIN을 추가합니다.
public function join( string $table, string $first, string $operator = '=', string $second = null): self예:
QueryBuilder::table('posts') ->select('posts.*', 'users.username', 'categories.name') ->join('users', 'posts.user_id', '=', 'users.id') ->join('categories', 'posts.category_id', '=', 'categories.id') ->where('posts.published', '=', 1) ->get();leftJoin / rightJoin
섹션 제목: “leftJoin / rightJoin”대체 조인 유형.
public function leftJoin( string $table, string $first, string $operator = '=', string $second = null): self
public function rightJoin( string $table, string $first, string $operator = '=', string $second = null): self예:
QueryBuilder::table('users') ->select('users.*', 'COUNT(posts.id) as post_count') ->leftJoin('posts', 'users.id', '=', 'posts.user_id') ->groupBy('users.id') ->get();그룹별
섹션 제목: “그룹별”결과를 열별로 그룹화합니다.
public function groupBy(...$columns): self예:
QueryBuilder::table('orders') ->select('user_id', 'COUNT(*) as order_count', 'SUM(total) as total_spent') ->groupBy('user_id') ->get();
QueryBuilder::table('sales') ->select('department', 'region', 'SUM(amount) as total') ->groupBy('department', 'region') ->get();HAVING 조건을 추가합니다.
public function having(string $column, string $operator = '=', mixed $value = null): self예:
QueryBuilder::table('orders') ->select('user_id', 'COUNT(*) as order_count') ->groupBy('user_id') ->having('order_count', '>', 5) ->get();주문 기준
섹션 제목: “주문 기준”주문 결과.
public function orderBy(string $column, string $direction = 'ASC'): self매개변수:
| 매개변수 | 유형 | 설명 |
|---|---|---|
$column | 문자열 | 정렬 기준 |
$direction | 문자열 | ASC 또는 DESC |
예:
// Single orderQueryBuilder::table('users') ->select('*') ->orderBy('created_at', 'DESC') ->get();
// Multiple ordersQueryBuilder::table('posts') ->select('*') ->orderBy('category_id', 'ASC') ->orderBy('created_at', 'DESC') ->get();
// Random orderQueryBuilder::table('quotes') ->select('*') ->orderBy('RAND()') ->get();한계 / 오프셋
섹션 제목: “한계 / 오프셋”결과를 제한하고 오프셋합니다.
public function limit(int $limit): selfpublic function offset(int $offset): self예:
// Simple limitQueryBuilder::table('posts') ->select('*') ->limit(10) ->get();
// Pagination$page = 2;$perPage = 20;$offset = ($page - 1) * $perPage;
QueryBuilder::table('posts') ->select('*') ->limit($perPage) ->offset($offset) ->get();실행 방법
섹션 제목: “실행 방법”쿼리를 실행하고 모든 결과를 반환합니다.
public function get(): array반환: array - 결과 행의 배열
예:
$users = QueryBuilder::table('users') ->select('id', 'username', 'email') ->where('status', '=', 'active') ->orderBy('username') ->get();
foreach ($users as $user) { echo $user['username'] . ' (' . $user['email'] . ')' . "\n";}첫 번째 결과를 가져옵니다.
public function first(): ?array반환: ?array - 첫 번째 행 또는 null
예:
$user = QueryBuilder::table('users') ->select('*') ->where('id', '=', 123) ->first();
if ($user) { echo 'Found: ' . $user['username'];}마지막
섹션 제목: “마지막”마지막 결과를 가져옵니다.
public function last(): ?array예:
$latestPost = QueryBuilder::table('posts') ->select('*') ->orderBy('created_at', 'DESC') ->last();결과 개수를 가져옵니다.
public function count(): int반환: int - 행 수
예:
$activeUsers = QueryBuilder::table('users') ->where('status', '=', 'active') ->count();
echo "Active users: $activeUsers";###이 존재합니다
쿼리가 결과를 반환하는지 확인합니다.
public function exists(): bool반환: bool - 결과가 존재하면 True입니다.
예:
if (QueryBuilder::table('users')->where('email', '=', 'test@example.com')->exists()) { echo 'User already exists';}집계 값을 가져옵니다.
public function aggregate(string $function, string $column): mixed예:
$maxPrice = QueryBuilder::table('products') ->aggregate('MAX', 'price');
$avgAge = QueryBuilder::table('users') ->aggregate('AVG', 'age');
$totalSales = QueryBuilder::table('orders') ->aggregate('SUM', 'total');INSERT 쿼리
섹션 제목: “INSERT 쿼리”행을 삽입합니다.
public function insert(array $values): bool예:
QueryBuilder::table('users')->insert([ 'username' => 'john', 'email' => 'john@example.com', 'password' => password_hash('secret', PASSWORD_BCRYPT), 'created_at' => date('Y-m-d H:i:s')]);insertMany
섹션 제목: “insertMany”여러 행을 삽입합니다.
public function insertMany(array $rows): bool예:
QueryBuilder::table('log_entries')->insertMany([ ['action' => 'login', 'user_id' => 1, 'timestamp' => time()], ['action' => 'logout', 'user_id' => 2, 'timestamp' => time()], ['action' => 'update', 'user_id' => 3, 'timestamp' => time()]]);업데이트 쿼리
섹션 제목: “업데이트 쿼리”업데이트
섹션 제목: “업데이트”행을 업데이트합니다.
public function update(array $values): int반환: int - 영향을 받은 행 수
예:
// Update single userQueryBuilder::table('users') ->where('id', '=', 123) ->update([ 'email' => 'newemail@example.com', 'updated_at' => date('Y-m-d H:i:s') ]);
// Update multiple rowsQueryBuilder::table('posts') ->where('status', '=', 'draft') ->where('created_at', '<', date('Y-m-d', strtotime('-30 days'))) ->update([ 'status' => 'archived' ]);증가/감소
섹션 제목: “증가/감소”열을 늘리거나 줄입니다.
public function increment(string $column, int $amount = 1): intpublic function decrement(string $column, int $amount = 1): int예:
// Increment view countQueryBuilder::table('posts') ->where('id', '=', 123) ->increment('views');
// Decrement stockQueryBuilder::table('products') ->where('id', '=', 456) ->decrement('stock', 5);쿼리 삭제
섹션 제목: “쿼리 삭제”행을 삭제합니다.
public function delete(): int반환: int - 삭제된 행 수
예:
// Delete single recordQueryBuilder::table('comments') ->where('id', '=', 789) ->delete();
// Delete multiple recordsQueryBuilder::table('log_entries') ->where('created_at', '<', date('Y-m-d', strtotime('-30 days'))) ->delete();테이블에서 모든 행을 삭제합니다.
public function truncate(): bool예:
// Clear all sessionsQueryBuilder::table('sessions')->truncate();고급 기능
섹션 제목: “고급 기능”원시 표현식
섹션 제목: “원시 표현식”QueryBuilder::table('products') ->select('id', 'name', QueryBuilder::raw('price * quantity as total')) ->get();하위 쿼리
섹션 제목: “하위 쿼리”$recentPostIds = QueryBuilder::table('posts') ->select('id') ->where('created_at', '>', date('Y-m-d', strtotime('-7 days'))) ->toSql();
$comments = QueryBuilder::table('comments') ->select('*') ->whereIn('post_id', $recentPostIds) ->get();SQL 가져오기
섹션 제목: “SQL 가져오기”public function toSql(): string예:
$sql = QueryBuilder::table('users') ->select('id', 'username') ->where('status', '=', 'active') ->toSql();
echo $sql;// SELECT id, username FROM xoops_users WHERE status = ?완전한 예
섹션 제목: “완전한 예”조인을 사용한 복합 선택
섹션 제목: “조인을 사용한 복합 선택”<?php/** * Get posts with author and category info */
$posts = QueryBuilder::table('posts') ->select( 'posts.id', 'posts.title', 'posts.content', 'posts.created_at', 'users.username as author', 'categories.name as category' ) ->join('users', 'posts.user_id', '=', 'users.id') ->join('categories', 'posts.category_id', '=', 'categories.id') ->where('posts.published', '=', 1) ->orderBy('posts.created_at', 'DESC') ->limit(10) ->get();
foreach ($posts as $post) { echo '<article>'; echo '<h2>' . htmlspecialchars($post['title']) . '</h2>'; echo '<p class="meta">By ' . htmlspecialchars($post['author']) . ' in ' . htmlspecialchars($post['category']) . '</p>'; echo '<p>' . htmlspecialchars($post['content']) . '</p>'; echo '</article>';}QueryBuilder를 사용한 페이지 매김
섹션 제목: “QueryBuilder를 사용한 페이지 매김”<?php/** * Paginated results */
$page = isset($_GET['page']) ? (int)$_GET['page'] : 1;$perPage = 20;$offset = ($page - 1) * $perPage;
// Get total count$total = QueryBuilder::table('articles') ->where('status', '=', 'published') ->count();
// Get page results$articles = QueryBuilder::table('articles') ->select('*') ->where('status', '=', 'published') ->orderBy('created_at', 'DESC') ->limit($perPage) ->offset($offset) ->get();
// Calculate pagination$pages = ceil($total / $perPage);
// Display resultsforeach ($articles as $article) { echo '<div class="article">' . htmlspecialchars($article['title']) . '</div>';}
// Display pagination linksif ($pages > 1) { echo '<nav class="pagination">'; for ($i = 1; $i <= $pages; $i++) { if ($i == $page) { echo '<span class="current">' . $i . '</span>'; } else { echo '<a href="?page=' . $i . '">' . $i . '</a>'; } } echo '</nav>';}집계를 사용한 데이터 분석
섹션 제목: “집계를 사용한 데이터 분석”<?php/** * Sales analysis */
// Total sales by region$regionSales = QueryBuilder::table('orders') ->select('region', QueryBuilder::raw('SUM(total) as total_sales'), QueryBuilder::raw('COUNT(*) as order_count')) ->groupBy('region') ->orderBy('total_sales', 'DESC') ->get();
foreach ($regionSales as $region) { echo $region['region'] . ': $' . number_format($region['total_sales'], 2) . ' (' . $region['order_count'] . ' orders)' . "\n";}
// Average order value$avgOrderValue = QueryBuilder::table('orders') ->aggregate('AVG', 'total');
echo 'Average order value: $' . number_format($avgOrderValue, 2);모범 사례
섹션 제목: “모범 사례”- 매개변수화된 쿼리 사용 - QueryBuilder는 매개변수 바인딩을 자동으로 처리합니다.
- 체인 방법 - 읽기 쉬운 코드를 위해 유창한 인터페이스 활용
- SQL 출력 테스트 -
toSql()을 사용하여 생성된 쿼리를 확인합니다. - 인덱스 사용 - 자주 쿼리되는 열이 인덱싱되었는지 확인하세요.
- 결과 제한 - 대규모 데이터 세트에는 항상
limit()을 사용합니다. - 집계 사용 - 데이터베이스가 PHP 대신 계산/합산을 수행하도록 합니다.
- 이스케이프 출력 - 표시된 데이터를 항상
htmlspecialchars()으로 이스케이프합니다. - 인덱스 성능 - 느린 쿼리를 모니터링하고 그에 따라 최적화합니다.
관련 문서
섹션 제목: “관련 문서”- XoopsDatabase - 데이터베이스 계층 및 연결
- Criteria - 레거시 Criteria 기반 쿼리 시스템 -../Core/XoopsObject - 데이터 객체 지속성 -../Module/Module-System - 모듈 데이터베이스 작업
참조: XOOPS 데이터베이스 API