Attributes and Contracts
Use this reference when defining a finding or integrating Data Health with application code.
Contents
Section titled “Contents”- Complete finding example
- Finding base class
- Attributes
- Detection contract
- Verification contracts
- Resolution contracts
- Facades and manager methods
- Finding record operations
- Enums
Complete finding example
Section titled “Complete finding example”namespace App\DataHealth;
use App\Models\Order;use DataHealth\Attributes\Async;use DataHealth\Attributes\AutoResolve;use DataHealth\Attributes\AutoVerify;use DataHealth\Attributes\Description;use DataHealth\Attributes\Key;use DataHealth\Attributes\Scheduled;use DataHealth\Attributes\Urgency;use DataHealth\Attributes\Worklist;use DataHealth\Contracts\CanDetect;use DataHealth\Contracts\CanResolve;use DataHealth\Contracts\CanVerify;use DataHealth\Enums\FindingUrgency;use DataHealth\Finding;
#[Key('paid-order-marked-pending')]#[Description('A paid order is still marked as pending.')]#[Urgency(FindingUrgency::SOON)]#[Worklist('billing-operations')]#[Scheduled('*/15 * * * *')]#[Async(queue: 'data-health', connection: 'redis')]#[AutoVerify(2)]class PaidOrderMarkedPending extends Finding implements CanDetect, CanVerify, CanResolve{ public static function detect(): int { $count = 0;
Order::query() ->whereNotNull('paid_at') ->where('status', 'pending') ->eachById(function (Order $order) use (&$count): void { self::found($order); $count++; });
return $count; }
#[Description('Check whether the order is no longer both paid and pending.')] public function verify(): bool { $order = $this->model->fresh();
return $order === null || $order->paid_at === null || $order->status !== 'pending'; }
#[Description('Mark the order as paid.')] public function resolve(): bool { return $this->model->update(['status' => 'paid']); }}Use only the attributes and contracts the finding needs. AutoResolve attempts a fix immediately after creation, while AutoVerify queues a read-only check immediately or after a short delay.
Finding base class
Section titled “Finding base class”Every finding extends DataHealth\Finding.
| Member | Signature | Meaning |
|---|---|---|
| Constructor | __construct(Model $model, array $context = []) |
Receives the affected model and, when reconstructed from a record, its stored context. |
| Record a finding | static found(mixed ...$args): FindingRecord |
Constructs the finding with the supplied arguments and records or refreshes its identity. |
| Build context | buildContext(): array |
Returns the context persisted for a newly detected finding; defaults to an empty array. |
| Key | static key(): string |
Returns the Key value or, by default, the class basename. |
| Urgency | static getUrgency(): ?FindingUrgency |
Returns the class-level urgency or null. |
| Worklist | static getWorklist(): ?string |
Returns the class-level worklist or null. |
| Description | static getDescription(): ?string |
Returns the class-level description or null. |
| Method description | static getMethodDescription(string $method): ?string |
Returns a Description attached to the named method. |
| Auto-resolution | isAutomaticallyResolved(): bool |
Reports whether the class has AutoResolve. |
| Auto-verification delay | getAutoVerifyDelay(): ?int |
Returns the AutoVerify delay in minutes, or null when automatic verification is disabled. |
found() accepts the finding constructor arguments. The default constructor therefore supports MyFinding::found($model) and MyFinding::found($model, $context). Override the constructor only when the finding genuinely needs another input shape.
Attributes
Section titled “Attributes”All attributes are in DataHealth\Attributes.
| Attribute | Target and constructor | Effect |
|---|---|---|
Key |
Class: new Key(string $key) |
Gives the finding a stable stored identity instead of using its class basename. |
Description |
Class or method: new Description(string $description) |
Describes the finding type or its detect(), verify(), or resolve() action in user interfaces. |
Urgency |
Class: new Urgency(FindingUrgency $urgency) |
Sets urgency on newly created records; the default is NORMAL. |
Worklist |
Class: new Worklist(string $worklist) |
Assigns newly created records to a named operational worklist. |
Scheduled |
Class: new Scheduled(string $expression) |
Registers a CanDetect finding with Laravel’s scheduler using a five-field cron expression. |
Async |
Class: new Async(?string $queue = null, ?string $connection = null) |
Dispatches scheduled detection to a unique queued job, optionally selecting its queue and connection. |
AutoResolve |
Class, no arguments | Calls the finding’s resolver immediately after a new record is created. |
AutoVerify |
Class: new AutoVerify(int $delay = 0) |
Queues verification after a new record is created, optionally delayed by a non-negative number of minutes. |
Changing Urgency or Worklist does not rewrite existing records; those values are copied when a record is first created. Descriptions are read from code at runtime and are not stored in the findings table.
Use a globally unique, permanent Key if the class may be renamed or if classes in different namespaces share a basename. Changing a key makes existing records impossible to reconstruct until they are migrated to the new key.
Detection contract
Section titled “Detection contract”Implement DataHealth\Contracts\CanDetect to make a finding detectable:
public static function detect(): int|callable|null;| Return value | Meaning |
|---|---|
int |
Number of findings detected; useful for manual UI feedback. |
callable |
Work that a supporting caller may execute through Laravel’s container. Filament manual detection executes it; scheduled detection calls detect() itself and does not execute a returned callable. |
null |
Detection completed without reporting a count. |
The method is responsible for querying the source and calling YourFinding::found(...) for each problem. Returning a number alone does not create records.
Verification contracts
Section titled “Verification contracts”Implement DataHealth\Contracts\CanVerify to check whether an existing record’s problem has disappeared:
public function verify(): bool|callable|string;The result may be:
- a boolean;
- a callable returning a boolean, invoked through Laravel’s container; or
- the class name of a service implementing
DataHealth\Contracts\Verifier.
A dedicated verifier has this signature:
use DataHealth\Models\FindingRecord;
public function verify(FindingRecord $record): bool;When the final result is true, Data Health changes the record status to resolved. A false result leaves the current status unchanged.
AutoVerify dispatches this verification after a new record’s transaction commits. It runs only for newly created records and only while the record remains active. Use #[AutoVerify(2)] to delay the queued check by two minutes; asynchronous and delayed execution requires a configured queue worker.
Resolution contracts
Section titled “Resolution contracts”Implement DataHealth\Contracts\CanResolve to offer an application-defined fix:
public function resolve(): bool|callable|string;As with verification, return a boolean, a callable returning a boolean, or the class name of a service implementing DataHealth\Contracts\Resolver:
use DataHealth\Models\FindingRecord;
public function resolve(FindingRecord $record): bool;A final true result marks the record resolved; false leaves its status unchanged. Exceptions are not converted to false and should be handled by the application where appropriate.
Facades and manager methods
Section titled “Facades and manager methods”DataHealth\Facades\DataHealth proxies the public DataHealthManager operations:
| Method | Result |
|---|---|
found(Finding $finding): FindingRecord |
Creates, refreshes, or reactivates a finding record. Prefer the finding class’s found() helper for normal detection code. |
getFindingForRecord(FindingRecord $record): Finding |
Reconstructs the finding from its stored key, model, and context. |
verify(FindingRecord $record): bool |
Executes the finding’s verification behavior and updates status on success. |
resolve(FindingRecord $record): bool |
Executes the finding’s resolution behavior and updates status on success. |
$record->getFinding() is the record-oriented convenience for DataHealth::getFindingForRecord($record). Both reconstruct the same finding instance from its stored key, affected model, and context; use whichever reads more naturally at the call site.
DataHealth\Facades\CheckCursor exposes one operation for bounded scans:
CheckCursor::next(Builder $query, string $check, int $limit): Collection;Pass a query ordered by its numeric primary key, the finding class as $check, and a positive batch size. See Large Datasets for behavior and third-party API patterns.
Finding record operations
Section titled “Finding record operations”Manual verification and resolution should go through the DataHealth facade so the package executes the finding behavior before changing status. The remaining lifecycle operations are direct changes to the public FindingRecord model:
use DataHealth\Enums\RecordStatus;
// Ignore this occurrence.$record->update(['status' => RecordStatus::Ignored]);
// Reopen an ignored or resolved occurrence.$record->update(['status' => RecordStatus::Active]);
// Mark it resolved without verification or resolution logic.$record->update(['status' => RecordStatus::Resolved]);These updates intentionally do not call verify() or resolve(). Rediscovery keeps an ignored record ignored, while rediscovery changes a resolved record back to active.
Use the polymorphic relationship to assign an application model without depending on the Filament integration:
$record->assignee()->associate($user);$record->save();
// Remove the assignment later.$record->assignee()->dissociate();$record->save();Data Health stores assignment but does not define eligible assignee types or authorization rules in the core package. Those decisions belong to the application.
DataHealth\Enums\FindingUrgency contains:
| Case | Stored value |
|---|---|
IMMEDIATE |
immediate |
SOON |
soon |
NORMAL |
normal |
DEFERRED |
deferred |
DataHealth\Enums\RecordStatus contains:
| Case | Stored value | Meaning |
|---|---|---|
Active |
active |
The problem currently requires attention. |
Ignored |
ignored |
An operator has chosen not to act on the record. |
Resolved |
resolved |
Verification, resolution, or a manual action has marked the problem complete. |