michaelravedoni / laravel-value-objects
A base package for creating value objects in Laravel, with Artisan command and generic Eloquent cast.
Requires
- php: ^8.1
- illuminate/database: ^10.0|^11.0|^12.0
- illuminate/support: ^10.0|^11.0|^12.0
Requires (Dev)
- laravel/pint: ^1.23
- orchestra/testbench: ^7.0|^8.0|^9.0|^10.0|^11.0
- pestphp/pest: ^2.31|^3.0
- phpunit/phpunit: ^10.0|^11.0|^12.0
README
Simply create value objects in Laravel.
Documentation | Contributing
Built with ❤︎ by Michael Ravedoni and contributorsIntroduction
A simple, lightweight Laravel package for creating value objects, including an Artisan command for generating them and a generic Eloquent casting system.
✨ Features
- Basic Value Object: An abstract class for structuring your value objects.
- Contractual interface: For strong typing and consistent implementation.
- Generic Eloquent Casting: Easily converts model attributes into instances of your custom value objects and vice versa.
- Artisan command: Quickly generates value object skeletons.
- Built-in validation: Add your validation logic directly into the constructor of your value objects.
- Extensive compatibility: Supports Laravel 10, 11, 12 and PHP 8.2, 8.3, 8.4.
🚀 Installation
This package can be installed through Composer :
composer require michaelravedoni/laravel-value-objects
The package will be automatically discovered by Laravel.
💡 Documentation
Creating a Value Object
The easiest way to create a new value object is to use the Artisan command provided by the :
php artisan make:value-object MyCustomValue
This will create an app/ValueObjects/MyCustomValue.php
file with a code skeleton.
You then need to implement the constructor to define the value and add your validation logic, as well as the value()
method to return the raw value.
Example: An Email
value object.
// app/ValueObjects/Email.php <?php namespace App\ValueObjects; use MichaelRavedoni\LaravelValueObjects\ValueObjects\BaseValueObject; use InvalidArgumentException; class Email extends BaseValueObject { protected string $value; public function __construct(string $value) { if (!filter_var($value, FILTER_VALIDATE_EMAIL)) { throw new InvalidArgumentException("Invalid email format: {$value}"); } $this->value = $value; } public function value(): string { return $this->value; } public function getDomain(): string { return substr($this->value, strpos($this->value, '@') + 1); } }
Casting with Eloquent
To use your value object with an Eloquent model, you can cast it directly using the package's generic ValueObjectCast
in your $casts
array.
// app/Models/User.php <?php namespace App\Models; use Illuminate\Database\Eloquent\Model; use MichaelRavedoni\LaravelValueObjects\Casts\ValueObjectCast; use App\ValueObjects\Email; // Importez votre objet valeur class User extends Model { protected $fillable = [ 'name', 'email', ]; protected $casts = [ 'email' => ValueObjectCast::class . ':' . Email::class, ]; }
Accessing Values and Methods
Once cast, you can access your attribute as an instance of your value object, by calling its methods or by treating it as a string (using the __toString()
method inherited from BaseValueObject
).
use App\Models\User; use App\ValueObjects\Email; // Create a user $user = User::create([ 'name' => 'Alice', 'email' => new Email('alice@example.com'), // Accepts a value object instance ]); // Or directly a raw value (the cast will convert it) $user2 = User::create([ 'name' => 'Bob', 'email' => 'bob@test.com', ]); // Access the value object $user = User::find(1); echo "Full email address : " . $user->email; // alice@example.com (via __toString()) echo "Email domain : " . $user->email->getDomain(); // example.com // Update the $user->email = 'new.email@domain.com'; // You can assign a string $user->save(); $user->email = new Email('another@mail.org'); // Or a new instance of a value object $user->save(); // Manage null values (if the column is nullable) $userWithNullEmail = User::create(['name' => 'John', 'email' => null]); if ($userWithNullEmail->email === null) { echo "The email is null."; }
🎬 Usage
Let's take the example of `Gender' to illustrate a concrete case.
1. Create the Gender
Value Object :.
php artisan make:value-object Gender
Edit the file app/ValueObjects/Gender.php
:
// app/ValueObjects/Gender.php <?php namespace App\ValueObjects; use MichaelRavedoni\LaravelValueObjects\ValueObjects\BaseValueObject; use InvalidArgumentException; class Gender extends BaseValueObject { protected string $value; // 'm' ou 'w' public function __construct(string $value) { if (!in_array($value, ['m', 'w'])) { throw new InvalidArgumentException("Invalid gender value: {$value}. Must be 'm' or 'w'."); } $this->value = $value; } public function value(): string { return $this->value; } public function getLabel(): string { return match ($this->value) { 'm' => 'Man', 'w' => 'Woman', default => 'Not specified', }; } public function getDescription(): string { return match ($this->value) { 'm' => 'Type of athlete : Male', 'w' => 'Type of athlete : Female', default => 'Gender not specified', }; } }
2. Use in the Athlete
Model :
Make sure that your Athlete
model has a gender
column (type char(1)
or string
).
// app/Models/Athlete.php <?php namespace App\Models; use Illuminate\Database\Eloquent\Model; use MichaelRavedoni\LaravelValueObjects\Casts\ValueObjectCast; use App\ValueObjects\Gender; class Athlete extends Model { protected $fillable = [ 'name', 'gender', ]; protected $casts = [ 'gender' => ValueObjectCast::class . ':' . Gender::class, ]; }
3. Practical use :
use App\Models\Athlete; use App\ValueObjects\Gender; $athlete = Athlete::create([ 'name' => 'Alice Tremblay', 'gender' => 'w', ]); echo $athlete->gender; // Display : w echo $athlete->gender->getLabel(); // Display : Woman echo $athlete->gender->getDescription(); // Display : Gender of athlete : Woman $athlete->gender = new Gender('m'); $athlete->save(); $updatedAthlete = Athlete::find($athlete->id); echo $updatedAthlete->gender->getLabel(); // Display : Man
🤝 Contribute
Please see CONTRIBUTING for details.
📜 Credits
- Michael Ravedoni - Initial work - michaelravedoni
- Inspired form Michael Rubel package.
See also the list of contributors who participated in this project.
📄 Licence
MIT License. Please see License File for more information.