Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

auroralabs's avatar

AuditRelationsManager not auditing Comments or Attachments

I have set up an AuditRelationsManager Resource to audit changes to the Receipts model however the Recipts also have Comments and Attachments that I would like to Audit also. Both have been setup as Relations that show up on the Recipts List view.

So far changes to the Recipts are being Audited but not any changes made to the Comments or Attachments. Below is the code for the Models and the AuditRelationsManager

Comment Model

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use OwenIt\Auditing\Contracts\Auditable;
use OwenIt\Auditing\Auditable as AuditableTrait;

class Comment extends Model implements Auditable
{
    use AuditableTrait;

    public function receipt()
    {
        return $this->belongsTo(Receipt::class);
    }

    public function dispatch()
    {
        return $this->belongsTo(Dispatch::class);
    }
}

Attachment Model

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use OwenIt\Auditing\Contracts\Auditable;
use OwenIt\Auditing\Auditable as AuditableTrait;

class Attachment extends Model implements Auditable
{
    use AuditableTrait;

    public function receipt()
    {
        return $this->belongsTo(Receipt::class);
    }

    public function dispatch()
    {
        return $this->belongsTo(Dispatch::class);
    }
}

Receipt Model

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\MorphMany;
use Illuminate\Database\Eloquent\SoftDeletes;
use OwenIt\Auditing\Contracts\Auditable;
use OwenIt\Auditing\Auditable as AuditableTrait;

class Receipt extends Model implements Auditable
{
    use AuditableTrait;
    
    public function comments()
    {
        return $this->hasMany(Comment::class);
    }

    public function attachments()
    {
        return $this->hasMany(Attachment::class);
    }
    
   public function audits(): MorphMany
    {
        return $this->morphMany(Audit::class, 'auditable');
    }
}

AuditRelationManager

<?php

namespace App\Filament\Resources\AuditRelationManagerResource\RelationManagers;

use App\Models\Comment;
use App\Models\Attachment;
use App\Models\Location;
use App\Models\Receipt;
use App\Models\Status;
use App\Models\Warehouse;
use Filament\Facades\Filament;
use Filament\Notifications\Notification;
use Filament\Resources\RelationManagers\RelationManager;
use Filament\Tables;
use Filament\Tables\Table;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\Relation;
use Illuminate\Support\Arr;
use OwenIt\Auditing\Contracts\Audit;

class AuditRelationManager extends RelationManager
{
    protected static string $relationship = 'audits';

    protected static ?string $recordTitleAttribute = 'id';

    protected $listeners = ['updateAuditRelationManager' => '$refresh'];

    public static function isLazy(): bool
    {
        return false;
    }

    public static function canViewForRecord(Model $ownerRecord, string $pageClass): bool
    {
        return Filament::auth()->user()->can('viewAudit', $ownerRecord);
    }

    public static function getTitle(Model $ownerRecord, string $pageClass): string
    {
        return 'Audits';
    }

    public function table(Table $table): Table
    {
        return $table
            ->modifyQueryUsing(fn (Builder $query): Builder =>
                $query->with('user', 'auditable') // Eager load 'user' and 'auditable' relationships
                      ->whereIn('auditable_type', [Receipt::class, Comment::class, Attachment::class]) // Include audits for Receipt, Comment, and Attachment models
                      ->orderBy('created_at', 'asc') // Order the results by creation date
            )
            ->columns([
                Tables\Columns\TextColumn::make('user.name')
                    ->label('User'),
                Tables\Columns\TextColumn::make('event')
                    ->label('Event'),
                Tables\Columns\TextColumn::make('created_at')
                    ->since()
                    ->label('Created'),
                Tables\Columns\ViewColumn::make('old_values')
                    ->view('filament.auditing.key_value')
                    ->label('Old Values'),
                Tables\Columns\ViewColumn::make('new_values')
                    ->view('filament.auditing.key_value')
                    ->label('New Values'),
            ])
            ->filters([
                // Define filters here if needed
            ])
            ->headerActions([
                // Define header actions here if needed
            ])
            ->actions([
                 // Define actions here if needed
            ])
            ->bulkActions([
                // Define bulk actions here if needed
            ]);
    }

    protected function formatAuditValues(array $values): array
    {
        $formatted = [];

        foreach ($values as $key => $value) {
            switch ($key) {
                case 'status_id':
                    $status = Status::find($value);
                    $formatted['Status'] = $status ? $status->name : 'Unknown Status';
                    break;
                case 'warehouse_id':
                    $warehouse = Warehouse::find($value);
                    $formatted['Warehouse'] = $warehouse ? $warehouse->name : 'Unknown Warehouse';
                    break;
                case 'location_id':
                    $location = Location::find($value);
                    $formatted['Location'] = $location ? $location->name : 'Unknown Location';
                    break;
                case 'comments':
                    if (is_array($value)) {
                        $comments = Comment::whereIn('id', $value)->pluck('content')->toArray();
                        $formatted['Comments'] = $comments ? implode(', ', $comments) : 'No Comments';
                    } else {
                        $formatted['Comments'] = 'Invalid format for comments';
                    }
                    break;
                case 'attachments':
                    if (is_array($value)) {
                        $attachments = Attachment::whereIn('id', $value)->pluck('file_name')->toArray();
                        $formatted['Attachments'] = $attachments ? implode(', ', $attachments) : 'No Attachments';
                    } else {
                        $formatted['Attachments'] = 'Invalid format for attachments';
                    }
                    break;
                default:
                    $formatted[$key] = $value;
                    break;
            }
        }

        return $formatted;
    }


    protected static function unchangedAuditNotification(): void
    {
        Notification::make()
            ->title('Nothing to change')
            ->warning()
            ->send();
    }

    protected function canCreate(): bool
    {
        return false;
    }

    protected function canEdit(Model $record): bool
    {
        return false;
    }

    protected function canDelete(Model $record): bool
    {
        return false;
    }
}
0 likes
0 replies

Please or to participate in this conversation.