Skip to content

Commit 75a6e3a

Browse files
committed
Add activity feed query scopes
1 parent f0f427c commit 75a6e3a

2 files changed

Lines changed: 78 additions & 0 deletions

File tree

src/Models/Activity.php

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
use Illuminate\Database\Eloquent\Model;
99
use Illuminate\Database\Eloquent\Relations\MorphTo;
1010
use Illuminate\Support\Carbon;
11+
use Illuminate\Support\Str;
1112

1213
/**
1314
* @property string $event
@@ -57,6 +58,46 @@ public function scopeEvent(Builder $query, string|array $event): Builder
5758
return $query->whereIn('event', (array) $event);
5859
}
5960

61+
public function scopeEventPrefix(Builder $query, string|array $prefix): Builder
62+
{
63+
return $query->where(function (Builder $query) use ($prefix): void {
64+
foreach ((array) $prefix as $eventPrefix) {
65+
$query->orWhere('event', 'like', Str::finish($eventPrefix, '.').'%');
66+
}
67+
});
68+
}
69+
70+
public function scopeForActor(Builder $query, Model $actor): Builder
71+
{
72+
return $query
73+
->where('actor_type', $actor->getMorphClass())
74+
->where('actor_id', $actor->getKey());
75+
}
76+
77+
public function scopeForSubject(Builder $query, Model $subject): Builder
78+
{
79+
return $query
80+
->where('subject_type', $subject->getMorphClass())
81+
->where('subject_id', $subject->getKey());
82+
}
83+
84+
public function scopeForParent(Builder $query, Model $parent): Builder
85+
{
86+
return $query
87+
->where('parent_type', $parent->getMorphClass())
88+
->where('parent_id', $parent->getKey());
89+
}
90+
91+
public function scopeOccurredSince(Builder $query, Carbon|string $since): Builder
92+
{
93+
return $query->where('occurred_at', '>=', $since);
94+
}
95+
96+
public function scopeLatestActivity(Builder $query): Builder
97+
{
98+
return $query->latest('occurred_at')->latest($this->getQualifiedKeyName());
99+
}
100+
60101
public function tenantColumn(): string
61102
{
62103
return config('filament-activity.tenant.foreign_key', 'tenant_id');

tests/Feature/ActivityRecorderTest.php

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,43 @@
4545
expect(Activity::query()->forTenant($teamA)->sole()->id)->toBe($teamAActivity->id);
4646
});
4747

48+
it('scopes activities by actor subject parent event prefix and recency', function (): void {
49+
$actor = User::query()->create(['name' => 'Ada']);
50+
$otherActor = User::query()->create(['name' => 'Grace']);
51+
$post = Post::query()->create(['name' => 'Topic']);
52+
$otherPost = Post::query()->create(['name' => 'Other topic']);
53+
$parent = Post::query()->create(['name' => 'Parent topic']);
54+
$recorder = app(ActivityRecorder::class);
55+
56+
$matchingActivity = $recorder->record(
57+
event: 'forum.post.created',
58+
subject: $post,
59+
actor: $actor,
60+
parent: $parent,
61+
);
62+
$matchingActivity->forceFill(['occurred_at' => now()->subHour()])->save();
63+
64+
$recorder->record('course.lesson.completed', $post, actor: $actor, parent: $parent);
65+
$recorder->record('forum.post.created', $otherPost, actor: $actor, parent: $parent);
66+
$recorder->record('forum.post.created', $post, actor: $otherActor, parent: $parent);
67+
68+
expect(Activity::query()->forActor($actor)->count())->toBe(3)
69+
->and(Activity::query()->forSubject($post)->count())->toBe(3)
70+
->and(Activity::query()->forParent($parent)->count())->toBe(4)
71+
->and(Activity::query()->eventPrefix('forum')->count())->toBe(3)
72+
->and(Activity::query()->eventPrefix(['forum.post'])->count())->toBe(3)
73+
->and(Activity::query()->occurredSince(now()->subMinutes(30))->count())->toBe(3)
74+
->and(Activity::query()
75+
->forActor($actor)
76+
->forSubject($post)
77+
->forParent($parent)
78+
->eventPrefix('forum')
79+
->occurredSince(now()->subHours(2))
80+
->sole()
81+
->id
82+
)->toBe($matchingActivity->id);
83+
});
84+
4885
it('exposes subject activities through the trait', function (): void {
4986
$post = Post::query()->create(['name' => 'Topic']);
5087

0 commit comments

Comments
 (0)