Weave Code
Code Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Value Objects Bundle Laravel Package

cubicmushroom/value-objects-bundle

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Install the Bundle

    composer require cubicmushroom/value-objects-bundle
    

    Ensure compatibility with your Symfony version (e.g., 2.7.x for Symfony 2.7).

  2. 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.

  3. 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;
        }
    }
    

Implementation Patterns

Core Workflows

  1. 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).
    
  2. 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);
        }
    }
    
  3. Validation Integration Combine with Symfony’s validator for reusable rules:

    # config/validation.yaml
    App\ValueObjects\Email:
        constraints:
            - Email: ~
    
  4. 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)';
        }
        // ...
    }
    
  5. 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)
        {
            // ...
        }
    }
    

Gotchas and Tips

Pitfalls

  1. Symfony Version Lock

    • The bundle only supports Symfony 2.x–4.x. Attempting to use it with Symfony 5+ will fail.
    • Fix: Use nicolopignatelli/valueobjects directly or find a modern alternative (e.g., spatie/value-object).
  2. Doctrine ORM Quirks

    • Value objects cannot be persisted directly via Doctrine’s default mappings. Use:
      • Custom field types (as shown above).
      • Embeddable entities (if the bundle supports Symfony 3.x+).
      • Serialized storage (last resort; lose type safety).
  3. Circular Dependencies

    • Avoid circular references between value objects (e.g., Email containing a User containing an Email).
    • Fix: Use DTOs or separate domain layers.
  4. Validation Overhead

    • Each value object validates on instantiation. For bulk operations, validate once and reuse:
      $emails = array_map(fn($raw) => new Email($raw), $rawEmails);
      

Debugging Tips

  1. Assertion Failures

    • If a value object throws InvalidArgumentException, check:
      • The assertIsValid() method in your custom class.
      • Input sanitization (e.g., trimming whitespace for StringValueObject).
  2. Symfony Dependency Injection

    • Value objects cannot be autowired directly in Symfony’s container. Register them as services:
      services:
          App\ValueObjects\Email:
              arguments: ['%kernel.environment%'] # Example: dynamic value
      
  3. Testing

    • Mock value objects in tests to avoid instantiation overhead:
      $this->createMock(Email::class);
      
    • Use data providers for validation edge cases:
      public function validEmailProvider(): array
      {
          return [['user@example.com'], ['first.last@sub.domain.com']];
      }
      

Extension Points

  1. 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.');
            }
        }
    }
    
  2. Serialization Implement __toString() or JsonSerializable for API responses:

    class Money extends AbstractValueObject
    {
        public function jsonSerialize(): array
        {
            return ['amount' => $this->value, 'currency' => $this->currency];
        }
    }
    
  3. 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.
        }
    }
    
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
jayeshmepani/jpl-moshier-ephemeris-php
elnasnato/laraliveui
labrodev/rest-sdk
sampaui/sampaui
babelqueue/php-sdk
facebook/capi-param-builder-php
babelqueue/symfony
hamzi/corewatch
minionfactory/raw-hydrator
hexters/coinpayment
rjcodes/rjcms
act-training/laravel-permissions-manager
alimarchal/laravel-chart-of-accounts
babenkoivan/elastic-scout-driver
mkwebdesign/filament-watchdog-v5
renatomarinho/laravel-page-speed
zedmagdy/filament-business-hours
renatovdemoura/blade-elements-ui
devgeek/beacon-admin
benjamin-rqt/data-watcher-bundle