tailflow / dto
A simple and lightweight implementation of Data Transfer Objects (DTO) in Laravel with optional casting support.
Requires
- php: >=8.1
- ext-json: *
- illuminate/contracts: >=8.0
Requires (Dev)
- orchestra/testbench: 6.0
- phpunit/phpunit: >=8.0
README
A simple and lightweight implementation of Data Transfer Objects (DTO) in Laravel with optional casting support.
Under the hood it implements Laravel's Castable
interface with a Laravel custom cast that handles serializing between the DataTransferObject
(or a compatible array) and your JSON database column.
Installation
You can install the package via composer:
composer require tailflow/dto
Usage
1. Create your DataTransferObject
or DataTransferObjectCollection
namespace App\DataTransferObjects; use Tailflow\DataTransferObjects\DataTransferObject; class Address extends DataTransferObject { public string $country; public string $city; public string $street; }
namespace App\DataTransferObjects; use Tailflow\DataTransferObjects\DataTransferObjectCollection; class WorkAddresses extends DataTransferObjectCollection { public static function getItemClass(): string { return Address::class; } }
(Optional) 2. Configure your Eloquent attribute casting:
Note that this should be a jsonb
or json
column in your database schema.
namespace App\Models; use App\DataTansferObjects\Address; use App\DataTansferObjects\WorkAddresses; use Illuminate\Database\Eloquent\Model; class User extends Model { protected $casts = [ 'address' => Address::class, 'work_addresses' => WorkAddresses::class, ]; }
3. Enjoy ~
Pass DTOs as arguments or use as return values, and get a nice autocompletion.
function getAddress(Address $originalAddress): Address { $address = new Address(); $address->county = $originalAddress->country; $address->city = 'Tokyo'; $address->street = '4-2-8 Shiba-koen'; return $address; } // or function getAddress(): Address { return new Address( [ 'country' => 'Japan', 'city' => 'Tokyo', 'street' => '4-2-8 Shiba-koen', ] ); }
On Eloquent models, you can now pass either an instance of your Address
class, or even just an array with a compatible structure. It will automatically be cast between your class and JSON for storage and the data will be validated on the way in and out.
$workAddress = new Address(); $workAddress->country = 'Japan'; $workAddress->city = 'Osaka'; $user = User::create([ // ... 'address' => [ 'country' => 'Japan', 'city' => 'Tokyo', 'street' => '4-2-8 Shiba-koen', ], 'work_addresses' => [ $workAddress ] ]); $residents = User::where('address->city', 'Tokyo')->get();
But the best part is that you can decorate your class with domain-specific methods to turn it into a powerful value object.
$user->address->toMapUrl(); $user->address->getCoordinates(); $user->address->getPostageCost($sender); $user->address->calculateDistance($otherUser->address); echo (string) $user->address;
License
The MIT License (MIT). Please see License File for more information.