Weave 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

Valueobjects Laravel Package

nicolopignatelli/valueobjects

Laravel-friendly PHP value objects library that helps you model domain concepts as immutable, self-validating objects (like Email, Money, Url, etc.). Encourages DDD patterns, clearer APIs, and safer data handling with consistent validation and comparison.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Installation:

    composer require nicolopignatelli/valueobjects
    

    Add the service provider to config/app.php:

    'providers' => [
        // ...
        Nicolopignatelli\ValueObjects\ValueObjectsServiceProvider::class,
    ],
    
  2. First Use Case: Define a simple value object (e.g., Email):

    use Nicolopignatelli\ValueObjects\ValueObject;
    
    class Email extends ValueObject
    {
        protected $value;
    
        public function __construct(string $value)
        {
            $this->validate($value);
            $this->value = $value;
        }
    
        protected function validate(string $value): void
        {
            if (!filter_var($value, FILTER_VALIDATE_EMAIL)) {
                throw new \InvalidArgumentException("Invalid email format.");
            }
        }
    
        public function __toString(): string
        {
            return $this->value;
        }
    }
    
  3. Usage:

    $email = new Email('[email protected]');
    echo $email; // Outputs: [email protected]
    

Where to Look First

  • Documentation: The GitHub README outlines core concepts and basic usage.
  • Tests: The tests folder demonstrates real-world patterns (e.g., Email, Money, Percentage).
  • ValueObject Base Class: Study ValueObject.php for built-in methods like equals(), hashCode(), and immutability enforcement.

Implementation Patterns

Core Workflows

  1. Immutable Data Modeling: Use value objects to enforce immutability and domain-specific validation. Example:

    class UserId extends ValueObject
    {
        public function __construct(string $value)
        {
            $this->validate($value);
            $this->value = $value;
        }
    
        protected function validate(string $value): void
        {
            if (!preg_match('/^[a-f0-9]{24}$/', $value)) {
                throw new \InvalidArgumentException("Invalid MongoDB ID format.");
            }
        }
    }
    
  2. Domain-Specific Logic: Encapsulate business rules within value objects. Example for Money:

    class Money extends ValueObject
    {
        public function add(Money $other): Money
        {
            return new self($this->value + $other->value);
        }
    
        public function isGreaterThan(Money $other): bool
        {
            return $this->value > $other->value;
        }
    }
    
  3. Type Safety: Leverage PHP 7.4+ typed properties for stricter contracts:

    class Percentage extends ValueObject
    {
        private float $value;
    
        public function __construct(float $value)
        {
            $this->validate($value);
            $this->value = $value;
        }
    
        protected function validate(float $value): void
        {
            if ($value < 0 || $value > 100) {
                throw new \InvalidArgumentException("Percentage must be between 0 and 100.");
            }
        }
    }
    

Integration Tips

  1. Laravel Eloquent: Use value objects as column attributes or accessors:

    class User extends Model
    {
        protected $casts = [
            'email' => Email::class,
        ];
    
        public function getEmailAttribute($value)
        {
            return new Email($value);
        }
    }
    
  2. API Requests: Validate incoming requests with value objects:

    use Illuminate\Http\Request;
    
    public function store(Request $request)
    {
        $email = new Email($request->input('email'));
        // Proceed with $email...
    }
    
  3. Collections: Apply value objects to Laravel collections for consistency:

    $users = User::all()->map(fn ($user) => new Email($user->email));
    
  4. DTOs (Data Transfer Objects): Combine value objects into DTOs for API responses:

    class UserDTO
    {
        public function __construct(
            public Email $email,
            public string $name,
        ) {}
    }
    

Gotchas and Tips

Pitfalls

  1. Validation Overhead:

    • Issue: Excessive validation in constructors can slow down bulk operations.
    • Fix: Use lazy validation or static factory methods:
      class Email extends ValueObject
      {
          public static function tryFrom(string $value): ?self
          {
              if (!filter_var($value, FILTER_VALIDATE_EMAIL)) {
                  return null;
              }
              return new self($value);
          }
      }
      
  2. Serialization Quirks:

    • Issue: Value objects may not serialize/deserialize correctly with Laravel's serialize or JSON APIs.
    • Fix: Implement JsonSerializable or __serialize()/__unserialize():
      class Email implements JsonSerializable
      {
          public function jsonSerialize(): string
          {
              return $this->value;
          }
      }
      
  3. Database Storage:

    • Issue: Storing value objects directly in databases may cause issues with type casting.
    • Fix: Use accessors/mutators or store raw values:
      protected $casts = [
          'email' => 'string', // Store raw, hydrate as Email on access
      ];
      
  4. Performance with Large Values:

    • Issue: Complex value objects (e.g., large strings or arrays) can bloat memory.
    • Fix: Offload storage to external systems or use lazy-loading.

Debugging Tips

  1. Equality Comparisons:

    • Override equals() to handle edge cases (e.g., case sensitivity for strings):
      public function equals($other): bool
      {
          return $other instanceof self && strtolower($this->value) === strtolower($other->value);
      }
      
  2. Hashing:

    • Customize hashCode() for consistent hashing (critical for collections):
      public function hashCode(): int
      {
          return hash('sha256', $this->value);
      }
      
  3. Logging:

    • Use __toString() for readable logs:
      class Money
      {
          public function __toString(): string
          {
              return sprintf('$%.2f', $this->value);
          }
      }
      

Extension Points

  1. Custom Assertions: Extend validation with custom rules:

    use Illuminate\Validation\Rule;
    
    class Domain extends ValueObject
    {
        protected function validate(string $value): void
        {
            $domain = explode('.', $value);
            if (count($domain) < 2) {
                throw new \InvalidArgumentException("Invalid domain format.");
            }
            // Add custom rules (e.g., check against a list of allowed domains)
        }
    }
    
  2. Value Object Factories: Create static factories for complex logic:

    class Url extends ValueObject
    {
        public static function fromString(string $url): self
        {
            $parsed = parse_url($url);
            if (!$parsed || empty($parsed['scheme']) || empty($parsed['host'])) {
                throw new \InvalidArgumentException("Invalid URL.");
            }
            return new self($url);
        }
    }
    
  3. Laravel Policy Integration: Use value objects in policies for authorization:

    class UpdateUserPolicy
    {
        public function update(User $user, Email $newEmail): bool
        {
            return $user->email->equals($newEmail);
        }
    }
    
  4. Testing: Mock value objects in tests for isolation:

    $mockEmail = Mockery::mock(Email::class, ['[email protected]']);
    $this->app->instance(Email::class, $mockEmail);
    
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.
coleus/frame
thtg88/journalism
filagora/filagora-ai-forums
rlacerda83/query-parser
socialdept/atp-orm
cboxdk/laravel-assistant-client
socialdept/atp-resolver
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings