cubicmushroom/value-objects-bundle
Install the Bundle
composer require cubicmushroom/value-objects-bundle
Ensure compatibility with your Symfony version (e.g., 2.7.x for Symfony 2.7).
Register the Bundle
Add to app/AppKernel.php (Symfony 2.x) or config/bundles.php (Symfony 3.x+):
// Symfony 2.x
new \CubicMushroom\Symfony\ValueObjectsBundle\CMValueObjectsBundle(),
For Symfony 3.x+, autoloading handles registration automatically.
First Use Case: Define a Value Object
Create a custom value object (e.g., Email) extending nicolopignatelli/valueobjects:
use Nicolopignatelli\ValueObjects\AbstractValueObject;
use Nicolopignatelli\ValueObjects\Scalar\StringValueObject;
class Email extends StringValueObject
{
protected function assertIsValid(): void
{
if (!filter_var($this->value, FILTER_VALIDATE_EMAIL)) {
throw new \InvalidArgumentException('Invalid email format.');
}
}
}
Use it in a Symfony entity or service:
use App\ValueObjects\Email;
class User
{
private Email $email;
public function __construct(Email $email)
{
$this->email = $email;
}
}
Immutable Value Objects Leverage the bundle to enforce immutability in domain logic:
$email = new Email('user@example.com');
// $email->value = 'new@example.com'; // Throws error (immutable).
Type Safety in Constructors Use value objects in Symfony services/DTOs for strict typing:
class UserService
{
public function createUser(Email $email, string $name): User
{
return new User($email, $name);
}
}
Validation Integration Combine with Symfony’s validator for reusable rules:
# config/validation.yaml
App\ValueObjects\Email:
constraints:
- Email: ~
Database Mapping
Use Doctrine extensions (e.g., Gedmo) or custom types to store value objects:
// src/Doctrine/DBAL/Types/EmailType.php
use Doctrine\DBAL\Types\Type;
use Doctrine\DBAL\Platforms\AbstractPlatform;
class EmailType extends Type
{
public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform)
{
return 'VARCHAR(255)';
}
// ...
}
API Request Handling
Transform incoming data into value objects via Symfony’s ParamConverter:
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
class UserController
{
/**
* @ParamConverter("email", converter="fos_rest.request_body")
*/
public function createUser(Email $email)
{
// ...
}
}
Symfony Version Lock
nicolopignatelli/valueobjects directly or find a modern alternative (e.g., spatie/value-object).Doctrine ORM Quirks
Circular Dependencies
Email containing a User containing an Email).Validation Overhead
$emails = array_map(fn($raw) => new Email($raw), $rawEmails);
Assertion Failures
InvalidArgumentException, check:
assertIsValid() method in your custom class.StringValueObject).Symfony Dependency Injection
services:
App\ValueObjects\Email:
arguments: ['%kernel.environment%'] # Example: dynamic value
Testing
$this->createMock(Email::class);
public function validEmailProvider(): array
{
return [['user@example.com'], ['first.last@sub.domain.com']];
}
Custom Assertions
Extend AbstractValueObject to add domain-specific rules:
class DomainName extends StringValueObject
{
protected function assertIsValid(): void
{
if (!preg_match('/^(?!-)[A-Za-z0-9-]{1,63}(?<!-)\.[A-Za-z]{2,6}$/', $this->value)) {
throw new \InvalidArgumentException('Invalid domain format.');
}
}
}
Serialization
Implement __toString() or JsonSerializable for API responses:
class Money extends AbstractValueObject
{
public function jsonSerialize(): array
{
return ['amount' => $this->value, 'currency' => $this->currency];
}
}
Symfony Forms Use value objects in form types for type-safe data binding:
class UserType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('email', EmailType::class); // Custom form field.
}
}
How can I help you explore Laravel packages today?