jimmyahalpara/laravel-activitylog-enricher 问题修复 & 功能扩展

解决BUG、新增功能、兼容多环境部署,快速响应你的开发需求

邮箱:yvsm@zunyunkeji.com | QQ:316430983 | 微信:yvsm316

jimmyahalpara/laravel-activitylog-enricher

Composer 安装命令:

composer require jimmyahalpara/laravel-activitylog-enricher

包简介

A Laravel package to enrich Spatie ActivityLog entries by resolving foreign key IDs to readable labels

README 文档

README

Latest Version on Packagist Total Downloads GitHub Tests Action Status PHP Stan

A Laravel package that enriches Spatie ActivityLog entries by resolving foreign key IDs to human-readable labels, making your audit trails more meaningful and user-friendly.

Features

  • 🔄 Automatic Foreign Key Resolution: Convert foreign key IDs to readable labels
  • 🏗️ Nested Array Support: Handle complex nested structures like items.*.material_id
  • 🎯 Flexible Label Sources: Support for model attributes, accessor methods, or custom methods
  • 🗂️ Predefined Configurations: Reusable mapping configurations via config file
  • 🛡️ Soft Delete Awareness: Handles soft-deleted models gracefully
  • 🚫 Error Resilient: Continues enrichment even when some models are missing
  • 🧪 Fully Tested: Comprehensive test suite with 100% coverage
  • 📋 Type Safe: Full PHP 8.1+ type declarations and PHPStan level 8 compliance

Requirements

  • PHP 8.2 or higher
  • Laravel 9.0, 10.0, 11.0, or 12.0
  • Spatie Laravel ActivityLog 4.0+

Installation

Install the package via Composer:

composer require jimmyahalpara/laravel-activitylog-enricher

The package will automatically register its service provider.

Optionally, publish the configuration file:

php artisan vendor:publish --provider="JimmyAhalpara\ActivityLogEnricher\ActivityLogEnricherServiceProvider" --tag="activity-log-enricher-config"

Quick Start

Basic Usage

Use the enricher in your model's tapActivity method:

<?php

use JimmyAhalpara\ActivityLogEnricher\Facades\ActivityLogEnricher;
use Spatie\Activitylog\LogOptions;
use Spatie\Activitylog\Models\Activity;
use Spatie\Activitylog\Traits\LogsActivity;

class Job extends Model
{
    use LogsActivity;

    public function tapActivity(Activity $activity, string $eventName): void
    {
        ActivityLogEnricher::enrichActivity($activity, [
            'customer_id' => [
                'class' => Contact::class,
                'label_attribute' => 'name',
                'new_key' => 'customer_name',
            ],
            'design_id' => [
                'class' => Design::class,
                'label_attribute' => 'title',
                'new_key' => 'design_title',
            ],
        ]);
    }

    public function getActivitylogOptions(): LogOptions
    {
        return LogOptions::defaults()
            ->logAll()
            ->logOnlyDirty()
            ->dontSubmitEmptyLogs();
    }
}

Before and After Comparison

Before enrichment:

{
  "old": {
    "customer_id": 1,
    "design_id": 5
  },
  "attributes": {
    "customer_id": 2,
    "design_id": 5
  }
}

After enrichment:

{
  "old": {
    "customer_id": 1,
    "customer_name": "ABC Corporation",
    "design_id": 5,
    "design_title": "Premium Package Design"
  },
  "attributes": {
    "customer_id": 2,
    "customer_name": "XYZ Industries",
    "design_id": 5,
    "design_title": "Premium Package Design"
  }
}

Configuration Options

Field Mapping Configuration

Each field mapping supports the following options:

'foreign_key_name' => [
    'class' => ModelClass::class,           // Required: The model class to resolve
    'label_attribute' => 'attribute_name',  // Optional: Attribute/method for label (default: 'label')
    'new_key' => 'enriched_key_name',       // Optional: New key name (default: guessed from foreign_key)
]

Label Attribute Resolution

The enricher tries multiple methods to resolve labels in this order:

  1. Direct method call: $model->getLabelWithoutClass()
  2. Accessor method: $model->getLabelAttribute()
  3. Direct attribute: $model->label
  4. Appended attribute: If label is in the model's $appends array
  5. Fallback: (string) $model

Advanced Usage

Nested Array Enrichment

Perfect for enriching line items or nested structures:

ActivityLogEnricher::enrichActivity($activity, [
    'items.*.product_id' => [
        'class' => Product::class,
        'label_attribute' => 'name',
        'new_key' => 'product_name',
    ],
    'items.*.category_id' => [
        'class' => Category::class,
        'label_attribute' => 'title',
        'new_key' => 'category_title',
    ],
]);

Input:

{
  "attributes": {
    "items": [
      {"product_id": 1, "category_id": 2, "quantity": 5},
      {"product_id": 3, "category_id": 1, "quantity": 2}
    ]
  }
}

Output:

{
  "attributes": {
    "items": [
      {
        "product_id": 1,
        "product_name": "Laptop Computer",
        "category_id": 2,
        "category_title": "Electronics",
        "quantity": 5
      },
      {
        "product_id": 3,
        "product_name": "Office Chair",
        "category_id": 1,
        "category_title": "Furniture",
        "quantity": 2
      }
    ]
  }
}

Using Predefined Configurations

Define reusable configurations in config/activity-log-enricher.php:

'mappings' => [
    'orders' => [
        'customer_id' => [
            'class' => Customer::class,
            'label_attribute' => 'company_name',
            'new_key' => 'customer',
        ],
        'items.*.product_id' => [
            'class' => Product::class,
            'label_attribute' => 'name',
            'new_key' => 'product',
        ],
    ],
],

Then use in your model:

public function tapActivity(Activity $activity, string $eventName): void
{
    ActivityLogEnricher::enrichActivityWithConfig($activity, 'orders');
}

Custom Label Methods

Use any model method for label generation:

class Material extends Model
{
    public function getLabelWithoutClass(): string
    {
        return $this->name . ' (' . $this->code . ')';
    }
    
    public function getFullDescriptionAttribute(): string
    {
        return $this->name . ' - ' . $this->description;
    }
}

// Usage
ActivityLogEnricher::enrichActivity($activity, [
    'material_id' => [
        'class' => Material::class,
        'label_attribute' => 'getLabelWithoutClass', // Direct method
    ],
    'secondary_material_id' => [
        'class' => Material::class,
        'label_attribute' => 'full_description', // Accessor attribute
    ],
]);

Automatic Key Guessing

When new_key is not specified, the enricher automatically removes _id suffix:

ActivityLogEnricher::enrichActivity($activity, [
    'customer_id' => [
        'class' => Customer::class,
        'label_attribute' => 'name',
        // new_key will be automatically set to 'customer'
    ],
    'shipping_address_id' => [
        'class' => Address::class,
        'label_attribute' => 'full_address',
        // new_key will be automatically set to 'shipping_address'
    ],
]);

Real-World Examples

E-commerce Order System

class Order extends Model
{
    use LogsActivity;

    public function tapActivity(Activity $activity, string $eventName): void
    {
        ActivityLogEnricher::enrichActivity($activity, [
            'customer_id' => [
                'class' => Customer::class,
                'label_attribute' => 'full_name',
                'new_key' => 'customer',
            ],
            'billing_address_id' => [
                'class' => Address::class,
                'label_attribute' => 'formatted_address',
                'new_key' => 'billing_address',
            ],
            'shipping_address_id' => [
                'class' => Address::class,
                'label_attribute' => 'formatted_address',
                'new_key' => 'shipping_address',
            ],
            'items.*.product_id' => [
                'class' => Product::class,
                'label_attribute' => 'name',
                'new_key' => 'product',
            ],
            'items.*.variant_id' => [
                'class' => ProductVariant::class,
                'label_attribute' => 'display_name',
                'new_key' => 'variant',
            ],
        ]);
    }
}

Project Management System

class Task extends Model
{
    use LogsActivity;

    public function tapActivity(Activity $activity, string $eventName): void
    {
        ActivityLogEnricher::enrichActivity($activity, [
            'project_id' => [
                'class' => Project::class,
                'label_attribute' => 'name',
                'new_key' => 'project',
            ],
            'assigned_to' => [
                'class' => User::class,
                'label_attribute' => 'full_name',
                'new_key' => 'assignee',
            ],
            'created_by' => [
                'class' => User::class,
                'label_attribute' => 'full_name',
                'new_key' => 'creator',
            ],
            'attachments.*.document_id' => [
                'class' => Document::class,
                'label_attribute' => 'filename',
                'new_key' => 'document',
            ],
        ]);
    }
}

Manufacturing System (Based on Original Usage)

class Job extends Model
{
    use LogsActivity;

    public function tapActivity(Activity $activity, string $eventName): void
    {
        ActivityLogEnricher::enrichActivity($activity, [
            'customer_id' => [
                'class' => Contact::class,
                'label_attribute' => 'label',
                'new_key' => 'customer',
            ],
            'design_id' => [
                'class' => Design::class,
                'label_attribute' => 'label',
                'new_key' => 'design',
            ],
            'plate_id' => [
                'class' => Plate::class,
                'label_attribute' => 'label',
                'new_key' => 'plate',
            ],
            'laminate_material_id' => [
                'class' => Material::class,
                'label_attribute' => 'getLabelWithoutClass',
                'new_key' => 'laminate_material',
            ],
            'cap_material_id' => [
                'class' => Material::class,
                'label_attribute' => 'getLabelWithoutClass',
                'new_key' => 'cap_material',
            ],
            'should_material_id' => [
                'class' => Material::class,
                'label_attribute' => 'getLabelWithoutClass',
                'new_key' => 'shoulder_material',
            ],
            'pvc_sleeve_material_id' => [
                'class' => Material::class,
                'label_attribute' => 'getLabelWithoutClass',
                'new_key' => 'pvc_sleeve_material',
            ],
            'liner_material_id' => [
                'class' => Material::class,
                'label_attribute' => 'getLabelWithoutClass',
                'new_key' => 'liner_material',
            ],
            'granules_material_id' => [
                'class' => Material::class,
                'label_attribute' => 'getLabelWithoutClass',
                'new_key' => 'granules_material',
            ],
            'cold_foil_material_id' => [
                'class' => Material::class,
                'label_attribute' => 'getLabelWithoutClass',
                'new_key' => 'cold_foil_material',
            ],
        ]);
    }
}

Error Handling

The enricher is designed to be resilient and will continue processing even when encountering errors:

  • Missing models: If a foreign key references a non-existent model, the enrichment for that field is skipped
  • Invalid classes: Throws InvalidModelException for configuration errors
  • Missing attributes: Falls back to string representation of the model
  • Soft deleted models: Automatically included using withTrashed()

Exception Types

use JimmyAhalpara\ActivityLogEnricher\Exceptions\InvalidModelException;

try {
    ActivityLogEnricher::enrichActivity($activity, $invalidMapping);
} catch (InvalidModelException $e) {
    // Handle configuration errors
    Log::error('ActivityLog enrichment failed: ' . $e->getMessage());
}

Performance Considerations

Eager Loading

To optimize performance, ensure your models are properly eager loaded before enrichment:

// In your model or controller
$activities = Activity::with(['subject', 'causer'])->get();

Caching

Consider implementing model caching for frequently accessed labels:

class Product extends Model
{
    public function getLabelAttribute(): string
    {
        return Cache::remember(
            "product_label_{$this->id}",
            3600,
            fn() => $this->name . ' (' . $this->sku . ')'
        );
    }
}

Testing

Run the test suite:

composer test

Run tests with coverage:

composer test-coverage

Run static analysis:

composer analyse

Run code formatting:

composer format

Run all quality checks:

composer quality

Contributing

Contributions are welcome! Please follow these steps:

  1. Fork the repository
  2. Create a feature branch: git checkout -b feature/amazing-feature
  3. Make your changes and add tests
  4. Ensure all tests pass: composer quality
  5. Commit your changes: git commit -m 'Add amazing feature'
  6. Push to the branch: git push origin feature/amazing-feature
  7. Submit a pull request

Development Setup

git clone https://github.com/jimmyahalpara/laravel-activitylog-enricher.git
cd laravel-activitylog-enricher
composer install
composer quality

Security

If you discover any security-related issues, please email jimmy@ahalpara.in instead of using the issue tracker.

Credits

License

The MIT License (MIT). Please see License File for more information.

Changelog

Please see CHANGELOG for more information on what has changed recently.

Support

If you find this package useful, please consider:

  • ⭐ Starring the repository
  • 🐛 Reporting issues
  • 💡 Contributing improvements
  • 📢 Sharing with others

For support, questions, or feature requests, please open an issue on GitHub.

jimmyahalpara/laravel-activitylog-enricher 适用场景与选型建议

jimmyahalpara/laravel-activitylog-enricher 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 5 次下载、GitHub Stars 达 0, 最近一次更新时间为 2025 年 10 月 21 日, 在 PHP 生态内属于活跃度较高的组件。

它主要适用于以下技术方向: 「logging」 「Audit」 「laravel」 「spatie」 「activity-log」 「enricher」 等业务场景。在实际项目中,围绕这些方向常见需要落地的问题包括:接口对接、性能调优、并发安全、与既有框架(Laravel / ThinkPHP / Yii / Webman 等)的兼容适配,以及生产环境的日志埋点与稳定性保障。

我们在过去多个企业项目中使用过 jimmyahalpara/laravel-activitylog-enricher 或与其功能相近的方案,如果你在选型或落地过程中遇到问题,例如 版本兼容、二次改造、私有化封装、与内部系统对接、生产 BUG 排查,欢迎联系我们协助评估。

围绕 jimmyahalpara/laravel-activitylog-enricher 我们能提供哪些服务?
定制开发 / 二次开发

基于 jimmyahalpara/laravel-activitylog-enricher 在你已有业务上做功能扩展、字段裁剪、UI 适配、与内部账号 / 权限 / 日志系统的深度对接。

BUG 修复 & 性能优化

线上偶发问题、内存泄漏、慢查询、并发异常等排查修复;针对高流量场景做缓存、队列、索引层面的调优。

项目外包 & 长期维护

承接完整的项目从需求 → 设计 → 开发 → 上线 → 长期运维;也可按月提供技术保姆服务。

yvsm@zunyunkeji.com QQ:316430983 微信:yvsm316 西安尊云信息科技 · 专注 PHP / Go / 分布式系统研发

统计信息

  • 总下载量: 5
  • 月度下载量: 0
  • 日度下载量: 0
  • 收藏数: 0
  • 点击次数: 31
  • 依赖项目数: 0
  • 推荐数: 0

GitHub 信息

  • Stars: 0
  • Watchers: 0
  • Forks: 0
  • 开发语言: PHP

其他信息

  • 授权协议: MIT
  • 更新时间: 2025-10-21