›
›
›
  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

Summaries and tenancy.

Current health summaries can live beside score history or on a shared landlord connection.

A score computation writes append-only history and one current summary. Use the summary table for operational queries such as listing every at-risk customer.

One current row per identity

The package identifies a summary by tenant ID, subject morph type, subject ID, and score name. Recomputing the same score updates that row while preserving every entry in the score history table.

$atRisk = CustomerHealth::inState(
    state: 'at_risk',
    score: 'customer_health',
)->get();

$all = CustomerHealth::summaries()->get();

Both methods return an Eloquent builder for HealthSummary. You can add normal query constraints before calling get(), paginate(), or another terminal method.

Each summary exposes tenant_id, subject_type, subject_id, score, value, state, and computed_at. subjectIdentity() returns a MorphIdentity for explicit resolution.

Single-database applications

The defaults are:

'connection' => null,
'summary_connection' => null,
'tenant_resolver' => NullTenantResolver::class,

Both history and summaries use Laravel's current default connection. Summary rows have a null tenant ID.

Database-per-tenant applications

Set connection to the tenant connection and summary_connection to the landlord connection:

'connection' => 'tenant',
'summary_connection' => 'landlord',
'tenant_resolver' => App\Tenancy\CurrentTenantId::class,

The resolver must be an invokable class implementing TenantResolver:

namespace App\Tenancy;

use ByRcsc\LaravelCustomerHealth\Contracts\TenantResolver;

final readonly class CurrentTenantId implements TenantResolver
{
    public function __invoke(): int|string|null
    {
        return tenant()?->getKey();
    }
}

It may return an integer, non-empty string, or null. The package stores a non-null value as a string. Invalid classes or return values throw InvalidTenantResolverException.

Run the events, milestones, and scores migrations on each tenant connection. Run the summaries migration on the landlord connection.

Spatie Laravel Multitenancy

The optional SpatieTenantResolver reads Tenant::current()?->getKey():

'tenant_resolver' =>
    ByRcsc\LaravelCustomerHealth\Tenancy\SpatieTenantResolver::class,

The package core does not depend on Spatie. The adapter returns null when the Spatie tenant class is unavailable or no tenant is current.

When Spatie's queues are tenant-aware, queued product-event jobs contain only primitive identity data. Spatie can restore the dispatching tenant before the worker writes to the tenant connection.

Cross-connection consistency

When history and summaries share a connection, score history and summary updates occur in one transaction.

When they use different connections, there is no distributed transaction. The history transaction calls the summary connection during computation, and an exception prevents the history record from committing. An infrastructure failure after the landlord write but before the tenant commit can still leave a summary ahead of history.

Recomputing known subjects rebuilds missing or stale summaries from current signals.

Resolve summary subjects deliberately

$summary->subjectIdentity()->resolve() queries the model using its default connection unless you pass a connection name:

$subject = $summary->subjectIdentity()->resolve('tenant');

A landlord summary cannot infer which tenant database contains the model from tenant_id. Switch tenant context or supply the correct connection before resolving it.

What to read next

  • Querying customer health for current-state, activity, and stalled-onboarding queries.
  • Recomputing scores to rebuild summary rows.
  • Configuration for connection and tenant resolver defaults.
PreviousHealth scoresNextQuerying customer health
View source

On this page

  1. One current row per identity
  2. Single-database applications
  3. Database-per-tenant applications
  4. Spatie Laravel Multitenancy
  5. Cross-connection consistency
  6. Resolve summary subjects deliberately
  7. What to read next