Weave Code
Code Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Avanaudit Bundle Laravel Package

cris/avanaudit-bundle

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Installation

    composer require cris/avanaudit-bundle
    

    (Note: The README mentions data-dog/audit-bundle, but the repo URL points to cris/avanaudit-bundle. Use the latter.)

  2. Enable the Bundle Add to config/bundles.php:

    return [
        // ...
        Cris\AvanauditBundle\CrisAvanauditBundle::class => ['all' => true],
    ];
    
  3. Database Migration Run:

    php bin/console doctrine:migrations:diff
    php bin/console doctrine:migrations:migrate
    

    This auto-generates the audit_entry table.

  4. First Audit Log Trigger an ORM operation (e.g., User::create() or EntityManager::persist()). Check logs or the demo route (/audit) to verify entries.


Implementation Patterns

Core Workflow

  1. Automatic Tracking

    • All Doctrine ORM operations (insert/update/delete) are audited without manual annotations.
    • Example: Updating a User entity:
      $user = $entityManager->find(User::class, 1);
      $user->setEmail('new@example.com');
      $entityManager->flush(); // Audit log captured here.
      
  2. Relation Tracking

    • Many-to-many/many-to-one changes are logged with ASSOCIATED/DISSOCIATED actions.
    • Example: Adding a Role to a User:
      $user->addRole($role);
      $entityManager->flush(); // Logs role association.
      
  3. User Context

    • If Security bundle is enabled, logs link to the authenticated user via TokenStorage.
  4. Transaction Safety

    • Audit entries are inserted within the same transaction. Rollbacks preserve data integrity.

Integration Tips

  • Custom Fields Extend the AuditEntry entity to add metadata (e.g., ip_address):

    // src/Entity/AuditEntry.php
    namespace App\Entity;
    use Cris\AvanauditBundle\Entity\AuditEntry as BaseAuditEntry;
    
    class AuditEntry extends BaseAuditEntry {
        private $ipAddress;
        // ...
    }
    
  • Filtering Logs Use Doctrine queries to filter by entity, action, or user:

    $auditEntries = $entityManager->getRepository(AuditEntry::class)
        ->findBy(['entityClass' => User::class, 'action' => 'UPDATE']);
    
  • Event Listeners Subscribe to audit.log events for custom logic:

    // src/EventListener/AuditListener.php
    use Cris\AvanauditBundle\Event\AuditEvent;
    
    class AuditListener {
        public function onAudit(AuditEvent $event) {
            if ($event->getAction() === 'DELETE') {
                // Trigger cleanup logic.
            }
        }
    }
    

    Register in services.yaml:

    services:
        App\EventListener\AuditListener:
            tags:
                - { name: kernel.event_listener, event: audit.log, method: onAudit }
    
  • Demo Route Use /audit (from the demo) for debugging or expose via API:

    // src/Controller/AuditController.php
    use Cris\AvanauditBundle\Entity\AuditEntry;
    
    class AuditController extends AbstractController {
        public function index(EntityManagerInterface $em): Response {
            $audits = $em->getRepository(AuditEntry::class)->findAll();
            return $this->render('audit/index.html.twig', ['audits' => $audits]);
        }
    }
    

Gotchas and Tips

Pitfalls

  1. No DQL/SQL Tracking

    • Issue: Direct queries ($em->createQuery('UPDATE ...') or raw SQL) bypass the audit.
    • Fix: Use ORM methods ($entityManager->persist()) or wrap SQL in a custom listener.
  2. Performance Overhead

    • Issue: Audit logs add latency during flush().
    • Fix:
      • Disable for bulk operations:
        $entityManager->getConnection()->getConfiguration()->setSQLLogger(null);
        // ... bulk operations ...
        $entityManager->flush();
        
      • Use async processing (e.g., Symfony Messenger) for non-critical audits.
  3. Missing User Context

    • Issue: Logs show null for user_id if no authenticated user exists.
    • Fix: Ensure Security bundle is configured and TokenStorage is available.
  4. Relation Diffs

    • Issue: Complex relations (e.g., nested collections) may not diff clearly.
    • Fix: Override getRelationDiff() in a custom AuditEntry:
      public function getRelationDiff($field, $oldValue, $newValue) {
          if ($field === 'orders') {
              return "Orders changed from {$oldValue->count()} to {$newValue->count()} items.";
          }
          return parent::getRelationDiff($field, $oldValue, $newValue);
      }
      

Debugging Tips

  1. Enable SQL Logging Add to .env:

    DOCTRINE_DDL_AUTO_CREATE_SQL=true
    DOCTRINE_DDL_AUTO_CREATE_SQL_LOG_FILE=audit_ddl.log
    
  2. Check Event Dispatching Verify audit.log events fire by adding a debug listener:

    public function onAudit(AuditEvent $event) {
        error_log('Audit triggered: ' . $event->getAction());
    }
    
  3. Inspect AuditEntry Table Query raw logs to debug:

    SELECT * FROM audit_entry WHERE entity_class = 'App\Entity\User' ORDER BY created_at DESC LIMIT 10;
    

Extension Points

  1. Custom Actions Add new actions (e.g., SOFT_DELETE) by extending AuditEntry:

    // src/Entity/AuditEntry.php
    const SOFT_DELETE = 'SOFT_DELETE';
    // ...
    
  2. Pre/Post Audit Logic Use Doctrine lifecycle callbacks:

    // src/Entity/User.php
    use Doctrine\ORM\Mapping as ORM;
    use Cris\AvanauditBundle\Event\AuditEvent;
    
    #[ORM\Entity]
    class User {
        #[ORM\PrePersist]
        public function prePersist() {
            if (!$this->getEmail()) {
                throw new \RuntimeException("Email required for audit.");
            }
        }
    }
    
  3. Exclude Entities Skip auditing for specific entities by implementing AuditExclusionInterface:

    use Cris\AvanauditBundle\Contract\AuditExclusionInterface;
    
    class TempData implements AuditExclusionInterface {}
    
  4. Custom Storage Override the AuditEntryRepository to store logs in a queue or external service:

    // src/Repository/AuditEntryRepository.php
    use Cris\AvanauditBundle\Repository\AuditEntryRepository as BaseRepository;
    
    class AuditEntryRepository extends BaseRepository {
        public function save(AuditEntry $entry) {
            // Send to RabbitMQ/Redis instead of DB.
        }
    }
    
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
emuniq/filament-browser-notifications
syriable/filament-translator
hungnm28/livewire-form
wenprise/eloquent
crudly/encrypted
fadion/bouncy
cuci/prototurk-sdk
gos/pubsub-router-bundle
cuci/prototurk-sdk-symfony
clementtalleu/easyadmin-markdown-bundle
codeflextech/permission-manager
karnoweb/livewire-datepicker
sayedenam/sayed-dashboard
milito/query-filter
apiboxsym/user-bundle
apiboxsym/health-check-bundle
jayeshmepani/jpl-moshier-ephemeris-php
elnasnato/laraliveui
labrodev/rest-sdk
sampaui/sampaui