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

Common Value Objects Laravel Package

apie/common-value-objects

Common value objects for the Apie ecosystem: ready-to-use PHP enums (e.g., Gender) and identifier base classes (e.g., UUID v4) for entities, fields, or composite value objects. Designed to be extended and used as examples in your own domain.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Installation:

    composer require apie/common-value-objects
    

    Ensure your composer.json includes "apie/common-value-objects": "^x.y.z" (replace with the latest version).

  2. First Use Case: Use a pre-built value object (e.g., UuidV4) for an entity ID:

    use Apie\Core\Identifiers\UuidV4;
    
    $id = UuidV4::createRandom(); // Generates a random UUIDv4
    
  3. Key Files:

    • src/Apie/Core/Enums/ for enums (e.g., Gender).
    • src/Apie/Core/Identifiers/ for identifiers (e.g., UuidV4, Slug).
    • src/Apie/Core/ValueObjects/ for other VOs (e.g., FirstName, DateTimeRange).

Implementation Patterns

1. Entity Identification

Extend UuidV4 (or other identifiers) for entity IDs:

use Apie\Core\Identifiers\UuidV4;
use Apie\Core\Identifiers\IdentifierInterface;
use ReflectionClass;

class PostId extends UuidV4 implements IdentifierInterface
{
    public static function getReferenceFor(): ReflectionClass
    {
        return new ReflectionClass(Post::class);
    }
}

Workflow:

  • Use PostId::createRandom() in Post constructor.
  • Implement getIdentifier() in entities to return the ID.

2. Composite Value Objects

Combine VOs (e.g., FirstName + LastName) for domain-specific types:

use Apie\Core\ValueObjects\FirstName;
use Apie\Core\ValueObjects\LastName;

class FullName
{
    public function __construct(
        private FirstName $firstName,
        private LastName $lastName
    ) {}
}

3. Validation via VOs

Replace loose strings with strict VOs (e.g., SmallDatabaseText):

use Apie\Core\ValueObjects\SmallDatabaseText;

$title = new SmallDatabaseText("Laravel"); // Auto-trims and validates length

4. Integration with Laravel

  • Eloquent Models: Use VOs in model attributes (e.g., protected $id as PostId). Override getKey() to return the VO’s string value:
    public function getKey(): string
    {
        return $this->id->value();
    }
    
  • Form Requests: Validate inputs against VOs (e.g., StrongPasswordField):
    use Apie\Core\ValueObjects\StrongPasswordField;
    
    public function rules()
    {
        return [
            'password' => ['required', function ($attribute, $value, $fail) {
                try {
                    new StrongPasswordField($value);
                } catch (\InvalidArgumentException $e) {
                    $fail($e->getMessage());
                }
            }]
        ];
    }
    

5. Enums in Laravel

Use enums (e.g., Gender) with Laravel’s HasFactory:

use Apie\Core\Enums\Gender;

class User extends Model
{
    protected $casts = [
        'gender' => Gender::class,
    ];
}

Gotchas and Tips

Pitfalls

  1. Identifier Reflection: Forgetting to implement getReferenceFor() in custom identifiers will cause runtime errors when used with Apie entities.

  2. VO Immutability: VOs are immutable. Avoid direct property modification (e.g., $vo->value = 'new'). Use factory methods (e.g., UuidV4::fromString()).

  3. Database Migrations:

    • UUID fields require uuid-ossp or uuid extension in PostgreSQL/MySQL.
    • Slug fields need varchar with appropriate length (e.g., 255 for KebabCaseSlug).
  4. Password Validation: StrongPasswordField enforces strict rules (e.g., min length, complexity). Customize by extending the class:

    class CustomPassword extends StrongPasswordField
    {
        protected function validateStrength(string $value): void
        {
            if (strlen($value) < 10) {
                throw new \InvalidArgumentException("Password must be at least 10 characters.");
            }
        }
    }
    

Debugging

  • Invalid VO Inputs: Catch InvalidArgumentException when constructing VOs:
    try {
        $name = new FirstName(""); // Throws if empty
    } catch (\InvalidArgumentException $e) {
        Log::error("Invalid name: " . $e->getMessage());
    }
    
  • UUID Format Errors: Use UuidV4::fromString() to validate UUIDs before insertion:
    $id = UuidV4::fromString($request->input('id')); // Throws if invalid
    

Extension Points

  1. Custom VOs: Extend existing VOs (e.g., Slug) to add domain logic:
    class BlogSlug extends Slug
    {
        public function __construct(string $value)
        {
            if (str_contains($value, ' ')) {
                throw new \InvalidArgumentException("Blog slugs cannot contain spaces.");
            }
            parent::__construct($value);
        }
    }
    
  2. Composite Validation: Override DateTimeRange to add custom constraints:
    class BusinessHoursRange extends DateTimeRange
    {
        public function __construct(\DateTimeInterface $start, \DateTimeInterface $end)
        {
            if ($start->format('H') < 9 || $end->format('H') > 17) {
                throw new \InvalidArgumentException("Hours must be between 9 AM and 5 PM.");
            }
            parent::__construct($start, $end);
        }
    }
    
  3. Laravel Service Providers: Bind VOs to the container for dependency injection:
    $this->app->bind(FirstName::class, function ($app) {
        return new FirstName($app['request']->input('first_name'));
    });
    

Performance Tips

  • UUID Generation: Cache UuidV4::createRandom() if generating IDs in bulk (e.g., for seeding).
  • VO Reuse: Reuse VOs across requests (e.g., Slug for SEO URLs) to avoid redundant validation.

Testing

  • Unit Tests: Test VO construction with edge cases:
    $this->expectException(InvalidArgumentException::class);
    new FirstName(""); // Empty name
    
  • Integration Tests: Verify Laravel model casting (e.g., enums) and form validation.
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.
codifyo/ts-generator-bundle
andydefer/laravel-cluster
testo/fiber
mintobit/jobqueue
a4sex/maintenance-bundle
a4sex/entity-date-update
a4sex/client-identifier
a4sex/base-utilites
a4sex/key-value-storage
a4sex/micro-status
chilldev/dependency-injection-extra
datinglibre/datinglibre-app-api
biberltd/corebundle
bricre/symfony-bundle-test
biberltd/logbundle
dominium/http-adapter-bundle
dominium/google-analytics
a4sex/auto-clean-entity
christhompsontldr/laravel-inky
spatie/mailcoach-vapor