Services API beta
The plugin exposes two services: OrderLifecycleLogger for reading and writing log entries, and StatsService for computing store-wide metrics.
OrderLifecycleLogger
Access via:
use johnhenry\orderlifecycle\OrderLifecycle;
$logger = OrderLifecycle::getInstance()->logger;log()
Logs an order lifecycle event, capturing a full snapshot and optionally auto-generating a change description.
Signature:
public function log(
Order $order,
EventType $type,
array $payload = [],
?string $message = null
): voidParameters:
| Parameter | Type | Description |
|---|---|---|
$order | Order | The Craft Commerce order element |
$type | EventType | An EventType enum case |
$payload | array | Extra data stored in the snapshot's payload key |
$message | string|null | Custom message. If null, a message is auto-generated by diffing against the previous snapshot |
Example:
use johnhenry\orderlifecycle\OrderLifecycle;
use johnhenry\orderlifecycle\enums\EventType;
// Auto-generated message
OrderLifecycle::getInstance()->logger->log(
$order,
EventType::CART_UPDATED
);
// Custom message with extra payload data
OrderLifecycle::getInstance()->logger->log(
$order,
EventType::STATUS_CHANGED,
['triggeredBy' => 'webhook', 'source' => 'ERP'],
'Status updated via ERP sync'
);Throws: yii\db\Exception, yii\base\Exception, yii\base\InvalidConfigException
logCheckoutStarted()
Logs a checkoutStarted event for an order. Only records once per order - safe to call on every checkout page load.
Signature:
public function logCheckoutStarted(Order $order): boolReturns: true if the event was logged, false if already recorded for this order.
Example:
$logged = OrderLifecycle::getInstance()->logger->logCheckoutStarted($cart);getLogsForOrder()
Retrieves all log entries for an order, newest first.
Signature:
public function getLogsForOrder(int $orderId): arrayReturns: Array of log rows, each containing:
| Key | Type | Description |
|---|---|---|
id | int | Log entry ID |
orderId | int | Order ID |
type | string | Event type string (e.g. orderCompleted) |
message | string|null | Log message |
snapshot | string | JSON-encoded order snapshot |
userId | int|null | User who triggered the event |
ip | string|null | IP address (if collection enabled) |
dateCreated | string | ISO datetime string |
dateUpdated | string | ISO datetime string |
uid | string | GUID |
Example:
$logs = OrderLifecycle::getInstance()->logger->getLogsForOrder($order->id);
foreach ($logs as $log) {
echo $log['type'] . ': ' . $log['message'] . PHP_EOL;
echo 'At: ' . $log['dateCreated'] . PHP_EOL;
}getLastSnapshot()
Retrieves the decoded snapshot from the most recent log entry for an order.
Signature:
public function getLastSnapshot(int $orderId): ?arrayReturns: Decoded snapshot array or null if no logs exist.
Example:
$snapshot = OrderLifecycle::getInstance()->logger->getLastSnapshot($order->id);
if ($snapshot) {
$previousTotal = $snapshot['order']['totalPrice'];
$previousStatus = $snapshot['order']['statusHandle'];
}getLastLogForOrderAndType()
Retrieves the most recent log entry of a specific type for an order.
Signature:
public function getLastLogForOrderAndType(int $orderId, EventType $type): ?arrayReturns: Log row array or null.
Example:
use johnhenry\orderlifecycle\enums\EventType;
$lastPayment = OrderLifecycle::getInstance()->logger->getLastLogForOrderAndType(
$order->id,
EventType::PAYMENT_PROCESSED
);updateLog()
Updates fields on an existing log entry.
Signature:
public function updateLog(int $logId, array $updates): voidExample:
OrderLifecycle::getInstance()->logger->updateLog($logId, [
'message' => 'Updated message after retry',
]);Snapshot Structure
Every log() call captures the full order state:
[
'order' => [
'id' => 123,
'number' => 'abc123def456',
'isCompleted' => true,
'couponCode' => 'SAVE20',
'totalQty' => 3,
'totalPrice' => 99.99,
'currency' => 'EUR',
'statusId' => 2,
'statusHandle' => 'processing',
'shippingMethodHandle' => 'standardShipping',
'shippingMethodName' => 'Standard Shipping',
'shippingAddressId' => 456,
'billingAddressId' => 789,
],
'customer' => [
'email' => '[email protected]',
'isGuest' => false,
'customerId' => 789,
'userId' => 789,
],
'lineItems' => [
['sku' => 'PRODUCT-001', 'qty' => 2, 'subtotal' => 49.98],
],
'discounts' => [], // discount adjustments
'shippingAdjustments' => [], // shipping adjustments
'taxAdjustments' => [], // tax adjustments
'addresses' => [
'billingCountry' => 'IE',
'shippingCountry' => 'IE',
],
'payload' => [], // data passed to log() $payload parameter
]Auto-Generated Change Messages
When $message is null, the logger diffs the current snapshot against the previous one and generates a description. Examples:
'PRODUCT-001' added (qty: 2)
'PRODUCT-001' removed (was qty: 1)
'PRODUCT-001' quantity increased from 1 to 3
Total quantity changed from 2 to 3
Total price changed from 25.00 EUR to 49.99 EUR
Coupon code 'SAVE20' applied
Coupon code 'SAVE20' removed
Order status changed to 'processing'
Shipping method changed from Standard to Express
Shipping country changed to 'GB'
Email set to '[email protected]' (guest)Multiple changes are joined with semicolons.
Database Table
Logs are stored in the orderlifecycle_logs table (prefixed by Craft's table prefix):
| Column | Type | Notes |
|---|---|---|
id | INT PK | Auto-increment |
orderId | INT | FK → commerce_orders.id |
type | VARCHAR(255) | EventType string value |
message | TEXT | |
snapshot | LONGTEXT | JSON |
userId | INT | FK → users.id, nullable |
ip | VARCHAR(45) | IPv4/IPv6, nullable |
dateCreated | DATETIME | |
dateUpdated | DATETIME | |
uid | CHAR(36) |
Indexes: (orderId, dateCreated), dateCreated, userId, type.
Best Practices
- Use
EventTypeenum cases - don't pass raw strings tolog() - Let auto-detection handle messages - only pass
$messagewhen you need a specific custom description - Include relevant payload data - add context that will be useful for debugging or reporting
- Wrap in try/catch if logging must not block - log failures shouldn't interrupt the main order flow
try {
OrderLifecycle::getInstance()->logger->log($order, EventType::CART_UPDATED);
} catch (\Throwable $e) {
Craft::error('Failed to log order lifecycle: ' . $e->getMessage(), 'order-lifecycle');
}StatsService
The StatsService computes store-wide aggregate metrics from the orderlifecycle_logs table. It powers both the Overview dashboard page and the Order Lifecycle Stats widget.
Access via:
use johnhenry\orderlifecycle\OrderLifecycle;
$statsService = OrderLifecycle::getInstance()->stats;getStats()
Returns an array of store metrics for the specified number of days.
Signature:
public function getStats(int $days): arrayReturns:
| Key | Type | Description |
|---|---|---|
totalLogs | int | Total log entries in the period |
uniqueOrders | int | Orders with at least one log entry |
avgLogsPerOrder | float | Average events per order |
topEventTypes | array | Top 5 event types by count ([['type' => ..., 'count' => ...], ...]) |
avgTimeToCompletion | string|null | Average time from cartCreated to orderCompleted (e.g. "2h", "45m") |
conversionRate | float | Percentage of carts that completed |
cartsCreated | int | Unique orders with a cartCreated event |
ordersCompleted | int | Unique orders with an orderCompleted event |
avgCheckoutDuration | string|null | Average time from checkoutStarted to orderPaid |
abandonmentRate | float | Percentage of carts that did not complete |
abandonedCarts | int | Count of abandoned carts |
avgPaymentAttempts | float | Average paymentAttempt events per completed order |
returningCustomerRate | float | Percentage of completed orders from registered customers |
avgCartValue | float|null | Mean total price of completed orders |
emailSuccessRate | float | Email success rate as a percentage |
emailsSent | int | Total emailSent events |
emailsFailed | int | Total emailFailed events |
days | int | The $days parameter echoed back |
Example:
$stats = OrderLifecycle::getInstance()->stats->getStats(30);
echo "Conversion rate: {$stats['conversionRate']}%\n";
echo "Average cart value: {$stats['avgCartValue']}\n";
echo "Email success rate: {$stats['emailSuccessRate']}%\n";See Also
- Events API - All EventType cases and what triggers them
- Timeline View Guide - Viewing logs in the control panel
- Statistics Guide - Analysing logged data
- Export Guide - Exporting events to CSV