›
byrcsc/laravel-customer-health · 1.x
Laravel events expose recorded activity, milestones, onboarding progress, and health-state transitions.
Listen to these events when package writes should trigger application work. All six event classes are final, readonly, and dispatched synchronously.
| Event | Public properties | When it fires |
|---|---|---|
ProductEventRecorded | ProductEventRecord $record | After each raw product-event write |
MilestoneReached | Milestone $milestone | After a milestone is inserted for the first time |
OnboardingStepCompleted | Milestone $milestone, string $checklist, string $step | After a checklist step milestone is first inserted |
OnboardingCompleted | Milestone $milestone, string $checklist | After the checklist completion milestone is inserted |
HealthScoreComputed | HealthScoreRecord $record, ScoreResult $result | After every committed score computation |
HealthStateChanged | HealthScoreRecord $record, ?string $from, string $to | After a committed computation changes state |
OnboardingStepCompleted::$step is the milestone ProductEvent class name.
OnboardingCompleted::$milestone is the generated
onboarding:<checklist-name> milestone.
namespace App\Listeners;
use ByRcsc\LaravelCustomerHealth\Events\ProductEventRecorded;
final class RecordProductAudit
{
public function handle(ProductEventRecorded $event): void
{
$event->record->name;
$event->record->subject_type;
$event->record->subject_id;
$event->record->properties;
$event->record->occurred_at;
}
}This event fires for every raw event, including repeated milestone events.
namespace App\Listeners;
use ByRcsc\LaravelCustomerHealth\Events\MilestoneReached;
final class NotifyCustomerSuccess
{
public function handle(MilestoneReached $event): void
{
if ($event->milestone->name !== 'workflow_created') {
return;
}
// React to the first workflow created by this subject.
}
}The milestone table's unique index prevents duplicate first-occurrence events when concurrent calls track the same milestone.
use ByRcsc\LaravelCustomerHealth\Events\OnboardingCompleted;
use ByRcsc\LaravelCustomerHealth\Events\OnboardingStepCompleted;
final class RecordOnboardingProgress
{
public function handle(
OnboardingStepCompleted|OnboardingCompleted $event,
): void {
$event->checklist;
$event->milestone->subject_type;
$event->milestone->subject_id;
}
}These events are registered through the connection's afterCommit callback.
They do not fire when the surrounding database transaction rolls back.
namespace App\Listeners;
use ByRcsc\LaravelCustomerHealth\Events\HealthStateChanged;
final class OpenCustomerSuccessTask
{
public function handle(HealthStateChanged $event): void
{
if ($event->to !== 'at_risk') {
return;
}
$identity = [
'type' => $event->record->subject_type,
'id' => $event->record->subject_id,
];
// Create or update an idempotent task for this identity.
}
}The first computation fires HealthStateChanged with $from === null. A later
computation in the same state fires only HealthScoreComputed.
Both score events are registered after the score history and summary transaction commits.
ProductEventRecorded and MilestoneReached dispatch after the package's
event write transaction returns. If the caller has opened an outer transaction,
these two events can run before that outer transaction commits.
Onboarding and score events use database afterCommit callbacks. They wait for
the outer transaction to commit.
All listeners run synchronously unless the listener implements Laravel's
ShouldQueue. A listener exception can fail the request or queued product-event
job that dispatched it.
Queued job retries can create repeated ProductEventRecorded events and raw
rows. Make listeners repeatable when queued tracking is enabled.
Pruning and purging do not dispatch package lifecycle events. Direct writes to the readable package models also bypass the event flow.