›
›
›
  1. docs
  2. ›
  3. byrcsc/laravel-customer-health
1.x
Browse documentationOpenClose

Getting started

  • Introduction
  • Installation and setup
  • Quick start

Core concepts

  • Product events
  • Features and milestones
  • Onboarding
  • Health scores
  • Summaries and tenancy

Operations

  • Querying customer health
  • Queueing events
  • Recomputing scores
  • Retention and erasure
  • Production operations

Reference

  • Configuration
  • Public API
  • Events
  • Console commands
  • Database storage
  • Testing
  • Troubleshooting

Getting started

  • Introduction
  • Installation and setup
  • Quick start

Core concepts

  • Product events
  • Features and milestones
  • Onboarding
  • Health scores
  • Summaries and tenancy

Operations

  • Querying customer health
  • Queueing events
  • Recomputing scores
  • Retention and erasure
  • Production operations

Reference

  • Configuration
  • Public API
  • Events
  • Console commands
  • Database storage
  • Testing
  • Troubleshooting

byrcsc/laravel-customer-health · 1.x

Querying customer health.

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.

Check feature adoption

$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.
}

Measure feature usage

$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).

Read last activity

$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.

Find inactive subjects

$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');

Read onboarding progress

$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.

Read score history

$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.

Query current state

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.

What to read next

  • Features and milestones to understand which queries survive raw-event pruning.
  • Summaries and tenancy to resolve subjects from a landlord summary.
  • Public API for exact facade and value object signatures.
PreviousSummaries and tenancyNextQueueing events
View source

On this page

  1. Check feature adoption
  2. Measure feature usage
  3. Read last activity
  4. Find inactive subjects
  5. Read onboarding progress
  6. Read score history
  7. Query current state
  8. What to read next