Installation
composer require boson-php/id-contracts
No additional configuration is required—this is a pure contract package with zero runtime dependencies.
First Use Case
Define a simple identifier contract for a domain entity (e.g., UserId):
use Boson\IdContracts\Id;
class UserId implements Id
{
private string $value;
public function __construct(string $value)
{
$this->value = $value;
}
public function value(): string
{
return $this->value;
}
}
Use it in a repository or service:
class UserRepository
{
public function findById(UserId $id): ?User
{
// Fetch user logic...
}
}
Key Files to Explore
src/Id.php: The core Id interface.src/IdGenerator.php: Optional trait for generating IDs (if extended).Id contracts as root identifiers for aggregates.
class Order implements AggregateRoot
{
public function __construct(
public OrderId $id,
public CustomerId $customerId,
public Collection<OrderItem> $items
) {}
}
Id in value objects for relationships.
class OrderItem
{
public function __construct(
public ProductId $productId,
public int $quantity
) {}
}
public function findById(string|UserId $id): ?User
{
$id = is_string($id) ? new UserId($id) : $id;
// ...
}
Id types:
class UserService
{
public function __construct(private UserId $userId) {}
}
Id objects in repositories.
class UserRepository
{
public function findById(string $rawId): ?User
{
return User::query()->find(new UserId($rawId));
}
}
Id objects:
class UserQueryBuilder
{
public function whereId(UserId $id): self
{
return $this->where('id', $id->value());
}
}
Id objects in request handlers.
class StoreUserRequest
{
public function toUserId(): UserId
{
return new UserId($this->id);
}
}
Id values.
class UserId implements JsonSerializable
{
public function jsonSerialize(): string
{
return $this->value();
}
}
Id interfaces in unit tests for isolation.
$mockId = $this->createMock(Id::class);
$mockId->method('value')->willReturn('test-id');
IdGenerator (if extended).Over-Engineering
Id classes for every primitive ID (e.g., PostId, CommentId) if the domain doesn’t require it. Use string or int directly for simple cases.Id contracts for aggregate roots or globally unique identifiers.Performance Overhead
Id objects adds minor overhead. Benchmark if used in hot paths (e.g., loop iterations).StringObject) for lightweight wrappers if needed.Database Migrations
Id::value() return type (e.g., VARCHAR for string IDs).Serialization Quirks
Illuminate\Support\Collection) may not handle custom Id objects by default.Arrayable or JsonSerializable for seamless integration.Type Errors
Argument of type 'string' is not assignable to 'Id', ensure all entry points (APIs, factories) convert raw values to Id objects.instanceof checks or IDE hints to catch mismatches early.Circular References
UserId in Order and OrderId in User) can cause serialization loops.__toString() or JsonSerializable to break cycles.Custom ID Generators
IdGenerator to create domain-specific IDs (e.g., UUIDs, ULIDs).
class UuidIdGenerator implements IdGenerator
{
public function generate(): string
{
return Uuid::generate()->toString();
}
}
Validation
Id classes (e.g., regex for UUIDs).
class UserId implements Id
{
public function __construct(string $value)
{
if (!preg_match('/^[0-9a-f]{8}-[0-9a-f]{4}/', $value)) {
throw new InvalidArgumentException('Invalid UUID format.');
}
$this->value = $value;
}
}
Laravel-Specific
class User extends Model
{
public function getIdAttribute(string $value): UserId
{
return new UserId($value);
}
}
Id interfaces to concrete implementations for dependency injection.
$this->app->bind(UserId::class, function () {
return new UserId('default-id');
});
API Resources
Id values.
class UserResource extends JsonResource
{
public function toArray($request)
{
return [
'id' => $this->resource->id->value(),
// ...
];
}
}
How can I help you explore Laravel packages today?