boson-php/value-object-contracts
Installation
composer require boson-php/value-object-contracts
No additional configuration is needed—this is a contract-only package.
First Use Case: Defining a Value Object
Create a class implementing Boson\ValueObject\ValueObjectContract:
use Boson\ValueObject\ValueObjectContract;
class Email implements ValueObjectContract
{
private string $value;
public function __construct(string $value)
{
$this->value = $value;
}
public function equals(ValueObjectContract $other): bool
{
return $this->value === $other->getValue();
}
public function getValue(): string
{
return $this->value;
}
}
Where to Look First
Boson\ValueObject\ namespace for core interfaces.tests/ for real-world usage examples.Use ValueObjectContract for immutable, self-validating objects:
class UserId implements ValueObjectContract
{
private string $id;
public function __construct(string $id)
{
if (!preg_match('/^[a-f0-9]{24}$/', $id)) {
throw new \InvalidArgumentException('Invalid ID format');
}
$this->id = $id;
}
public function equals(ValueObjectContract $other): bool
{
return $this->id === $other->getValue();
}
public function getValue(): string
{
return $this->id;
}
}
use Boson\ValueObject\ValueObjectContract;
class StoreUserRequest extends FormRequest
{
public function validate(): array
{
return [
'email' => ['required', 'email'],
];
}
public function validatedValueObjects(): array
{
return [
'email' => new Email($this->input('email')),
];
}
}
class User extends Model
{
protected $casts = [
'email' => Email::class,
];
}
Use Boson\ValueObject\Collection for typed collections:
use Boson\ValueObject\Collection;
$emails = Collection::make([
new Email('user1@example.com'),
new Email('user2@example.com'),
]);
ValueObjectContract for objects like Email, Money, or DateRange.Immutability
Equality Contracts
equals() must be consistent with ==. For example:
// Bad: Compares object references
public function equals(ValueObjectContract $other): bool {
return $this === $other;
}
$this->value === $other->getValue()).Database Storage
$casts may not work out-of-the-box for complex Value Objects.protected $attributes = ['email' => null];
public function getEmailAttribute($value)
{
return $value ? new Email($value) : null;
}
public function setEmailAttribute($value)
{
$this->attributes['email'] = $value instanceof Email ? $value->getValue() : $value;
}
Performance with Large Collections
Collection::equals() performs O(n) comparisons. Avoid in hot paths.HashSet-like structure for lookups if needed.instanceof ValueObjectContract to verify objects.__toString() for debugging:
public function __toString(): string
{
return $this->getValue();
}
equals() in unit tests to verify behavior:
$this->assertTrue((new Email('test@example.com'))->equals(new Email('test@example.com')));
Custom Validation
Extend ValueObjectContract with validation logic in the constructor:
class Age implements ValueObjectContract
{
private int $value;
public function __construct(int $value)
{
if ($value < 0 || $value > 120) {
throw new \InvalidArgumentException('Invalid age');
}
$this->value = $value;
}
// ... equals(), getValue()
}
JSON Serialization
Implement JsonSerializable for API responses:
use JsonSerializable;
class Money implements ValueObjectContract, JsonSerializable
{
// ...
public function jsonSerialize(): array
{
return ['amount' => $this->amount, 'currency' => $this->currency];
}
}
Laravel Service Providers Bind Value Objects to the container for dependency injection:
$this->app->bind(Email::class, function ($app) {
return new Email($app['request']->input('email'));
});
How can I help you explore Laravel packages today?