Files
4WDCSA.co.za/DATABASE_SERVICE_EXAMPLES.md
twotalesanimation 7e544311e3 Docs: DatabaseService usage examples and migration guide
- Added comprehensive before/after examples
- Covers: SELECT, SELECT one, INSERT, UPDATE, DELETE, COUNT, EXISTS
- Transaction handling examples
- Type specification reference (i, d, s, b)
- Migration path and benefits summary
- Reduces query code by 50-75%
- Guide for gradual implementation throughout codebase
2025-12-03 20:06:34 +02:00

8.0 KiB

DatabaseService Usage Examples

This document shows how to refactor existing code to use the new DatabaseService class for cleaner, more maintainable database operations.

Current State

Files are using the procedural MySQLi pattern:

$stmt = $conn->prepare("SELECT * FROM users WHERE email = ?");
$stmt->bind_param("s", $email);
$stmt->execute();
$result = $stmt->get_result();
$row = $result->fetch_assoc();
$stmt->close();

Example 1: Simple SELECT (admin_members.php)

Current Code

$stmt = $conn->prepare("SELECT user_id, first_name, last_name, tel_cell, email, dob, accept_indemnity FROM membership_application");
$stmt->execute();
$result = $stmt->get_result();

// Then in HTML/JS loop:
while ($row = $result->fetch_assoc()) {
    // display row
}

Using DatabaseService

// Simple - get all records
$members = $db->select("SELECT user_id, first_name, last_name, tel_cell, email, dob, accept_indemnity FROM membership_application");

// In HTML/JS loop:
foreach ($members as $row) {
    // display row
}

Benefits:

  • No manual bind_param(), execute(), close() needed
  • Returns array directly
  • Automatic error tracking via $db->getLastError()

Example 2: SELECT with Parameters (validate_login.php)

Current Code

$query = "SELECT * FROM users WHERE email = ?";
$stmt = $conn->prepare($query);
$stmt->bind_param("s", $email);
$stmt->execute();
$result = $stmt->get_result();

if ($result->num_rows == 1) {
    $row = $result->fetch_assoc();
    // use $row
}
$stmt->close();

Using DatabaseService

$user = $db->selectOne(
    "SELECT * FROM users WHERE email = ?",
    [$email],
    "s"  // s = string type
);

if ($user) {
    // use $user - returns false if no row found
}

Benefits:

  • One-liner for single row
  • Handles null checks automatically
  • Type specification clear in parameters

Example 3: INSERT (validate_login.php)

Current Code

$query = "INSERT INTO users (email, first_name, last_name, profile_pic, password, is_verified) VALUES (?, ?, ?, ?, ?, ?)";
$stmt = $conn->prepare($query);
$is_verified = 1;
$stmt->bind_param("sssssi", $email, $first_name, $last_name, $picture, $password, $is_verified);
if ($stmt->execute()) {
    $user_id = $conn->insert_id;  // ❌ Bug: insert_id from $conn, not $stmt
    // use $user_id
}
$stmt->close();

Using DatabaseService

$user_id = $db->insert(
    "INSERT INTO users (email, first_name, last_name, profile_pic, password, is_verified) VALUES (?, ?, ?, ?, ?, ?)",
    [$email, $first_name, $last_name, $picture, $password, 1],
    "sssssi"
);

if ($user_id) {
    // $user_id contains the auto-increment ID
} else {
    $error = $db->getLastError();
}

Benefits:

  • Returns insert ID directly
  • Automatic error handling
  • Cleaner parameter list

Example 4: UPDATE (admin_members.php)

Current Code

$user_id = intval($_POST['user_id']);
$stmt = $conn->prepare("UPDATE membership_application SET accept_indemnity = 1 WHERE user_id = ?");
if ($stmt) {
    $stmt->bind_param("i", $user_id);
    $stmt->execute();
    $stmt->close();
}

Using DatabaseService

$user_id = intval($_POST['user_id']);
$affectedRows = $db->update(
    "UPDATE membership_application SET accept_indemnity = 1 WHERE user_id = ?",
    [$user_id],
    "i"
);

if ($affectedRows !== false) {
    // Updated successfully, $affectedRows = number of rows changed
}

Benefits:

  • Returns affected row count
  • No manual statement closing
  • Error available via $db->getLastError()

Example 5: COUNT / EXISTS

Current Pattern (Need 3 lines)

$stmt = $conn->prepare("SELECT COUNT(*) as count FROM users WHERE email = ?");
$stmt->bind_param("s", $email);
$stmt->execute();
$result = $stmt->get_result();
$row = $result->fetch_assoc();
if ($row['count'] > 0) { /* exists */ }
$stmt->close();

Using DatabaseService (One line)

$exists = $db->exists("users", "email = ?", [$email], "s");
if ($exists) {
    // User exists
}

Benefits:

  • Boolean result
  • Intent is clear
  • One-liner

Example 6: Multiple Rows with Filtering

Current Code

$status = 'active';
$stmt = $conn->prepare("SELECT * FROM members WHERE status = ? ORDER BY last_name ASC");
$stmt->bind_param("s", $status);
$stmt->execute();
$result = $stmt->get_result();
$members = [];
while ($row = $result->fetch_assoc()) {
    $members[] = $row;
}
$stmt->close();

Using DatabaseService

$members = $db->select(
    "SELECT * FROM members WHERE status = ? ORDER BY last_name ASC",
    ['active'],
    "s"
);

Benefits:

  • Returns array directly
  • No loop needed
  • 2 lines vs 8 lines

Example 7: Error Handling

Current Pattern

$stmt = $conn->prepare("SELECT * FROM users WHERE id = ?");
if (!$stmt) {
    echo "Prepare failed: " . $conn->error;
    exit();
}
$stmt->bind_param("i", $id);
if (!$stmt->execute()) {
    echo "Execute failed: " . $stmt->error;
    exit();
}

Using DatabaseService

$user = $db->selectOne("SELECT * FROM users WHERE id = ?", [$id], "i");
if ($user === false) {
    $error = $db->getLastError();
    error_log("Database error: " . $error);
    // handle error
}

Benefits:

  • Error handling centralized
  • No null checks for each step
  • Debug via $db->getLastQuery()

Example 8: Transactions

Current Pattern

$conn->begin_transaction();
try {
    $stmt = $conn->prepare("INSERT INTO orders ...");
    $stmt->execute();
    
    $stmt = $conn->prepare("UPDATE inventory ...");
    $stmt->execute();
    
    $conn->commit();
} catch (Exception $e) {
    $conn->rollback();
}

Using DatabaseService

$db->beginTransaction();

$order_id = $db->insert("INSERT INTO orders ...", [...], "...");
if ($order_id === false) {
    $db->rollback();
    exit("Order creation failed");
}

$updated = $db->update("UPDATE inventory ...", [...], "...");
if ($updated === false) {
    $db->rollback();
    exit("Inventory update failed");
}

$db->commit();

Benefits:

  • Unified transaction API
  • Built-in error checking
  • Clean rollback on failure

Type Specification Reference

When using DatabaseService methods, specify parameter types:

Type Meaning Example
"i" Integer user_id = 5
"d" Double/Float price = 19.99
"s" String email = 'test@example.com'
"b" Blob Binary data

Examples:

// Single parameter
$db->select("SELECT * FROM users WHERE id = ?", [123], "i");

// Multiple parameters
$db->select(
    "SELECT * FROM users WHERE email = ? AND status = ?", 
    ["test@example.com", "active"], 
    "ss"
);

// Mixed types
$db->select(
    "SELECT * FROM orders WHERE user_id = ? AND total > ? AND date = ?",
    [5, 100.50, "2025-01-01"],
    "ids"  // integer, double, string
);

Migration Path

Phase 1: New Code

Start using $db for all new features and AJAX endpoints.

Phase 2: High-Traffic Files

Refactor popular files:

  1. validate_login.php - Login is critical
  2. functions.php - Helper functions
  3. admin_members.php, admin_payments.php - Admin pages

Phase 3: Gradual Rollout

As each file is refactored, commit and test thoroughly before moving to next.

Phase 4: Full Migration

Eventually all procedural $conn->prepare() patterns replaced.


Benefits Summary

Aspect Before After
Lines per query 5-8 1-3
Error handling Manual checks Automatic
Type safety bind_param() Parameter array
Statement closing Manual Automatic
Insert ID handling $conn->insert_id (buggy) Direct return
Debugging Check multiple vars getLastError(), getLastQuery()
Consistency Varies Unified API

Next Steps

  1. Start with one file (e.g., admin_members.php)
  2. Convert simple queries first
  3. Test thoroughly
  4. Commit and move to next file
  5. Keep $conn available for complex queries that don't fit the standard patterns

The $db service makes your code cleaner, safer, and easier to maintain.