›
byrcsc/laravel-customer-health · 1.x
Query adoption, activity, onboarding, scores, and current state through the CustomerHealth facade.
Use these queries when a controller, job, command, or customer success workflow needs a stored view of customer behavior or health.
$adopted = CustomerHealth::hasAdopted(
subject: $team,
feature: 'workflows',
);This query reads permanent milestone rows. It remains true after raw product events are pruned.
List the event declarations registered for each feature with
CustomerHealth::features():
foreach (CustomerHealth::features() as $feature => $eventClasses) {
// $feature is a string and $eventClasses is a list of ProductEvent classes.
}$usage = CustomerHealth::featureUsage('workflows')->for($team);
$first = $usage->firstUsedAt;
$last = $usage->lastUsedAt;
$allEvents = $usage->eventCount();
$recentEvents = $usage->eventCount(days: 30);
$recentActors = $usage->distinctActors(days: 30);Usage queries read raw events for every registered event in the feature.
firstUsedAt and lastUsedAt are nullable UTC CarbonImmutable values.
The day cutoff is inclusive. An event exactly 30 days old contributes to
eventCount(days: 30).
$lastSeen = CustomerHealth::lastSeen($team);
$sameValue = $team->lastProductActivity();The result is the latest raw event time or null. Pruning the latest event can
move this value backward or make it null.
$identities = CustomerHealth::inactive(days: 14)->get();
foreach ($identities as $identity) {
$subject = $identity->resolve();
}The query considers every subject found in raw events or milestones. It returns subjects whose last raw event is older than the UTC cutoff, plus milestone-only subjects whose raw history has been removed.
A subject at the exact cutoff is active. A subject with no event or milestone has no package record and cannot appear in the result.
Each result carries public type and id values. resolve() returns the
Eloquent model or null. Pass a connection name when the model must be read
from a tenant database:
$subject = $identity->resolve('tenant');$progress = CustomerHealth::onboarding(
subject: $team,
checklist: App\CustomerHealth\Onboarding::class,
);
$progress->completedSteps();
$progress->totalSteps();
$progress->percent();
$progress->currentStep();
$progress->isComplete();
$progress->stalledSince();Find partial checklists whose latest step is older than a cutoff:
$stalled = CustomerHealth::stalledInOnboarding(days: 14)->get();This query checks every registered checklist and removes duplicate subject identities from the combined result.
$latest = CustomerHealth::score($team, 'customer_health');
$history = CustomerHealth::scoreHistory($team, 'customer_health');score() reads the newest stored result and returns null when no score has
been computed. It does not evaluate signals.
scoreHistory() returns ScoreResult values in ascending order by computation
time and record ID.
Use summaries when you need the current state across subjects:
$atRisk = CustomerHealth::inState(
state: 'at_risk',
score: 'customer_health',
)->orderBy('computed_at')->get();For custom filters, start from the summary builder:
$staleHealthy = CustomerHealth::summaries()
->where('state', 'healthy')
->where('computed_at', '<', now()->subDay())
->get();inState() resolves the score declaration first. Omit the score argument only
when the first registered score is the intended one.