NexaUI framework menyediakan dukungan untuk database transactions yang memungkinkan Anda melakukan operasi database secara atomik dan konsisten.
Database transactions memungkinkan Anda melakukan serangkaian operasi database sebagai satu unit kerja. Jika semua operasi berhasil, transaction akan di-commit. Jika terjadi kesalahan, semua operasi akan di-rollback, mengembalikan database ke keadaan sebelumnya.
NexaUI menyediakan API yang sederhana untuk bekerja dengan transactions:
// Memulai transaction
$db = $this->Storage();
$db->beginTransaction();
try {
// Melakukan beberapa operasi database
$db->table('users')->insert([
'name' => 'John Doe',
'email' => 'john@example.com'
]);
$userId = $db->lastInsertId();
$db->table('profiles')->insert([
'user_id' => $userId,
'bio' => 'Software Developer',
'location' => 'Jakarta'
]);
// Jika semua operasi berhasil, commit transaction
$db->commit();
return true;
} catch (\Exception $e) {
// Jika terjadi error, rollback transaction
$db->rollback();
return false;
}
NexaUI mendukung nested transactions, memungkinkan Anda untuk memulai transaction baru di dalam transaction yang sudah berjalan:
$db = $this->Storage();
// Memulai transaction utama
$db->beginTransaction();
try {
// Operasi database pertama
$db->table('orders')->insert([
'customer_id' => 1,
'total' => 100.00
]);
$orderId = $db->lastInsertId();
// Memulai nested transaction
$db->beginTransaction();
try {
// Operasi database dalam nested transaction
$db->table('order_items')->insert([
'order_id' => $orderId,
'product_id' => 1,
'quantity' => 2,
'price' => 50.00
]);
// Commit nested transaction
$db->commit();
} catch (\Exception $e) {
// Rollback nested transaction
$db->rollback();
throw $e; // Re-throw exception untuk ditangkap oleh parent transaction
}
// Operasi database lain setelah nested transaction
$db->table('order_logs')->insert([
'order_id' => $orderId,
'action' => 'created',
'timestamp' => date('Y-m-d H:i:s')
]);
// Commit transaction utama
$db->commit();
return true;
} catch (\Exception $e) {
// Rollback transaction utama
$db->rollback();
return false;
}
Catatan: Tidak semua database mendukung nested transactions. Beberapa database seperti MySQL mengimplementasikan nested transactions dengan savepoints.
NexaUI menyediakan metode transaction
yang lebih sederhana dengan callback:
$db = $this->Storage();
// Menggunakan metode transaction dengan callback
$result = $db->transaction(function($db) {
// Semua operasi database dalam callback ini akan dijalankan dalam transaction
$db->table('users')->insert([
'name' => 'Jane Smith',
'email' => 'jane@example.com'
]);
$userId = $db->lastInsertId();
$db->table('profiles')->insert([
'user_id' => $userId,
'bio' => 'UX Designer',
'location' => 'Bandung'
]);
// Return true untuk commit, false untuk rollback
return true;
});
// $result akan berisi nilai return dari callback
if ($result) {
echo "Transaction berhasil";
} else {
echo "Transaction gagal";
}
Mengimplementasikan transactions dalam model untuk operasi yang kompleks:
class OrderModel extends NexaModel
{
protected $table = 'orders';
/**
* Membuat order baru dengan items
*
* @param array $orderData Data order
* @param array $items Data items
* @return array|bool Order data jika berhasil, false jika gagal
*/
public function createOrderWithItems(array $orderData, array $items)
{
$db = $this->Storage();
return $db->transaction(function($db) use ($orderData, $items) {
// Insert order
$db->table('orders')->insert($orderData);
$orderId = $db->lastInsertId();
// Insert order items
foreach ($items as $item) {
$item['order_id'] = $orderId;
$db->table('order_items')->insert($item);
}
// Update order total
$total = array_sum(array_map(function($item) {
return $item['price'] * $item['quantity'];
}, $items));
$db->table('orders')
->where('id', $orderId)
->update(['total' => $total]);
// Ambil data order lengkap
$order = $db->table('orders')->where('id', $orderId)->first();
$order['items'] = $db->table('order_items')->where('order_id', $orderId)->get();
return $order;
});
}
/**
* Update order dan items
*
* @param int $orderId ID order
* @param array $orderData Data order yang diupdate
* @param array $items Data items yang diupdate
* @return bool True jika berhasil, false jika gagal
*/
public function updateOrderWithItems(int $orderId, array $orderData, array $items)
{
$db = $this->Storage();
return $db->transaction(function($db) use ($orderId, $orderData, $items) {
// Update order
$db->table('orders')
->where('id', $orderId)
->update($orderData);
// Delete existing items
$db->table('order_items')
->where('order_id', $orderId)
->delete();
// Insert updated items
foreach ($items as $item) {
$item['order_id'] = $orderId;
$db->table('order_items')->insert($item);
}
// Update order total
$total = array_sum(array_map(function($item) {
return $item['price'] * $item['quantity'];
}, $items));
$db->table('orders')
->where('id', $orderId)
->update(['total' => $total]);
return true;
});
}
}
// Penggunaan
$orderModel = new OrderModel();
// Membuat order baru dengan items
$orderData = [
'customer_id' => 5,
'status' => 'pending',
'created_at' => date('Y-m-d H:i:s')
];
$items = [
[
'product_id' => 101,
'quantity' => 2,
'price' => 75.00
],
[
'product_id' => 102,
'quantity' => 1,
'price' => 50.00
]
];
$order = $orderModel->createOrderWithItems($orderData, $items);
if ($order) {
echo "Order #{$order['id']} berhasil dibuat dengan " . count($order['items']) . " items";
} else {
echo "Gagal membuat order";
}
class AccountModel extends NexaModel
{
protected $table = 'accounts';
/**
* Transfer dana antar rekening
*
* @param int $fromAccountId ID rekening pengirim
* @param int $toAccountId ID rekening penerima
* @param float $amount Jumlah yang ditransfer
* @return bool True jika berhasil, false jika gagal
*/
public function transferFunds(int $fromAccountId, int $toAccountId, float $amount)
{
if ($amount <= 0) {
throw new \InvalidArgumentException("Jumlah transfer harus lebih dari 0");
}
$db = $this->Storage();
return $db->transaction(function($db) use ($fromAccountId, $toAccountId, $amount) {
// Lock rekening pengirim untuk update (FOR UPDATE)
$fromAccount = $db->table('accounts')
->where('id', $fromAccountId)
->lockForUpdate()
->first();
if (!$fromAccount) {
return false;
}
// Cek saldo cukup
if ($fromAccount['balance'] < $amount) {
return false;
}
// Lock rekening penerima untuk update
$toAccount = $db->table('accounts')
->where('id', $toAccountId)
->lockForUpdate()
->first();
if (!$toAccount) {
return false;
}
// Kurangi saldo pengirim
$db->table('accounts')
->where('id', $fromAccountId)
->update(['balance' => $fromAccount['balance'] - $amount]);
// Tambah saldo penerima
$db->table('accounts')
->where('id', $toAccountId)
->update(['balance' => $toAccount['balance'] + $amount]);
// Catat transaksi
$db->table('transactions')->insert([
'from_account_id' => $fromAccountId,
'to_account_id' => $toAccountId,
'amount' => $amount,
'timestamp' => date('Y-m-d H:i:s')
]);
return true;
});
}
}
// Penggunaan
$accountModel = new AccountModel();
$success = $accountModel->transferFunds(1, 2, 500.00);
if ($success) {
echo "Transfer berhasil";
} else {
echo "Transfer gagal";
}