etel / identifier
Represents contracts and infrastructure for identifiers.
Requires
- php: ^8.4
- etel/comparable: ^2.0
Requires (Dev)
- friendsofphp/php-cs-fixer: ^3.94
- phpstan/phpstan: ^2.1
- phpunit/phpunit: ^13.0
- symfony/uid: ^7.0|^8.0
Suggests
- symfony/uid: Required to implement UuidIdentifier.
README
etel/identifier
Contracts and ready-to-use trait implementations for typed identifier value objects in PHP 8.4+.
Installation
composer require etel/identifier
To use UUID-based identifiers, also require symfony/uid:
composer require symfony/uid
Concepts
An identifier is an immutable value object that wraps a raw value (integer, string, or UUID)
and exposes it through a typed $value property and a string representation via $string.
Every identifier type carries a PREFIX constant used as a human-readable namespace in its string form
(e.g. usr_42, order_01JV3BWXYZ). An empty prefix is valid but prevents IdentifierResolver
from distinguishing the type by string.
Defining an identifier
Implement one of the sub-interfaces and use the matching trait:
Integer identifier (positive integers only):
use Etel\Identifier\IntegerIdentifier; use Etel\Identifier\Implementation\Integer\IntegerIdentifierTrait; final class UserId implements IntegerIdentifier { use IntegerIdentifierTrait; public const string PREFIX = 'usr_'; } $id = new UserId(42); $id->value; // 42 $id->string; // "usr_42" UserId::fromString('usr_42'); // UserId(42) UserId::tryFromString('usr_abc'); // null
UUID v7 / Base58 identifier (time-sortable, URL-safe):
use Etel\Identifier\UuidIdentifier; use Etel\Identifier\Implementation\Uuid\Uuid7Base58IdentifierTrait; use Symfony\Component\Uid\Uuid; final class OrderId implements UuidIdentifier { use Uuid7Base58IdentifierTrait; public const string PREFIX = 'order_'; } $id = new OrderId(Uuid::v7()); $id->value; // UuidV7 instance $id->string; // "order_1BvBMSEYstWetqTFn5Au4m" OrderId::fromString('order_1BvBMSEYstWetqTFn5Au4m'); // OrderId instance
String identifier:
use Etel\Identifier\StringIdentifier; use Etel\Identifier\Implementation\String\StringIdentifierTrait; final class CountryCode implements StringIdentifier { use StringIdentifierTrait; public const string PREFIX = 'country.'; } $id = new CountryCode('UA'); $id->value; // "UA" $id->string; // "country.UA"
Available traits
Integer
| Trait | Description |
|---|---|
IntegerIdentifierTrait |
Positive integer (>= 1) |
String
| Trait | Description |
|---|---|
StringIdentifierTrait |
Non-empty string |
UUID
| Trait | UUID version | String format |
|---|---|---|
Uuid4Rfc4122IdentifierTrait |
v4 | xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx |
Uuid4Base32IdentifierTrait |
v4 | 26-char Crockford Base32 |
Uuid4Base58IdentifierTrait |
v4 | 22-char Base58 |
Uuid7Rfc4122IdentifierTrait |
v7 | xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx |
Uuid7Base32IdentifierTrait |
v7 | 26-char Crockford Base32 |
Uuid7Base58IdentifierTrait |
v7 | 22-char Base58 |
If the built-in traits don't fit, you can implement the appropriate interface directly.
Identifiable
Mark domain objects that own an identifier with the Identifiable interface:
use Etel\Identifier\Identifiable; final class User implements Identifiable { public function __construct(public readonly UserId $id, ... ) {} }
Resolving identifiers by string
IdentifierResolver allows resolving an identifier from its string representation without knowing the concrete type
in advance. Implementations decide how identifier types are registered:
use Etel\Identifier\IdentifierResolver; $resolver->fromString('usr_42'); // UserId(42) $resolver->fromString('order_1BvBMSEY...'); // OrderId instance $resolver->tryFromString('unknown_x'); // null
Todo list
- more types
- resolver implementations