Quick Start
In this guide, you will create a finding for orders that have been paid but are still marked as pending, record an affected order, and then verify or resolve the problem.
Contents
Section titled “Contents”- The data problem
- Create the finding
- How detection works
- Create an inconsistent order
- Run detection
- Inspect the finding record
- Verify an external correction
- Resolve the finding
- What happens when detection runs again
- Next steps
The data problem
Section titled “The data problem”Assume your application has an App\Models\Order model with a nullable paid_at timestamp and a string status column. A paid order should have the paid status, so the following state is inconsistent:
paid_at: 2026-10-02 10:00:00status: pendingThe finding will consider an order unhealthy when paid_at is not null and status is pending.
Create the finding
Section titled “Create the finding”Create app/DataHealth/PaidOrderMarkedPending.php:
<?php
declare(strict_types=1);
namespace App\DataHealth;
use App\Models\Order;use DataHealth\Attributes\Description;use DataHealth\Attributes\Key;use DataHealth\Contracts\CanDetect;use DataHealth\Contracts\CanResolve;use DataHealth\Contracts\CanVerify;use DataHealth\Finding;
#[Key('paid-order-marked-pending')]#[Description('A paid order is still marked as pending.')]class PaidOrderMarkedPending extends Finding implements CanDetect, CanResolve, CanVerify{ public static function detect(): int { $detected = 0;
Order::query() ->whereNotNull('paid_at') ->where('status', 'pending') ->eachById(function (Order $order) use (&$detected): void { self::found($order);
$detected++; });
return $detected; }
public function verify(): bool { $order = $this->order()->refresh();
return $order->paid_at === null || $order->status !== 'pending'; }
public function resolve(): bool { return $this->order()->update([ 'status' => 'paid', ]); }
private function order(): Order { /** @var Order $order */ $order = $this->model;
return $order; }}The Key attribute gives the finding a stable database identity even if the PHP class is renamed later, while Description provides a human-readable explanation for interfaces such as Data Health Filament.
The three contracts enable different parts of the lifecycle:
CanDetectallows the class to search for inconsistent orders.CanVerifyallows an existing record to check whether its problem still applies.CanResolveallows the finding to correct the order itself.
How detection works
Section titled “How detection works”The detect() method queries only orders matching the unhealthy state and calls found() for each one. Data Health then creates a finding record identified by:
- the stable finding key;
- the order’s morph type;
- the order’s primary key; and
- the finding’s context.
This example does not need additional context because there can be only one instance of this problem per order. Calling found() repeatedly for the same order therefore refreshes the existing record instead of creating duplicates.
Returning the number of detected orders makes the method useful from commands, jobs, tests, and the Filament manual-detection interface.
Create an inconsistent order
Section titled “Create an inconsistent order”Open Laravel Tinker:
php artisan tinkerCreate an order that is paid but still pending, supplying any other attributes required by your application’s model:
use App\Models\Order;
$order = new Order();$order->paid_at = now();$order->status = 'pending';$order->save();Run detection
Section titled “Run detection”Call the finding’s detection method:
use App\DataHealth\PaidOrderMarkedPending;
PaidOrderMarkedPending::detect();// 1The return value is the number of orders detected during this run.
Inspect the finding record
Section titled “Inspect the finding record”Retrieve the record created for the order:
use DataHealth\Models\FindingRecord;
$record = FindingRecord::query() ->where('key', PaidOrderMarkedPending::key()) ->where('model_type', $order->getMorphClass()) ->where('model_id', $order->getKey()) ->firstOrFail();
$record->status->value;// 'active'
$record->model->is($order);// true
$record->context;// []The record remains linked to its order through a polymorphic Eloquent relationship. Its active status means that the inconsistency still requires attention.
Verify an external correction
Section titled “Verify an external correction”Verification is useful when the underlying data may have been corrected somewhere else. While the order is still pending, verification returns false and leaves the record active:
use DataHealth\Facades\DataHealth;
DataHealth::verify($record);// false
$record->refresh()->status->value;// 'active'Correct the order outside Data Health and verify it again:
$order->update(['status' => 'paid']);
DataHealth::verify($record);// true
$record->refresh()->status->value;// 'resolved'A verifier returns true when the recorded problem no longer applies. Data Health responds by marking the finding record as resolved.
Resolve the finding
Section titled “Resolve the finding”To demonstrate resolution, make the order inconsistent again and rerun detection:
$order->update(['status' => 'pending']);
PaidOrderMarkedPending::detect();// 1
$record->refresh()->status->value;// 'active'Data Health reactivates the existing record because the same problem has returned. Resolve it through the facade:
DataHealth::resolve($record);// true
$order->refresh()->status;// 'paid'
$record->refresh()->status->value;// 'resolved'The finding’s resolve() method changes the order, and its successful true response tells Data Health to mark the record as resolved.
What happens when detection runs again
Section titled “What happens when detection runs again”Running detection now returns zero because no order matches the unhealthy state:
PaidOrderMarkedPending::detect();// 0If the same order becomes inconsistent later, detection reactivates its existing finding record instead of inserting another one. This preserves the identity of the problem while allowing it to recur throughout the model’s lifetime.
Next steps
Section titled “Next steps”You now have a complete finding that can be detected, verified, and resolved. Continue with:
- Findings and Records for a deeper explanation of identity, context, and statuses;
- Detecting Problems for detection patterns and testing guidance;
- Scheduling and Queues to run detection automatically; or
- Filament Installation to manage findings through a Filament panel.