129 lines
4.3 KiB
PHP
129 lines
4.3 KiB
PHP
<?php
|
|
|
|
namespace App\Services\System;
|
|
|
|
use App\Enums\ActivityEventLabel;
|
|
use App\Models\User;
|
|
use App\Support\ActivityLog\ModelLabel;
|
|
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
|
|
use Illuminate\Database\Eloquent\Builder;
|
|
use Illuminate\Database\Eloquent\Model;
|
|
use Illuminate\Support\Collection;
|
|
use Spatie\Activitylog\Models\Activity;
|
|
|
|
class ActivityLogService
|
|
{
|
|
/**
|
|
* @param array{search: string, sort: string, direction: 'asc'|'desc'} $tableQuery
|
|
*/
|
|
public function paginateForIndex(array $tableQuery, string $event = '', string $subjectType = ''): LengthAwarePaginator
|
|
{
|
|
$query = Activity::query()
|
|
->with(['causer.profile'])
|
|
->when($tableQuery['search'] !== '', function (Builder $query) use ($tableQuery): void {
|
|
$search = $tableQuery['search'];
|
|
$query->where(function (Builder $query) use ($search): void {
|
|
$query->where('description', 'like', "%{$search}%")
|
|
->orWhere('event', 'like', "%{$search}%")
|
|
->orWhereHasMorph(
|
|
'causer',
|
|
[User::class],
|
|
fn (Builder $query) => $query
|
|
->where('username', 'like', "%{$search}%")
|
|
->orWhereHas('profile', fn (Builder $query) => $query->where('full_name', 'like', "%{$search}%")),
|
|
);
|
|
});
|
|
})
|
|
->when($event !== '', fn (Builder $query) => $query->where('event', $event))
|
|
->when($subjectType !== '', fn (Builder $query) => $query->where('subject_type', $subjectType));
|
|
|
|
$this->applySorting($query, $tableQuery['sort'], $tableQuery['direction']);
|
|
|
|
return $query
|
|
->paginate(10)
|
|
->withQueryString()
|
|
->through(fn (Activity $activity) => $this->present($activity));
|
|
}
|
|
|
|
/**
|
|
* @return array{
|
|
* id: int,
|
|
* description: string,
|
|
* event: string|null,
|
|
* event_label: string,
|
|
* subject_type: string|null,
|
|
* subject_label: string,
|
|
* subject_id: int|null,
|
|
* causer_name: string,
|
|
* created_at: string|null,
|
|
* created_at_formatted: string|null,
|
|
* changes: list<array{field: string, old: mixed, new: mixed}>
|
|
* }
|
|
*/
|
|
private function present(Activity $activity): array
|
|
{
|
|
return [
|
|
'id' => $activity->id,
|
|
'description' => $activity->description,
|
|
'event' => $activity->event,
|
|
'event_label' => ActivityEventLabel::labelFor($activity->event),
|
|
'subject_type' => $activity->subject_type,
|
|
'subject_label' => ModelLabel::for($activity->subject_type),
|
|
'subject_id' => $activity->subject_id,
|
|
'causer_name' => $this->resolveCauserName($activity->causer),
|
|
'created_at' => $activity->created_at?->toIso8601String(),
|
|
'created_at_formatted' => $activity->created_at?->translatedFormat('l, d F Y H:i'),
|
|
'changes' => $this->formatChanges($activity->attribute_changes),
|
|
];
|
|
}
|
|
|
|
private function resolveCauserName(?Model $causer): string
|
|
{
|
|
if (! $causer instanceof User) {
|
|
return '-';
|
|
}
|
|
|
|
return $causer->profile?->full_name ?? $causer->username ?? '-';
|
|
}
|
|
|
|
/**
|
|
* @return list<array{field: string, old: mixed, new: mixed}>
|
|
*/
|
|
private function formatChanges(?Collection $changes): array
|
|
{
|
|
if ($changes === null || $changes->isEmpty()) {
|
|
return [];
|
|
}
|
|
|
|
$attributes = $changes->get('attributes', []);
|
|
$old = $changes->get('old', []);
|
|
|
|
if (! is_array($attributes)) {
|
|
return [];
|
|
}
|
|
|
|
$formatted = [];
|
|
|
|
foreach ($attributes as $field => $newValue) {
|
|
$formatted[] = [
|
|
'field' => (string) $field,
|
|
'old' => is_array($old) ? ($old[$field] ?? null) : null,
|
|
'new' => $newValue,
|
|
];
|
|
}
|
|
|
|
return $formatted;
|
|
}
|
|
|
|
private function applySorting(Builder $query, string $sort, string $direction): void
|
|
{
|
|
if (in_array($sort, ['created_at', 'description', 'event'], true)) {
|
|
$query->orderBy($sort, $direction);
|
|
|
|
return;
|
|
}
|
|
|
|
$query->latest();
|
|
}
|
|
}
|