precision-soft/symfony-doctrine-audit

doctrine audit library

Maintainers

Package info

github.com/precision-soft/symfony-doctrine-audit

Homepage

Type:symfony-bundle

pkg:composer/precision-soft/symfony-doctrine-audit

Statistics

Installs: 20

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

v2.1.0 2025-01-06 08:00 UTC

This package is auto-updated.

Last update: 2026-03-18 07:45:36 UTC


README

You may fork and modify it as you wish.

Any suggestions are welcomed.

This is a work in progress.

Usage

Sample config and storage

precision_soft_doctrine_audit:
    storages:
        doctrine_one:
            type: doctrine
            entity_manager: audit_em_one
            config: # \PrecisionSoft\Doctrine\Audit\Storage\Doctrine\Configuration
                transaction_table_name: 'audit_transaction'
        file:
            type: file
            file: '%kernel.project_dir%/var/audit.log'
        doctrine_two:
            type: doctrine
            entity_manager: audit_em_two
            config: # \PrecisionSoft\Doctrine\Audit\Storage\Doctrine\Configuration
                transaction_table_name: 'audit_transaction'
        rabbit:
            type: custom
            service: Acme\Shared\Service\AuditStorageService
    auditors:
        doctrine:
            entity_manager: source_em_one
            storages:
                - doctrine
            transaction_provider: Acme\Shared\Service\AuditTransactionProviderService
            logger: monolog.logger
            ignored_fields:
                - created
                - modified
        file:
            entity_manager: source_em_two
            storages:
                - file
            transaction_provider: Acme\Shared\Service\AuditTransactionProviderService
        async:
            entity_manager: source_em_three
            storages:
                - doctrine_two
                - rabbit
            synchronous_storages:
                - rabbit # the rabbit storage will publish the storage dto and a consumer will be required to save to the doctrine storage
            transaction_provider: Acme\Shared\Service\AuditTransactionProviderService
# services.yaml
services:
    Acme\Shared\Service\AuditStorageService:
        arguments:
            $storage: '@precision_soft_doctrine_audit.storage.doctrine_two'
<?php

declare(strict_types=1);

namespace Acme\Shared\Service;

use PrecisionSoft\Doctrine\Audit\Contract\TransactionProviderInterface;
use PrecisionSoft\Doctrine\Audit\Dto\Storage\TransactionDto;

final class AuditTransactionProviderService implements TransactionProviderInterface
{
    public function getTransaction(): TransactionDto
    {
        $username = '~';

        /* @todo implement application specific logic */

        return new TransactionDto($username);
    }
}
<?php

declare(strict_types=1);

namespace Acme\Shared\Service;

use PrecisionSoft\Doctrine\Audit\Contract\StorageInterface;
use PrecisionSoft\Doctrine\Audit\Dto\Storage\StorageDto;
use PrecisionSoft\Doctrine\Audit\Storage\Doctrine\Storage;
use OldSound\RabbitMqBundle\RabbitMq\ProducerInterface;
use PhpAmqpLib\Message\AMQPMessage;
use Psr\Log\LoggerInterface;
use Symfony\Component\Serializer\Encoder\JsonEncoder;
use Symfony\Component\Serializer\SerializerInterface;
use Throwable;

class AuditStorageService implements StorageInterface
{
    private SerializerInterface $serializer;
    private Storage $storage;
    private ProducerInterface $producer;
    private LoggerInterface $logger;
    private ThrowableHandlerService $throwableHandlerService;

    public function __construct(
        SerializerInterface $serializer,
        Storage $storage,
        ProducerInterface $auditProducer,
        LoggerInterface $logger,
        ThrowableHandlerService $throwableHandlerService
    ) {
        $this->serializer = $serializer;
        $this->storage = $storage;
        $this->producer = $auditProducer;
        $this->logger = $logger;
        $this->throwableHandlerService = $throwableHandlerService;
    }

    public function save(StorageDto $storageDto): void
    {
        try {
            $message = $this->serializer->serialize($storageDto, JsonEncoder::FORMAT);

            $this->producer->publish($message);
        } catch (Throwable $t) {
            $context = $this->throwableHandlerService->getContext($t);
            /* @todo serialize by hand */
            $context['dto'] = $message ?? 'could not serialize';

            $this->logger->error($t->getMessage(), $context);
        }
    }

    public function consume(AMQPMessage $message): void
    {
        /** @var StorageDto $storageDto */
        $storageDto = $this->serializer->deserialize($message->getBody(), StorageDto::class, JsonEncoder::FORMAT);

        $this->storage->save($storageDto);
    }
}

Doctrine storage

This library will register two commands for each auditor with a doctrine type storage:

  • precision-soft:doctrine:audit:schema:create:<em-name> - will create the audit database schema for auditor default.
  • precision-soft:doctrine:audit:schema:update:<em-name> - will update the audit database schema for auditor default.

Upgrading

v2.x → v3.0

getOperation() returns Operation enum instead of string

use PrecisionSoft\Doctrine\Audit\Dto\Operation;

/* before */
$entity->getOperation() === 'delete'

/* after */
$entity->getOperation() === Operation::Delete
/* or, if you need the string value */
$entity->getOperation()->value === 'delete'

OPERATION_* constants removed from AbstractEntityDto

Replace any references to AbstractEntityDto::OPERATION_DELETE / OPERATION_INSERT / OPERATION_UPDATE / OPERATIONS with Operation::Delete / Insert / Update and Operation::values().

FileStorage JSONL format changed

  • Each entity now includes an operation field.
  • UPDATE fields that have changed are serialized as {"old": ..., "new": ...} instead of a plain value.

Dev

git clone git@github.com:precision-soft/symfony-doctrine-audit.git
cd symfony-doctrine-audit

./dc build && ./dc up -d

Inspired by