Skip to content

Add opt-in same-save merge for activity timeline (batch_uuid)#32

Merged
AsmitNepali merged 4 commits into
1.xfrom
activity-timeline-batch-merge
Jun 20, 2026
Merged

Add opt-in same-save merge for activity timeline (batch_uuid)#32
AsmitNepali merged 4 commits into
1.xfrom
activity-timeline-batch-merge

Conversation

@AsmitNepali

Copy link
Copy Markdown
Collaborator

What

Adds an opt-in, read-side "same-save merge" to the activity timeline. When one save produces several activity rows (e.g. a native updated row plus a custom-field row), the consumer can render them as a single timeline entry, keyed by spatie's batch_uuid.

Why

A single save often emits multiple activity rows. Without merging, each shows as its own timeline entry, fragmenting what is logically one event. This lets consumers collapse them — without changing how activities are written/logged.

Changes

  • ActivityLogSource
    • New opt-in state + fluent withSameBatchMerge(?string $mergedRenderer = null).
    • resolve(): when enabled, groups rows via groupByBatch() and yields one makeMergedEntry() per group; otherwise unchanged per-row behaviour.
    • groupByBatch(): groups rows sharing a non-empty batch_uuid, preserving the query's newest-first / first-seen order; rows without a batch_uuid each stay their own group.
    • makeMergedEntry(): representative base = first row with non-empty attribute_changes (else first row); properties = union of extractProperties() across the group; renderer set explicitly so RendererRegistry resolves it before event/type.
  • TimelineBuilder::fromActivityLog(?int $priority = null, ?string $mergedRenderer = null) — passes through to the source only when $mergedRenderer is non-null. Default null ⇒ existing behaviour.
  • Docs: new "Same-save merge" section in docs/.../1.sources.md (incl. the host-owned batch_uuid migration), README feature bullet, CHANGELOG entry.
  • Tests: tests/Feature/SameBatchMergeTest.php — merge carries both payloads + renderer; different/null batch_uuid not merged; newest-first ordering preserved; default behaviour unchanged.

Backward compatibility

Fully opt-in. Default $mergedRenderer = null keeps the original per-row behaviour. All 55 existing tests pass. fromActivityLogOf() is not merged yet (follow-up).

Consumer requirement

Merge groups by spatie batch_uuid — the host app owns this column. If activity_log lacks it:

Schema::table('activity_log', function (Blueprint $table): void {
    $table->uuid('batch_uuid')->nullable()->index();
});

Suggested release: minor (v1.2.0).

…ch rows

A save touching several custom fields emits one activity row per field, each
under the same custom_field_changes key. The previous spread union kept only
the last row's value, dropping the rest from the merged entry. Use array_merge
so list payloads concatenate while associative maps (native attributes/old)
still union per key.
@AsmitNepali AsmitNepali merged commit 49879c6 into 1.x Jun 20, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant