Skip to main content

Custom Reference Resolvers

Chronicle converts every actor and subject value into a Reference — a (type, id) pair — before storing it. The default resolver handles Eloquent models and objects with a public $id property. Replace it when your domain uses a different identity model.

The contract

interface ReferenceResolver
{
public function resolve(mixed $value): Reference;
}

resolve() receives the raw value passed to ->actor() or ->subject() and must return a Chronicle\Support\Reference:

use Chronicle\Support\Reference;

new Reference(string $type, string $id);

$type is typically a class name or a stable string identifier. $id must be a string. Both are stored as-is in the ledger.

Default resolver behaviour

DefaultReferenceResolver supports:

InputResolved as
Eloquent model with primary keyReference(ClassName, (string) $key)
Object with public $id propertyReference(ClassName, (string) $id)
Scalar valuethrows InvalidArgumentException
Unsupported objectthrows InvalidArgumentException

Pass a persisted model — an unsaved model with getKey() === null throws.

Writing a custom resolver

use Chronicle\Contracts\ReferenceResolver;
use Chronicle\Support\Reference;

class DomainReferenceResolver implements ReferenceResolver
{
public function resolve(mixed $value): Reference
{
// Eloquent models: delegate to standard behaviour
if ($value instanceof \Illuminate\Database\Eloquent\Model) {
return new Reference(
$value::class,
(string) $value->getKey(),
);
}

// Value objects that expose a stable identity
if ($value instanceof HasStableIdentity) {
return new Reference(
$value->typeName(),
$value->stableId(),
);
}

throw new \InvalidArgumentException(
sprintf('Cannot resolve Chronicle reference for %s.', get_debug_type($value))
);
}
}

Registering the resolver

Rebind ReferenceResolver::class in your application service provider. Chronicle's ChronicleServiceProvider binds it as a singleton — your binding in AppServiceProvider::register() will take precedence because application providers load after package providers:

use Chronicle\Contracts\ReferenceResolver;

class AppServiceProvider extends ServiceProvider
{
public function register(): void
{
$this->app->singleton(
ReferenceResolver::class,
DomainReferenceResolver::class,
);
}
}

There is no config key for the resolver — it is always resolved through the service container.

See also