VOOZH about

URL: https://deepwiki.com/mathsgod/light/6.2-auto-auditing-and-lifecycle-hooks

⇱ Auto-Auditing and Lifecycle Hooks | mathsgod/light | DeepWiki


Loading...
Last indexed: 31 January 2026 (cf9511)
Menu

Auto-Auditing and Lifecycle Hooks

Purpose and Scope

This document explains the automatic auditing system built into the Light\Model base class, which tracks who created and modified each record, along with timestamps. The system uses lifecycle hooks (save() and delete()) to intercept all data modifications and log them to an audit trail.

For information about querying and restoring historical data captured by this audit system, see 6.3 Revision System. For details about the EventLog model structure and revision queries, see 6.3.

Overview of Audit Architecture

The audit system operates transparently through method interception in the base model class. Every model inheriting from Light\Model automatically participates in the audit trail without requiring explicit instrumentation.


Sources: src/Model.php1-239

Audit Field Auto-Population

The Light\Model class automatically maintains four standard audit fields when they exist in the database schema:

FieldTypePopulated OnPurpose
created_timedatetimeInsertTimestamp when record was created
created_byintInsertUser ID who created the record
updated_timedatetimeUpdateTimestamp of last modification
updated_byintUpdateUser ID who last modified the record

Field Population Logic

The save() method determines whether the operation is an insert or update by checking if the primary key value exists:


Sources: src/Model.php144-170

User Context Resolution

The system retrieves the current authenticated user through the dependency injection container:

  1. Check if self::$container is set (injected via Model::SetContainer())
  2. Retrieve the Auth\Service instance from the container
  3. Call getUser()?->user_id to get the current user's ID
  4. Use this ID to populate created_by or updated_by fields

The container is set by the Light\App class during initialization at src/App.php using Model::SetContainer($this->container).

Sources: src/Model.php146-151 src/Model.php79-82

GraphQL Field Exposure

The audit fields are exposed to the GraphQL API through dedicated resolver methods that provide user-friendly formatting:

MethodGraphQL FieldReturnsDescription
createdTime()createdTimestringISO datetime string
createdBy()createdBystringName of user who created the record
updatedTime()updatedTimestringISO datetime string
updatedBy()updatedBystringName of user who last updated

The *By() methods resolve user IDs to human-readable names by loading the User model:

Sources: src/Model.php42-70

Save Lifecycle Hook

The save() method in Light\Model intercepts all save operations to record a comprehensive audit trail in the EventLog table.

Save Operation Flow


Sources: src/Model.php144-238

Insert Action Logging

For insert operations:

  • action is set to "Insert"
  • source is null (no previous state)
  • target contains the complete serialized state of the new record

Sources: src/Model.php214-217

Update Action Logging

For update operations, the system captures both before and after states:

  1. Load source state: Retrieve the current database record using static::get($this->$key)
  2. Filter blob fields: Remove longblob, mediumblob, and tinyblob fields from both source and target to avoid storing large binary data
  3. Set action: action is set to "Update"
  4. Encode states: Both source (before) and target (after) are JSON-encoded

Sources: src/Model.php172-212

Blob Field Filtering

The audit system excludes binary large object (BLOB) fields from the audit trail to prevent excessive storage consumption:


Sources: src/Model.php176-209

Configuration Flags

The base model class defines three static properties that control audit logging behavior:


Child models can override these flags to disable logging for specific action types:

Sources: src/Model.php16-18 src/Model.php221-225

Delete Lifecycle Hook

The delete() method intercepts deletion operations to preserve the final state of the record before it is removed from the database.

Delete Operation Flow


Sources: src/Model.php120-142

Delete Action Characteristics

For delete operations:

  • action is always "Delete"
  • source is always null (no "before" state needed)
  • target contains the complete final state of the record before deletion
  • The EventLog entry is created before calling parent::delete() to ensure the audit trail is preserved even if deletion fails

This approach ensures that deleted records remain queryable through the audit trail, enabling data recovery and compliance with data retention policies.

Sources: src/Model.php131-139

EventLog Table Structure

All audit trail entries are stored in the EventLog table with the following structure:

ColumnTypeDescription
eventlog_idint (PK)Unique identifier for the audit entry
classstringFully-qualified class name of the model
idintPrimary key value of the affected record
actionenumOne of: "Insert", "Update", "Delete"
sourcejsonPrevious state (null for Insert/Delete)
targetjsonNew state (null for neither shown in code)
user_idint (FK)User who performed the action (nullable)
created_timedatetimeWhen the action occurred

EventLog Entry Examples

Insert Action:


Update Action:


Delete Action:


Sources: src/Model.php131-139 src/Model.php227-235

Revision System Integration

The EventLog entries created by the lifecycle hooks form the foundation for the revision system. The Light\Type\Revision class wraps EventLog records to provide:

  • Historical state access via getContent() method
  • Delta calculation between source and target states
  • Field-level restoration through restoreFields() method

The RevisionController provides GraphQL mutations and queries:

  • getRevisionsByModel(model_class, model_id) - Query all revisions for a specific record
  • restoreRevision(revision_id, fields) - Restore specific fields from a historical state

Sources: src/Controller/RevisionController.php1-43 src/Type/Revision.php1-119

Permission Checking Methods

The base model class provides three permission-checking methods exposed to GraphQL that can be overridden by child classes to implement custom authorization logic:

MethodGraphQL FieldDefaultPurpose
canDelete()canDeletetrueCheck if user can delete this record
canUpdate()canUpdatetrueCheck if user can update this record
canView()canViewtrueCheck if user can view this record

Each method receives the current user through the #[InjectUser] annotation and can implement permission checks based on RBAC roles or business logic.

Sources: src/Model.php73-117

Data Sanitization

The audit system uses Light\Util::Sanitize() to clean data before JSON encoding. This utility function ensures that sensitive or problematic data is properly handled before storage in the audit trail.

Sources: src/Model.php136 src/Model.php196 src/Model.php211 src/Model.php216

Summary

The auto-auditing system in Light provides comprehensive, zero-configuration audit trails for all models:

  1. Automatic Field Population: created_by, created_time, updated_by, updated_time are maintained transparently
  2. Complete Change Tracking: Every insert, update, and delete is logged to EventLog with full state snapshots
  3. User Attribution: All changes are associated with the authenticated user from Auth\Service
  4. Binary Data Handling: BLOB fields are automatically filtered to prevent storage bloat
  5. Configurable Logging: Static flags allow selective enabling/disabling of audit logging per action type
  6. GraphQL Exposure: Audit fields are automatically available in the GraphQL API with user-friendly formatting
  7. Revision Restoration: Historical states can be queried and selectively restored via the revision system

This architecture ensures data integrity, regulatory compliance, and operational transparency without requiring developers to explicitly instrument their models.