Skip to content

Commit c39ced7

Browse files
committed
docs(database): add transaction side-effect recipes
- explain why external side effects should be deferred until commit - add afterCommit and afterRollback examples - clarify Model callbacks are not commit-aware by themselves Signed-off-by: memleakd <121398829+memleakd@users.noreply.github.com>
1 parent 2cc937a commit c39ced7

3 files changed

Lines changed: 54 additions & 0 deletions

File tree

user_guide_src/source/database/transactions.rst

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,31 @@ This is useful for side effects that should only happen after committed data is
180180
visible, such as dispatching a queued job or sending a notification, and for
181181
cleanup that should only happen after a real rollback.
182182

183+
Deferring Side Effects
184+
----------------------
185+
186+
Code that changes external state should usually not run in the middle of a
187+
transaction. If the transaction rolls back after the side effect has already
188+
run, the application may send a notification for data that was never saved,
189+
invalidate a cache for a write that did not persist, or start background work
190+
that cannot find the committed row it expects.
191+
192+
Register those side effects with ``afterCommit()`` instead:
193+
194+
.. literalinclude:: transactions/013.php
195+
196+
Use ``afterRollback()`` for cleanup that should happen only when the transaction
197+
does not commit, such as removing a temporary file created before the database
198+
write:
199+
200+
.. literalinclude:: transactions/014.php
201+
202+
Model callbacks such as ``afterInsert`` and ``afterUpdate`` run after the Model
203+
query has executed, but not necessarily after the surrounding transaction has
204+
committed. If a Model callback needs to run a side effect only after commit, it
205+
should register that work with the database connection's ``afterCommit()``
206+
method while an active transaction is already open.
207+
183208
Callbacks run after the database transaction has already committed or rolled
184209
back. If a callback throws an exception, that exception bubbles to the caller,
185210
but the transaction outcome is not changed.
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<?php
2+
3+
use CodeIgniter\Events\Events;
4+
5+
$orderId = $this->db->transaction(static function ($db) use ($order): int {
6+
$db->table('orders')->insert($order);
7+
$orderId = $db->insertID();
8+
9+
$db->afterCommit(static function () use ($orderId): void {
10+
service('cache')->delete('orders_list');
11+
Events::trigger('order_created', $orderId);
12+
13+
// Dispatch a queued job or send a notification here.
14+
// The new order is committed and visible to other database connections.
15+
});
16+
17+
return $orderId;
18+
});
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<?php
2+
3+
$this->db->transaction(static function ($db) use ($temporaryPath, $record): void {
4+
$db->afterRollback(static function () use ($temporaryPath): void {
5+
if (is_file($temporaryPath)) {
6+
unlink($temporaryPath);
7+
}
8+
});
9+
10+
$db->table('documents')->insert($record);
11+
});

0 commit comments

Comments
 (0)