Skip to main content
Version: 4.x

Entry Model Reference

Understand the structure of audit log entries

The Entry class represents a single audit log entry. This page documents all its properties and methods.

🔍 Overview

namespace DH\Auditor\Model;

final class Entry
{
// Created from database row
public static function fromArray(array $row): self;

// Accessors for all properties
public function getId(): ?int;
public function getType(): string;
public function getObjectId(): string;
// ... etc
}

🏗️ Creating Entries

Entries are typically created by the Reader when executing queries:

$reader = new Reader($provider);
$query = $reader->createQuery(User::class);
$entries = $query->execute(); // Returns Entry[]

You can also create entries manually (useful for testing):

use DH\Auditor\Model\Entry;

$entry = Entry::fromArray([
'id' => 1,
'type' => 'update',
'object_id' => '123',
'discriminator' => null,
'transaction_hash' => 'abc123',
'diffs' => '{"name":{"old":"John","new":"Jane"}}',
'extra_data' => '{"department":"IT","role":"admin"}', // or null
'blame_id' => '42',
'blame_user' => 'admin',
'blame_user_fqdn' => 'App\\Entity\\User',
'blame_user_firewall' => 'main',
'ip' => '192.168.1.1',
'created_at' => new \DateTimeImmutable(),
]);

📋 Properties & Methods

getId()

public function getId(): ?int

Returns the unique identifier of the audit entry (auto-increment primary key).

$entryId = $entry->getId();
// Returns: 12345

getType()

public function getType(): string

Returns the type of action that was audited.

$type = $entry->getType();
// Returns: 'insert', 'update', 'remove', 'associate', or 'dissociate'
TypeDescription
insertA new entity was created
updateAn existing entity was modified
removeAn entity was deleted
associateA many-to-many relationship was created
dissociateA many-to-many relationship was removed

getObjectId()

public function getObjectId(): string

Returns the ID of the audited entity.

$entityId = $entry->getObjectId();
// Returns: '123' (always string)

getDiscriminator()

public function getDiscriminator(): ?string

Returns the entity class name for single-table inheritance entities.

$discriminator = $entry->getDiscriminator();
// Returns: 'App\Entity\Admin' or null

This is used when auditing entities with SINGLE_TABLE inheritance to identify the actual entity class.

getTransactionHash()

public function getTransactionHash(): ?string

Returns the transaction hash that groups related changes.

$hash = $entry->getTransactionHash();
// Returns: 'a1b2c3d4e5f6...' (40 character hash)

All changes made in a single EntityManager::flush() share the same transaction hash.

getDiffs()

public function getDiffs(bool $includeMetadata = false): array

Returns the actual changes recorded for this audit entry.

// Basic usage
$diffs = $entry->getDiffs();

// Include @source metadata
$diffs = $entry->getDiffs(true);

Insert Diffs

// When type = 'insert'
[
'email' => [
'new' => 'john@example.com',
'old' => null,
],
'name' => [
'new' => 'John Doe',
'old' => null,
],
'roles' => [
'new' => ['ROLE_USER'],
'old' => null,
],
]

Update Diffs

// When type = 'update'
[
'name' => [
'new' => 'Jane Doe',
'old' => 'John Doe',
],
'email' => [
'new' => 'jane@example.com',
'old' => 'john@example.com',
],
]

Remove Diffs

// When type = 'remove'
// Contains a summary of the deleted entity
[
'id' => 123,
'label' => 'John Doe', // Generated from entity's __toString or ID
'class' => 'App\\Entity\\User',
]

Association Diffs

// When type = 'associate' or 'dissociate'
[
'source' => [
'class' => 'App\\Entity\\Post',
'id' => 1,
'label' => 'My Blog Post',
'field' => 'tags',
],
'target' => [
'class' => 'App\\Entity\\Tag',
'id' => 5,
'label' => 'PHP',
'field' => 'posts',
],
'is_owning_side' => true,
'table' => 'post_tag', // The join table
]

With Metadata

$diffs = $entry->getDiffs(true);
// Includes @source key with additional metadata
[
'@source' => [
// Source information
],
'name' => [
'new' => 'Jane',
'old' => 'John',
],
]

getExtraData()

public function getExtraData(): ?array

Returns any supplementary data attached to this audit entry, or null if none was set.

TIP

Extra data is populated via a LifecycleEvent listener. See the Extra Data guide for setup instructions and examples.

$extraData = $entry->getExtraData();
// Returns: ['department' => 'IT', 'role' => 'admin'] or null

// Also available as a virtual property
$extraData = $entry->extraData;

Extra data is populated via a LifecycleEvent listener. See the Extra Data guide for setup instructions and examples.

getUserId()

public function getUserId(): int|string|null

Returns the identifier of the user who made the change.

$userId = $entry->getUserId();
// Returns: 42 or '42' or 'user-uuid-123' or null

Returns null if no user was identified (e.g., system process, CLI command without user context).

getUsername()

public function getUsername(): ?string

Returns the username/display name of the user.

$username = $entry->getUsername();
// Returns: 'admin' or 'John Doe' or null

getUserFqdn()

public function getUserFqdn(): ?string

Returns the fully qualified class name of the user object.

$userClass = $entry->getUserFqdn();
// Returns: 'App\Entity\User' or null

getUserFirewall()

public function getUserFirewall(): ?string

Returns the Symfony security firewall name.

$firewall = $entry->getUserFirewall();
// Returns: 'main' or 'api' or null

getIp()

public function getIp(): ?string

Returns the client IP address.

$ip = $entry->getIp();
// Returns: '192.168.1.1' or '2001:db8::1' (IPv6) or null

getCreatedAt()

public function getCreatedAt(): ?\DateTimeImmutable

Returns when the audit entry was created.

$createdAt = $entry->getCreatedAt();
// Returns: DateTimeImmutable

echo $createdAt->format('Y-m-d H:i:s');
// Output: 2024-01-15 14:30:45

The timestamp uses the timezone configured in the Auditor configuration.

💡 Working with Entries

Display Audit Log

$entries = $query->execute();

foreach ($entries as $entry) {
printf(
"[%s] %s %s #%s by %s (%s)\n",
$entry->getCreatedAt()->format('Y-m-d H:i:s'),
ucfirst($entry->getType()),
$entry->getDiscriminator() ?? 'Entity',
$entry->getObjectId(),
$entry->getUsername() ?? 'System',
$entry->getIp() ?? 'N/A'
);

foreach ($entry->getDiffs() as $field => $change) {
if (isset($change['old'], $change['new'])) {
printf(
" - %s: %s → %s\n",
$field,
json_encode($change['old']),
json_encode($change['new'])
);
}
}
echo "\n";
}

Check Change Type

if ($entry->getType() === 'update') {
$changes = $entry->getDiffs();

// Check if specific field changed
if (isset($changes['email'])) {
echo sprintf(
"Email changed from %s to %s\n",
$changes['email']['old'],
$changes['email']['new']
);
}
}

Group by Transaction

$entries = $query->execute();
$grouped = [];

foreach ($entries as $entry) {
$hash = $entry->getTransactionHash();
if (!isset($grouped[$hash])) {
$grouped[$hash] = [];
}
$grouped[$hash][] = $entry;
}

// Now $grouped contains all changes per transaction
foreach ($grouped as $hash => $transactionEntries) {
echo "Transaction: $hash\n";
foreach ($transactionEntries as $entry) {
echo " - " . $entry->getType() . " on #" . $entry->getObjectId() . "\n";
}
}

Filter Changes by Field

// Find all entries where 'status' field changed
$statusChanges = array_filter($entries, function (Entry $entry) {
return isset($entry->getDiffs()['status']);
});

foreach ($statusChanges as $entry) {
$diffs = $entry->getDiffs();
echo sprintf(
"Status changed from '%s' to '%s' at %s\n",
$diffs['status']['old'],
$diffs['status']['new'],
$entry->getCreatedAt()->format('Y-m-d H:i:s')
);
}

Build Timeline

// Build a timeline of changes for an entity
$query = $reader->createQuery(User::class, [
'object_id' => 123,
'page_size' => null,
]);
$query->resetOrderBy();
$query->addOrderBy(Query::CREATED_AT, 'ASC');

$timeline = [];
foreach ($query->execute() as $entry) {
$timeline[] = [
'date' => $entry->getCreatedAt(),
'type' => $entry->getType(),
'user' => $entry->getUsername(),
'changes' => $entry->getDiffs(),
];
}

📄 JSON Serialization

NOTE

The diffs are stored as JSON and automatically decoded - no need to call json_decode().

// Diffs are returned as arrays, not JSON strings
$diffs = $entry->getDiffs();
// This is already an array, no need to json_decode()

// To get JSON for API response:
$json = json_encode([
'id' => $entry->getId(),
'type' => $entry->getType(),
'object_id' => $entry->getObjectId(),
'diffs' => $entry->getDiffs(),
'extra_data' => $entry->getExtraData(),
'created_at' => $entry->getCreatedAt()?->format('c'),
'user' => $entry->getUsername(),
]);

Next Steps