PHPackages                             fab2s/laravel-dt0 - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Parsing &amp; Serialization](/categories/parsing)
4. /
5. fab2s/laravel-dt0

ActiveLibrary[Parsing &amp; Serialization](/categories/parsing)

fab2s/laravel-dt0
=================

Immutable Data Transfer Objects (DTO) for Laravel with validation, Eloquent casting, and encryption

1.0.1(3mo ago)51.3k↓40%MITPHPPHP ^8.1CI passing

Since Apr 28Pushed 3mo ago1 watchersCompare

[ Source](https://github.com/fab2s/laravel-dt0)[ Packagist](https://packagist.org/packages/fab2s/laravel-dt0)[ Docs](https://github.com/fab2s/laravel-dt0)[ RSS](/packages/fab2s-laravel-dt0/feed)WikiDiscussions main Synced 1mo ago

READMEChangelog (2)Dependencies (7)Versions (6)Used By (0)

Laravel Dt0
===========

[](#laravel-dt0)

[![CI](https://github.com/fab2s/laravel-dt0/actions/workflows/ci.yml/badge.svg)](https://github.com/fab2s/laravel-dt0/actions/workflows/ci.yml) [![QA](https://github.com/fab2s/laravel-dt0/actions/workflows/qa.yml/badge.svg)](https://github.com/fab2s/laravel-dt0/actions/workflows/qa.yml) [![codecov](https://camo.githubusercontent.com/bfe46d6d94c1a03257a3e5161303432b9927aa37c1f85f23bbdc79ad04490f1d/68747470733a2f2f636f6465636f762e696f2f67682f66616232732f6c61726176656c2d6474302f67726170682f62616467652e7376673f746f6b656e3d59453641594544413634)](https://codecov.io/gh/fab2s/laravel-dt0) [![Latest Stable Version](https://camo.githubusercontent.com/a57dcfac7cc0c3f09dc94a29647506925ad6455d5af2f95f3ac0f41785c35869/687474703a2f2f706f7365722e707567782e6f72672f66616232732f6c61726176656c2d6474302f76)](https://packagist.org/packages/fab2s/laravel-dt0) [![PHPStan](https://camo.githubusercontent.com/83dd3d35cebed0eab9ee97ff1a5849c1344cda6a8ee9cac2cda20f5aa55b67bd/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f5048505374616e2d6c6576656c253230392d627269676874677265656e2e7376673f7374796c653d666c6174)](https://phpstan.org/) [![PRs Welcome](https://camo.githubusercontent.com/7d9ed3c8f22eceb1711573169b1390cc0b1194467340dc815205060c162b5309/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f5052732d77656c636f6d652d627269676874677265656e2e7376673f7374796c653d666c6174)](http://makeapullrequest.com) [![License](https://camo.githubusercontent.com/5c3c9c2003c7ff695dc0651907838d8447304e275a0121a2a89b1c0551d440cb/687474703a2f2f706f7365722e707567782e6f72672f66616232732f6474302f6c6963656e7365)](https://packagist.org/packages/fab2s/dt0)

A powerful [Laravel](https://laravel.com/) integration for [fab2s/dt0](https://github.com/fab2s/dt0), bringing **true immutability**, **Laravel validation**, and **Eloquent model casting** to your Data Transfer Objects.

Why Dt0?
--------

[](#why-dt0)

Traditional DTOs with mutable properties miss the core purpose: **guaranteeing that data won't be accidentally modified**. Dt0 leverages PHP 8.1+'s native `readonly` properties to enforce immutability at the language level, not by convention, but by design.

**Key Benefits:**

- **True Immutability** — Readonly properties prevent accidental modifications (fatal error, not silent bug)
- **Laravel Validation** — Full power of Laravel's validation rules on DTO properties
- **Eloquent Casting** — Use DTOs directly as model attributes with automatic serialization
- **Flexible Hydration** — Create from arrays, JSON strings, or other instances
- **Type Safety** — Strong typing with bidirectional casting support, `strict_types` enforced, PHPStan level 9 clean
- **Performance** — Logic compiled once per process and cached

> **Note:** This package extends [fab2s/dt0](https://github.com/fab2s/dt0) with Laravel-specific features (validation, Eloquent casting). All features from the base package, including [property casting](https://github.com/fab2s/dt0/blob/1.0.0/docs/casters.md), property renaming, default values, output filtering, and more, work seamlessly here. Visit the [dt0 documentation](https://github.com/fab2s/dt0) for the complete feature set.

**Flexible, not dogmatic.** While immutability is the core feature, Dt0 doesn't force it. Use mutable properties when needed. Expose protected properties via `with()`. The package provides capabilities; you decide how to use them.

Dt0 vs spatie/laravel-data
--------------------------

[](#dt0-vs-spatielaravel-data)

### Feature Comparison

[](#feature-comparison)

FeatureDt0spatie/laravel-dataTrue immutability (readonly)Native, enforcedOptional, by conventionLaravel validationBuilt-inBuilt-inEloquent model castingBuilt-inVia packageEncrypted propertiesBuilt-in (`EncryptedCaster`)ManualBidirectional casting`#[Cast(in:, out:, both:)]`Separate Cast + TransformerProperty renaming`#[Cast(renameFrom:, renameTo:)]``#[MapInputName]` / `#[MapOutputName]`Dependencies2 illuminate componentsFull framework service providerPHPStan level96PHP 8.1+ readonlyFirst-classSupported but not enforced### Performance

[](#performance)

Benchmarks from [`fab2s/dt0`](https://github.com/fab2s/dt0?tab=readme-ov-file#benchmarks) (reproducible via the [benchmark script](https://github.com/fab2s/dt0/blob/main/bench/Dt0Bench.php)):

OperationDt0spatie/laravel-dataSpeedupSimple DTO (8 props, 5 casts)142 µs1,158 µs~8x fasterComplex DTO (nested + arrays)742 µs3,628 µs~5x fastertoArray() (simple, cached)3.6 µs679 µs~189x fastertoArray() (nested, cached)3.6 µs2,056 µs~571x fasterTable of Contents
-----------------

[](#table-of-contents)

- [Installation](#installation)
- [Quick Start](#quick-start)
- [Real-World Example](#real-world-example)
- [Core Features](#core-features)
    - [Creating DTOs](#creating-dtos)
    - [Factory Methods](#factory-methods)
    - [Serialization](#serialization)
    - [Immutable Updates](#immutable-updates)
- [Laravel Validation](#laravel-validation)
    - [Defining Rules](#defining-rules)
    - [Rule Priority](#rule-priority)
    - [Triggering Validation](#triggering-validation)
    - [Custom Validation Rules](#custom-validation-rules)
- [Model Attribute Casting](#model-attribute-casting)
- [Casters](#casters)
    - [Built-in Casters](#built-in-casters)
    - [CollectionOfCaster](#collectionofcaster)
    - [EncryptedCaster](#encryptedcaster)
- [Artisan Generator](#artisan-generator)
- [Compatibility](#compatibility)
- [Contributing](#contributing)
- [License](#license)

Installation
------------

[](#installation)

```
composer require fab2s/laravel-dt0
```

Quick Start
-----------

[](#quick-start)

```
use fab2s\Dt0\Attribute\Rule;
use fab2s\Dt0\Attribute\Validate;
use fab2s\Dt0\Laravel\Dt0;
use fab2s\Dt0\Laravel\Validator;

#[Validate(Validator::class)]
class UserDto extends Dt0
{
    #[Rule(['required', 'string', 'max:255'])]
    public readonly string $name;

    #[Rule(['required', 'email'])]
    public readonly string $email;

    #[Rule(['nullable', 'integer', 'min:0'])]
    public readonly ?int $age;
}

// Create with validation (throws ValidationException on failure)
$user = UserDto::withValidation(
    name: 'John Doe',
    email: 'john@example.com',
    age: 30,
);

// Or create from various sources
$user = UserDto::from(['name' => 'John', 'email' => 'john@example.com']);
$user = UserDto::fromJson('{"name":"John","email":"john@example.com"}');

// Immutable — this triggers a fatal error:
// $user->name = 'Jane'; // Error!

// Serialize
$user->toArray();  // ['name' => 'John', 'email' => 'john@example.com', 'age' => 30]
$user->toJson();   // {"name":"John","email":"john@example.com","age":30}
```

Real-World Example
------------------

[](#real-world-example)

A complete request-to-response flow with validation, Eloquent casting, and typed access:

```
use fab2s\Dt0\Attribute\Cast;
use fab2s\Dt0\Attribute\Rule;
use fab2s\Dt0\Attribute\Validate;
use fab2s\Dt0\Caster\DateTimeCaster;
use fab2s\Dt0\Caster\DateTimeFormatCaster;
use fab2s\Dt0\Laravel\Dt0;
use fab2s\Dt0\Laravel\Validator;

// 1. Define the DTO
#[Validate(Validator::class)]
class CreateOrderDto extends Dt0
{
    #[Rule(['required', 'string', 'max:255'])]
    public readonly string $product;

    #[Rule(['required', 'integer', 'min:1'])]
    public readonly int $quantity;

    #[Rule(['required', 'email'])]
    public readonly string $customerEmail;

    #[Cast(in: new DateTimeCaster, out: new DateTimeFormatCaster('Y-m-d'))]
    public readonly ?DateTimeImmutable $deliveryDate;
}

// 2. In a controller — validate + hydrate in one step
public function store(Request $request): JsonResponse
{
    $order = CreateOrderDto::withValidation(...$request->all());
    // throws ValidationException with Laravel's error bag on failure

    // 3. Use with Eloquent (model casts the DTO to JSON automatically)
    $model = Order::create(['details' => $order]);

    // 4. Read back — Eloquent casts JSON back to DTO
    $model->refresh();
    $model->details->product; // typed, immutable access

    return response()->json($model);
}
```

### Form Request Integration

[](#form-request-integration)

Dt0 pairs with Form Requests for teams that prefer that pattern:

```
// Option A: Validate in Dt0 (replaces Form Request)
$dto = CreateOrderDto::withValidation(...$request->all());

// Option B: Validate in Form Request, hydrate with Dt0
class CreateOrderRequest extends FormRequest
{
    public function rules(): array { /* ... */ }

    public function toDto(): CreateOrderDto
    {
        return CreateOrderDto::from($this->validated());
    }
}

// In controller
public function store(CreateOrderRequest $request): JsonResponse
{
    $order = $request->toDto();
    // ...
}
```

Core Features
-------------

[](#core-features)

### Creating DTOs

[](#creating-dtos)

Extend `fab2s\Dt0\Laravel\Dt0` for full Laravel integration:

```
use fab2s\Dt0\Laravel\Dt0;

class ProductDto extends Dt0
{
    public readonly string $name;
    public readonly float $price;
    public readonly ?string $description;
}
```

### Factory Methods

[](#factory-methods)

Dt0 provides multiple ways to instantiate:

```
// Named arguments
$dto = new ProductDto(name: 'Widget', price: 19.99, description: null);

// Static factory
$dto = ProductDto::make(name: 'Widget', price: 19.99);

// From array
$dto = ProductDto::fromArray(['name' => 'Widget', 'price' => 19.99]);

// From JSON
$dto = ProductDto::fromJson('{"name":"Widget","price":19.99}');

// Polymorphic (accepts array, JSON string, or instance)
$dto = ProductDto::from($mixedInput);

// Safe version (returns null instead of throwing)
$dto = ProductDto::tryFrom($mixedInput);
```

**`new` vs Factory Methods**When using `new` directly with **promoted readonly properties that have a default value**, PHP initializes them immediately, **before** Dt0 can apply casting. Promoted properties without defaults behave normally.

See [Dt0 readme](https://github.com/fab2s/dt0/?tab=readme-ov-file#new-vs-factory-methods) for more details.

**Best practice**: Use factory methods (`make`, `from`, `fromArray`, etc.) for full casting support. Reserve `new` for cases where you're passing already-correct types or relying on defaults.

### Serialization

[](#serialization)

```
$dto->toArray();      // Array with objects preserved
$dto->toJsonArray();  // Array with jsonSerialize() called on nested objects
$dto->toJson();       // JSON string
(string) $dto;        // Also returns JSON (Stringable)
```

### Immutable Updates

[](#immutable-updates)

Create modified copies without mutating the original:

```
$original = ProductDto::from(['name' => 'Widget', 'price' => 19.99]);

// Clone with modifications
$updated = $original->update(price: 24.99);

// Compare instances
$original->equals($updated); // false
```

Laravel Validation
------------------

[](#laravel-validation)

Laravel Dt0 integrates seamlessly with [Laravel's validation system](https://laravel.com/docs/master/validation). Validation runs on input data **before** any casting or instantiation.

> **Note:** This package uses Laravel's `Validator` under the hood. All [Laravel validation rules](https://laravel.com/docs/master/validation#available-validation-rules), [custom rule objects](https://laravel.com/docs/master/validation#custom-validation-rules), and [error message customization](https://laravel.com/docs/master/validation#customizing-the-error-messages) work exactly as documented in Laravel.

### Defining Rules

[](#defining-rules)

Rules can be defined at three levels:

#### 1. Via `Validate` Class Attribute

[](#1-via-validate-class-attribute)

```
use fab2s\Dt0\Attribute\Rule;
use fab2s\Dt0\Attribute\Rules;
use fab2s\Dt0\Attribute\Validate;
use fab2s\Dt0\Laravel\Dt0;
use fab2s\Dt0\Laravel\Validator;

#[Validate(
    Validator::class,
    new Rules(
        name: new Rule('required|string|max:255'),
        email: new Rule('required|email'),
    ),
)]
class UserDto extends Dt0
{
    public readonly string $name;
    public readonly string $email;
}
```

#### 2. Via `Rules` Class Attribute

[](#2-via-rules-class-attribute)

```
#[Validate(Validator::class)]
#[Rules(
    name: new Rule(['required', 'string', 'max:255']),
    email: new Rule(['required', 'email']),
)]
class UserDto extends Dt0
{
    public readonly string $name;
    public readonly string $email;
}
```

#### 3. Via `Rule` Property Attribute

[](#3-via-rule-property-attribute)

```
#[Validate(Validator::class)]
class UserDto extends Dt0
{
    #[Rule(['required', 'string', 'max:255'])]
    public readonly string $name;

    #[Rule(['required', 'email'])]
    public readonly string $email;
}
```

**When to use each approach:**

ApproachBest forProperty `#[Rule]`Keeping rules close to properties, self-documenting DTOsClass `#[Rules]`Grouping rules together, inherited properties`#[Validate]` RulesDefault/fallback rules that subclasses can override### Rule Priority

[](#rule-priority)

When the same property has rules defined at multiple levels, **only the highest priority rule applies** — rules are not merged.

**Priority order:** Property `#[Rule]` &gt; Class `#[Rules]` &gt; `#[Validate]` Rules

```
#[Validate(
    Validator::class,
    new Rules(name: new Rule('min:100')),  // Lowest priority
)]
#[Rules(name: new Rule('min:50'))]          // Middle priority
class UserDto extends Dt0
{
    #[Rule('min:5')]  // Highest priority — only min:5 is applied
    public readonly string $name;
}

// Validates with min:5, NOT min:50 or min:100
$dto = UserDto::withValidation(name: 'hello'); // OK (5 chars)
```

### Triggering Validation

[](#triggering-validation)

```
use Illuminate\Validation\ValidationException;

try {
    $dto = UserDto::withValidation(...$request->all());
} catch (ValidationException $e) {
    // Handle validation errors
    $errors = $e->errors();
}
```

### Custom Validation Rules

[](#custom-validation-rules)

Use Laravel's custom rule classes:

```
use Closure;
use Illuminate\Contracts\Validation\ValidationRule;

class Lowercase implements ValidationRule
{
    public function validate(string $attribute, mixed $value, Closure $fail): void
    {
        if (strtolower($value) !== $value) {
            $fail('The :attribute must be lowercase.');
        }
    }
}

#[Validate(Validator::class)]
class SlugDto extends Dt0
{
    #[Rule(new Lowercase)]
    public readonly string $slug;
}
```

Model Attribute Casting
-----------------------

[](#model-attribute-casting)

Use DTOs directly as Eloquent model attributes with automatic JSON serialization:

```
use Illuminate\Database\Eloquent\Model;

class Order extends Model
{
    protected $casts = [
        'shipping_address' => AddressDto::class,
        'billing_address'  => AddressDto::class . ':nullable',
    ];
}
```

### Usage

[](#usage)

```
$order = new Order;

// Set from array
$order->shipping_address = ['street' => '123 Main St', 'city' => 'NYC'];

// Set from JSON
$order->shipping_address = '{"street":"123 Main St","city":"NYC"}';

// Set from DTO instance
$order->shipping_address = AddressDto::from(['street' => '123 Main St', 'city' => 'NYC']);

// Access as DTO
echo $order->shipping_address->city; // 'NYC'

// Compare
$order->shipping_address->equals(AddressDto::from(['street' => '123 Main St', 'city' => 'NYC'])); // true

// Nullable handling
$order->billing_address = null; // OK (has :nullable modifier)
$order->shipping_address = null; // Throws NotNullableException
```

### Requirements

[](#requirements)

Your DTO must either:

- Extend `fab2s\Dt0\Laravel\Dt0`, or
- Extend `fab2s\Dt0\Dt0` and use `fab2s\Dt0\Laravel\LaravelDt0Trait`

Casters
-------

[](#casters)

Casters transform property values during hydration (input) and serialization (output). The `#[Cast]` attribute supports `in:`, `out:`, and `both:` parameters:

```
// Same caster for both directions
#[Cast(both: new EncryptedCaster)]

// Different casters per direction
#[Cast(in: new DateTimeCaster, out: new DateTimeFormatCaster('Y-m-d'))]

// Combine both: with in: or out: — chained as a CasterCollection (onion ordering)
// Input runs: both → in | Output runs: out → both
#[Cast(both: new EncryptedCaster, in: new SomeSanitizer)]
```

### Built-in Casters

[](#built-in-casters)

From [`fab2s/dt0`](https://github.com/fab2s/dt0/blob/1.0.0/docs/casters.md):

CasterDescription`ScalarCaster`Converts to `int`, `float`, `bool`, or `string``ArrayOfCaster`Casts each array element to a type (scalar, Dt0, or enum)`DateTimeCaster`Parses to `DateTime`/`DateTimeImmutable``CarbonCaster`Parses to `Carbon`/`CarbonImmutable` (requires `nesbot/carbon`)`DateTimeFormatCaster`Formats DateTime to string`MathCaster`High-precision decimals (requires `fab2s/math`)`Dt0Caster`Explicit casting to a Dt0 class`ClassCaster`Instantiates arbitrary classes### CollectionOfCaster

[](#collectionofcaster)

Laravel Dt0 adds `CollectionOfCaster` for strongly-typed Laravel Collections:

```
use fab2s\Dt0\Attribute\Cast;
use fab2s\Dt0\Laravel\Caster\CollectionOfCaster;
use fab2s\Dt0\Laravel\Dt0;
use Illuminate\Support\Collection;

class OrderDto extends Dt0
{
    public readonly string $orderId;

    #[Cast(in: new CollectionOfCaster(OrderItemDto::class))]
    public readonly Collection $items;
}

// Each item in the array is cast to OrderItemDto
$order = OrderDto::from([
    'orderId' => 'ORD-123',
    'items' => [
        ['sku' => 'ABC', 'quantity' => 2],
        ['sku' => 'XYZ', 'quantity' => 1],
    ],
]);

$order->items; // Collection of OrderItemDto instances
```

Supported types:

- **Dt0 classes** — Each element cast via `Dt0::from()`
- **Enums** — Each element cast to the enum
- **Scalars** — `int`, `float`, `bool`, `string`

### EncryptedCaster

[](#encryptedcaster)

Encrypt/decrypt property values using Laravel's encryption:

```
use fab2s\Dt0\Attribute\Cast;
use fab2s\Dt0\Laravel\Caster\EncryptedCaster;
use fab2s\Dt0\Laravel\Dt0;

class UserDto extends Dt0
{
    public readonly string $name;

    #[Cast(both: new EncryptedCaster)]
    public readonly string $apiKey;
}

// Initialize with plaintext — auto-detected and passed through
$user = UserDto::from([
    'name' => 'John',
    'apiKey' => 'my-secret-key',
]);

// Or load from encrypted storage — auto-detected and decrypted
$user = UserDto::from([
    'name' => 'John',
    'apiKey' => $encryptedValue,
]);

$user->apiKey;      // Plaintext value
$user->toArray();   // ['name' => 'John', 'apiKey' => '...encrypted...']
```

**Auto-detection:** On input, the caster automatically detects Laravel's encrypted payload format. Encrypted values are decrypted, while plaintext strings, arrays, and objects pass through unchanged. This allows flexible initialization from both plaintext and encrypted sources.

**Custom encryption key and cipher:** By default, `EncryptedCaster` uses your `APP_KEY` and app cipher. When you need different values, **always use the `config:` prefix** to reference config paths rather than hardcoding key material or cipher names in your source code. This also avoids source code changes when rotating keys or updating ciphers — just update the config:

```
// Recommended — reference config paths (resolved at runtime)
#[Cast(both: new EncryptedCaster(key: 'config:services.payment.encryption_key'))]
public readonly string $paymentToken;

// Both key and cipher from config
#[Cast(both: new EncryptedCaster(
    key: 'config:services.payment.encryption_key',
    cipher: 'config:services.payment.cipher',
))]
public readonly string $secret;
```

The config values support `base64:`-encoded keys, just like `APP_KEY`:

```
// config/services.php
'payment' => [
    'encryption_key' => env('PAYMENT_ENCRYPTION_KEY'), // base64:...
    'cipher'         => env('PAYMENT_CIPHER', 'AES-256-CBC'),
],
```

**Other options:**

```
// Serialize complex values (arrays, objects)
new EncryptedCaster(serialize: true)

// Direct key/cipher (for programmatic usage only — never hardcode in attributes)
new EncryptedCaster(key: 'base64:...', cipher: 'AES-128-CBC')
```

**Performance:** `Encrypter` instances are statically cached by key and cipher combination. Multiple DTO instances or properties using the same encryption key share a single `Encrypter`, avoiding repeated instantiation overhead.

**Stack trace safety:** On PHP 8.2+, all sensitive parameters (keys, plaintext values) are annotated with `#[\SensitiveParameter]` and redacted from exception stack traces. On PHP 8.1, the attribute is silently ignored.

**Eloquent model safety:** When using an `EncryptedCaster` DTO as an [Eloquent model attribute](#model-attribute-casting), calling `$model->toArray()` or `$model->toJson()` will trigger the DTO's output casters, meaning encrypted fields are **always encrypted** in the serialized output. Plaintext is only accessible through direct property access on the DTO instance (`$model->myDto->apiKey`).

```
class SecureModel extends Model
{
    protected $casts = [
        'credentials' => CredentialsDto::class,
    ];
}

$model->credentials->apiKey;     // Plaintext (direct access)
$model->toArray()['credentials'] // ['apiKey' => '...encrypted...']
```

Artisan Generator
-----------------

[](#artisan-generator)

Scaffold new DTOs with the `make:dt0` Artisan command:

```
# Create a basic DTO
php artisan make:dt0 UserDto
# Creates app/Dto/UserDto.php

# Create with validation scaffolding
php artisan make:dt0 UserDto --validated
# Creates with #[Validate] and #[Rule] attributes
```

The service provider is auto-discovered — no manual registration needed.

Compatibility
-------------

[](#compatibility)

PHPLaravelStatus8.110.xSupported8.210.x, 11.xSupported8.310.x, 11.x, 12.xSupported8.411.x, 12.xSupportedContributing
------------

[](#contributing)

Contributions are welcome! Please feel free to:

- Open issues for bugs or feature requests
- Submit pull requests
- Improve documentation

```
# fix code style
composer fix

# run tests
composer test

# run tests with coverage
composer cov

# static analysis (src, level 9)
composer stan

# static analysis (tests, level 5)
composer stan-tests
```

License
-------

[](#license)

Laravel Dt0 is open-sourced software licensed under the [MIT license](http://opensource.org/licenses/MIT).

###  Health Score

45

—

FairBetter than 93% of packages

Maintenance82

Actively maintained with recent releases

Popularity25

Limited adoption so far

Community7

Small or concentrated contributor base

Maturity53

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~219 days

Total

4

Last Release

91d ago

Major Versions

0.0.1 → 1.0.02026-02-08

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/7323989?v=4)[fab2s](/maintainers/fab2s)[@fab2s](https://github.com/fab2s)

---

Top Contributors

[![fab2s](https://avatars.githubusercontent.com/u/7323989?v=4)](https://github.com/fab2s "fab2s (19 commits)")

---

Tags

data-transfer-objectdtoimmutablelaravelphpreadonlyphpjsonsymfonylaravelserializabledata-transfer-objectdtoimmutabledata processingreadonlyDT0

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan

Code StyleLaravel Pint

Type Coverage Yes

### Embed Badge

![Health badge](/badges/fab2s-laravel-dt0/health.svg)

```
[![Health](https://phpackages.com/badges/fab2s-laravel-dt0/health.svg)](https://phpackages.com/packages/fab2s-laravel-dt0)
```

###  Alternatives

[rumenx/php-sitemap

Framework-agnostic Sitemap generator for PHP, Laravel, and Symfony.

1.3k15.1k1](/packages/rumenx-php-sitemap)[fab2s/yaetl

Widely Extended Nodal Extract-Transform-Load ETL Workflow AKA NEJQTL or Nodal-Extract-Join-Qualify-Tranform-Load

64181.0k](/packages/fab2s-yaetl)[fab2s/dt0

Immutable DTOs with bidirectional casting. No framework required. 8x faster than the alternative.

101.6k1](/packages/fab2s-dt0)[sbsaga/toon

🧠 TOON for Laravel — a compact, human-readable, and token-efficient data format for AI prompts &amp; LLM contexts. Perfect for ChatGPT, Gemini, Claude, Mistral, and OpenAI integrations (JSON ⇄ TOON).

6115.6k](/packages/sbsaga-toon)[event4u/data-helpers

Framework-agnostic PHP library for data mapping, DTOs and utilities. Includes DataMapper, SimpleDto/LiteDto, DataAccessor/Mutator/Filter and helper classes (MathHelper, EnvHelper, etc.). Works with Laravel, Symfony/Doctrine or standalone PHP.

1421.5k](/packages/event4u-data-helpers)[ernysans/laraworld

Countries, Languages and Time Zones Package for Laravel 5.\*

1755.7k](/packages/ernysans-laraworld)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
