andydefer / laravel-activity
Polymorphic activity tracking for Laravel models with typed activity types, JSON data payloads, causer/subject relations, and query helpers.
Requires
- php: ^8.2
- andydefer/laravel-repository: ^2.17.21
- laravel/framework: ^12.0|^13.0|^14.0|^15.0
Requires (Dev)
- barryvdh/laravel-ide-helper: ^3.6
- composer/composer: ^2.0
- larastan/larastan: ^3.8
- laravel/pint: ^1.26
- orchestra/testbench: ^10.8
- phpunit/phpunit: ^12.5
- rector/rector: *
- symfony/var-dumper: ^7.0
- vimeo/psalm: ^6.14
Suggests
None
Provides
None
Conflicts
None
Replaces
None
README
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
- Prérequis
- Installation
- Configuration
- Utilisation
- Types d'activité
- Référence de l'API
- Value Objects
- Structure de la base de données
- Tests
- Licence
✨ 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 -
ActivityRecordetActivityDatapour 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
andydefer/laravel-repository- Implémentation du pattern Repositoryandydefer/php-vo- Value Objectsandydefer/domain-structures- Structures de domaine
👨💻 Auteur
Andy Kani
- GitHub: @andydefer
- Email: andykanidimbu@gmail.com
📄 Licence
Ce package est sous licence MIT. Voir le fichier LICENSE pour plus d'informations.