Looking to hire Laravel developers? Try LaraJobs

laravel-activity maintained by andydefer

Description
Polymorphic activity tracking for Laravel models with typed activity types, JSON data payloads, causer/subject relations, and query helpers.
Author
Last update
2026/09/26 18:48 (dev-main)
License
Links
Downloads
15

Comments
comments powered by Disqus

Laravel Activity

Système de journalisation d'activité polymorphique pour applications Laravel

Un package Laravel pour enregistrer, consulter et gérer les activités d'un modèle : inscription, connexion, création, paiement, etc. Chaque activité est typée par une chaîne libre et peut transporter des données structurées.


📋 Table des matières


✨ Fonctionnalités

  • ✅ Propriétaire polymorphique - Attachez une activité à n'importe quel modèle Eloquent
  • ✅ Type dynamique - Le type d'activité est une chaîne libre, pas une enum figée
  • ✅ Données structurées - Payload et metadata en JSON typés via StrictDataObject
  • ✅ Identifiant UUID - Chaque activité possède un UUID v4
  • ✅ Soft delete - Suppression non destructive avec restauration
  • ✅ Filtrage - Par propriétaire, type et plage de dates
  • ✅ Pagination et limite - Récupération contrôlée des activités
  • ✅ Table configurable - Nom de table personnalisable via la configuration
  • ✅ Pattern Repository - Séparation propre de l'accès aux données
  • ✅ DTOs typés - ActivityRecord et ActivityData pour les échanges

🚀 Prérequis

  • PHP 8.2 ou supérieur
  • Laravel 12.0, 13.0, 14.0 ou 15.0

📦 Installation

Installez le package via Composer :

composer require andydefer/laravel-activity

Publier la configuration et les migrations

php artisan vendor:publish --tag=activity-config
php artisan vendor:publish --tag=activity-migrations

Exécuter les migrations

php artisan migrate

⚙️ Configuration

Le fichier config/activity.php permet de contrôler le comportement du package :

<?php

declare(strict_types=1);

return [
    /*
    |--------------------------------------------------------------------------
    | Nom de la table
    |--------------------------------------------------------------------------
    |
    | Nom de la table utilisée pour stocker les activités.
    |
    */
    'table' => 'activities',

    /*
    |--------------------------------------------------------------------------
    | Alias morph
    |--------------------------------------------------------------------------
    |
    | Lorsque vrai, les types morph du propriétaire sont stockés en utilisant
    | l'alias morph (config('database.morph_map')) plutôt que le FQCN.
    |
    */
    'use_morph_alias' => false,

    /*
    |--------------------------------------------------------------------------
    | Rétention
    |--------------------------------------------------------------------------
    |
    | Nombre de jours pendant lesquels les activités sont conservées avant
    | d'être éligibles à la purge. Null désactive la purge.
    |
    */
    'retention_days' => null,
];

Le package est automatiquement découvert par Laravel. Aucun enregistrement manuel n'est nécessaire.


📖 Utilisation

Le service d'activité est injectable via ActivityServiceInterface.

Enregistrer une activité

use AndyDefer\LaravelActivity\Contracts\Services\ActivityServiceInterface;

final class RegistrationController extends Controller
{
    public function __construct(
        private readonly ActivityServiceInterface $activityService,
    ) {}

    public function register(Request $request)
    {
        $user = User::create($request->validated());

        $this->activityService->log(
            owner: $user,
            type: 'registered',
            description: 'User registered from the web form',
            data: ['ip' => $request->ip()],
            metadata: ['source' => 'web'],
        );

        return response()->json(['message' => 'Compte créé']);
    }
}

Récupérer les activités

// Toutes les activités du propriétaire
$activities = $this->activityService->getFor($user);

// Avec une limite
$latest = $this->activityService->getFor($user, limit: 10);

Récupérer par type

// Toutes les connexions de l'utilisateur
$logins = $this->activityService->getForByType($user, 'logged_in');

// Les 5 dernières connexions
$recentLogins = $this->activityService->getForByType($user, 'logged_in', limit: 5);

Récupérer la dernière activité

$lastActivity = $this->activityService->getLatestFor($user);

if ($lastActivity !== null) {
    echo $lastActivity->activity_type;
    echo $lastActivity->created_at;
}

Compter les activités

// Nombre total d'activités
$total = $this->activityService->countFor($user);

// Nombre d'activités d'un type donné
$logins = $this->activityService->countForByType($user, 'logged_in');

Vérifier l'existence d'un type

if ($this->activityService->hasActivityOfType($user, 'payment_completed')) {
    // L'utilisateur a au moins un paiement complété
}

Nettoyer les activités

// Supprime toutes les activités du propriétaire
$deleted = $this->activityService->clearFor($user);

🏷️ Types d'activité

Le type d'activité est une chaîne libre. Aucune enum n'est imposée par le package — vous pouvez utiliser les valeurs que vous voulez.

Les valeurs suivantes sont fréquemment utilisées comme convention :

Catégorie Valeurs suggérées
Cycle de vie created, updated, deleted, restored
Authentification logged_in, logged_out, registered, password_changed, email_verified
Interaction viewed, downloaded, shared, commented, liked, rated
Paiement payment_initiated, payment_completed, payment_failed, payment_refunded
Divers other

Une enum optionnelle est fournie à titre indicatif (AndyDefer\LaravelActivity\Enums\ActivityType) mais son usage n'est pas requis. Le stockage se fait toujours sous forme de chaîne, et toute valeur non prévue reste acceptée.


📚 Référence de l'API

ActivityServiceInterface

Méthode Description Retourne
log(Model $owner, string $type, ?string $description, ?array $data, ?array $metadata) Enregistre une activité Model
getFor(Model $owner, ?int $limit) Récupère les activités d'un propriétaire Collection
getForByType(Model $owner, string $type, ?int $limit) Récupère les activités filtrées par type Collection
getLatestFor(Model $owner) Récupère la dernière activité ?Model
countFor(Model $owner) Compte les activités d'un propriétaire int
countForByType(Model $owner, string $type) Compte les activités d'un type int
hasActivityOfType(Model $owner, string $type) Vérifie l'existence d'un type bool
clearFor(Model $owner) Supprime toutes les activités d'un propriétaire int

ActivityRepositoryInterface

Méthode Description Retourne
getFor(Model $owner, ?int $limit) Récupère les activités d'un propriétaire Collection
getForByType(Model $owner, string $type, ?int $limit) Récupère les activités filtrées par type Collection
getLatestFor(Model $owner) Récupère la dernière activité ?Activity
countFor(Model $owner) Compte les activités int
countForByType(Model $owner, string $type) Compte les activités par type int

🎯 Value Objects

Value Object Description Exemple
StrictDataObject Payload et metadata StrictDataObject::from(['key' => 'value'])
DateTimeZuluVO Date/heure UTC DateTimeZuluVO::from('2024-01-15T10:00:00Z')
UuidVO UUID v4 UuidVO::from('9b724dbf-...')

Accesseurs sur le modèle Activity

$activity = Activity::find($id);

// Propriétés typées
$activity->id;              // string (UUID)
$activity->owner_type;      // string
$activity->owner_id;        // string
$activity->activity_type;   // string
$activity->description;     // ?string

// Value Objects via accesseurs
$activity->data;            // ?StrictDataObject
$activity->metadata;        // ?StrictDataObject

// Relation polymorphique
$activity->owner;           // ?Model

// Timestamps
$activity->created_at;
$activity->updated_at;
$activity->deleted_at;

📝 Structure de la base de données

CREATE TABLE activities (
    id CHAR(36) PRIMARY KEY,               -- UUID v4
    owner_type VARCHAR(191) NOT NULL,      -- Type morph du propriétaire
    owner_id VARCHAR(191) NOT NULL,        -- Identifiant du propriétaire
    activity_type VARCHAR(191) NOT NULL,   -- Type d'activité (chaîne libre)
    description VARCHAR(255) NULL,         -- Description humaine
    data JSON NULL,                        -- Payload structuré
    metadata JSON NULL,                    -- Métadonnées additionnelles
    created_at TIMESTAMP NULL,
    updated_at TIMESTAMP NULL,
    deleted_at TIMESTAMP NULL,

    INDEX activities_owner_index (owner_type, owner_id),
    INDEX activities_activity_type_index (activity_type),
    INDEX activities_created_at_index (created_at)
);

🔍 Exemple complet

use AndyDefer\LaravelActivity\Contracts\Services\ActivityServiceInterface;
use Illuminate\Http\Request;

final class UserActivityController extends Controller
{
    public function __construct(
        private readonly ActivityServiceInterface $activityService,
    ) {}

    public function show(Request $request, User $user)
    {
        $activities = $this->activityService->getFor($user, limit: 20);
        $logins = $this->activityService->countForByType($user, 'logged_in');
        $lastActivity = $this->activityService->getLatestFor($user);

        return response()->json([
            'total' => $this->activityService->countFor($user),
            'logins' => $logins,
            'last_activity' => $lastActivity?->activity_type,
            'activities' => $activities->map(fn ($activity) => [
                'id' => $activity->id,
                'type' => $activity->activity_type,
                'description' => $activity->description,
                'data' => $activity->data?->toArray(),
                'created_at' => $activity->created_at,
            ]),
        ]);
    }

    public function destroy(User $user)
    {
        $deleted = $this->activityService->clearFor($user);

        return response()->json([
            'deleted' => $deleted,
        ]);
    }
}

🧪 Tests

Exécuter tous les tests

composer test

Exécuter uniquement les tests unitaires

composer test-unit

Exécuter uniquement les tests d'intégration

composer test-integration

Le package utilise orchestra/testbench avec une base de données SQLite en mémoire pour les tests d'intégration.


📦 Dépendances


👨‍💻 Auteur

Andy Kani


📄 Licence

Ce package est sous licence MIT. Voir le fichier LICENSE pour plus d'informations.