Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/dependency-review.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,6 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: 'Checkout Repository'
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: 'Dependency Review'
uses: actions/dependency-review-action@v2
uses: actions/dependency-review-action@v4
6 changes: 3 additions & 3 deletions .github/workflows/fix-php-code-style-issues.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,14 @@ jobs:

steps:
- name: Checkout code
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
ref: ${{ github.head_ref }}

- name: Fix PHP code style issues
uses: aglipanci/laravel-pint-action@latest
uses: aglipanci/laravel-pint-action@2.4

- name: Commit changes
uses: stefanzweifel/git-auto-commit-action@v4
uses: stefanzweifel/git-auto-commit-action@v5
with:
commit_message: Fix styling
2 changes: 0 additions & 2 deletions .github/workflows/phpunit-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,6 @@ jobs:
testbench: 10.*

exclude:
- laravel: 12.*
php: 8.1
- laravel: 10.*
php: 8.4
- laravel: 10.*
Expand Down
104 changes: 93 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -547,10 +547,10 @@ class OrderDTO extends ArgonautImmutableDTO
|---------|---------------|------------------------|
| Property modification | ✅ Allowed | ❌ Blocked (readonly) |
| Custom setters | ✅ `setPropertyName()` | ❌ Not supported |
| `setAttributes()` | ✅ Available | ❌ Not available |
| Casting | ✅ Full support | ✅ Full support |
| `setAttributes()` / `merge()` | ✅ Available | ❌ Not available |
| Casting (DTOs, enums, dates) | ✅ Full support | ✅ Full support |
| Validation | ✅ Full support | ✅ Full support |
| Serialization | ✅ Full support | ✅ Full support |
| Serialization (`toArray`, `toJson`, `only`, `except`) | ✅ Full support | ✅ Full support |
| Assembler integration | ✅ Full support | ✅ Full support |
| Nested assemblers | ✅ Full support | ✅ Full support |
| PHP requirement | 8.2+ | 8.2+ (readonly properties) |
Expand Down Expand Up @@ -611,24 +611,50 @@ class UserDTO extends ArgonautDTO

## 🔁 Casting Reference

Casting allows you to automatically transform values into other DTOs, Laravel Collections, arrays, dates, and more.
Casting allows you to automatically transform values into other DTOs, Laravel Collections, arrays, dates, enums, and more.

```php
protected array $casts = [
'registeredAt' => \Illuminate\Support\Carbon::class,
'profile' => ProfileDTO::class,
'roles' => [RoleDTO::class],
'permissions' => Collection::class . ':' . PermissionDTO::class,
'status' => StatusEnum::class,
'tags' => [TagEnum::class],
'priorities' => Collection::class . ':' . PriorityEnum::class,
];
```

| Cast Type | Example | Description |
|--------------------|-----------------------------------------------|----------------------------------|
| Scalar | `'string'`, `'int'`, etc. | Native PHP type cast |
| Single DTO | `ProfileDTO::class` | Cast an array to a DTO instance |
| Array of DTOs | `[RoleDTO::class]` | Cast to array of DTOs |
| Collection of DTOs | `Collection::class . ':' . CommentDTO::class` | Cast to a Laravel Collection |
| Date casting | `Carbon::class` | Cast to Carbon/DateTime instance |
| Cast Type | Example | Description |
|-------------------------|---------------------------------------------------|-----------------------------------------|
| Single DTO | `ProfileDTO::class` | Cast an array to a DTO instance |
| Array of DTOs | `[RoleDTO::class]` | Cast to array of DTOs |
| Collection of DTOs | `Collection::class . ':' . CommentDTO::class` | Cast to a Laravel Collection |
| Date casting | `Carbon::class` | Cast to Carbon/DateTime instance |
| BackedEnum | `StatusEnum::class` | Cast a raw value to a BackedEnum |
| Array of Enums | `[TagEnum::class]` | Cast to array of BackedEnum instances |
| Collection of Enums | `Collection::class . ':' . PriorityEnum::class` | Cast to a Collection of BackedEnums |

### BackedEnum Casting

PHP `BackedEnum` types are automatically detected and cast using `Enum::from()`. Existing enum instances are passed through unchanged. On serialization, enums are converted back to their backing value.

```php
use App\Enums\StatusEnum; // enum StatusEnum: string { case Active = 'active'; ... }

class TaskDTO extends ArgonautDTO
{
public ?StatusEnum $status = null;

protected array $casts = [
'status' => StatusEnum::class,
];
}

$task = new TaskDTO(['status' => 'active']);
$task->status; // StatusEnum::Active
$task->toArray()['status']; // 'active'
```

---

Expand All @@ -653,6 +679,62 @@ $userDTO->toArray(); // Recursively converts nested DTOs
$userDTO->toJson(); // JSON output (throws on encoding errors)
```

### Partial Serialization

Use `only()` and `except()` to serialize a subset of properties:

```php
$user = new UserDTO([
'username' => 'jdoe',
'email' => 'jdoe@example.com',
'firstName' => 'John',
'lastName' => 'Doe',
]);

$user->only('username', 'email');
// ['username' => 'jdoe', 'email' => 'jdoe@example.com']

$user->except('email');
// ['username' => 'jdoe', 'firstName' => 'John', 'lastName' => 'Doe', ...]
```

---

## 🔄 Merging Attributes

Mutable DTOs support merging additional attributes after construction:

```php
$user = new UserDTO(['email' => 'old@example.com', 'firstName' => 'John']);

$user->merge(['email' => 'new@example.com']);

$user->email; // 'new@example.com'
$user->firstName; // 'John' (unchanged)
```

`merge()` returns the same instance, so it can be chained:

```php
$user->merge(['firstName' => 'Jane'])->merge(['lastName' => 'Doe']);
```

> **Note**: `merge()` is only available on `ArgonautDTO`. Immutable DTOs do not support post-construction modification.

---

## 🧩 Trait-Based Architecture

Under the hood, both `ArgonautDTO` and `ArgonautImmutableDTO` compose shared behavior from three traits:

| Trait | Provides |
|-------|----------|
| `HasCasting` | `castInputValue()`, enum/DTO/collection/date casting |
| `HasSerialization` | `toArray()`, `toJson()`, `only()`, `except()`, `collection()` |
| `HasValidation` | `validate()`, `isValid()` |

This is transparent to most users, but if you are extending internal behavior (e.g., overriding `castInputValue()` in a subclass), note that these methods now live in traits rather than directly on the base class. Method resolution is identical for inheritance purposes.

---

## 🛠️ DTO Collection Helper
Expand Down
4 changes: 2 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,9 @@
},
"require-dev": {
"laravel/pint": "^1.0",
"orchestra/testbench": "^7.0|^8.0|^9.0|^10.0",
"orchestra/testbench": "^8.0|^9.0|^10.0",
"phpstan/phpstan": "^2.0",
"phpunit/phpunit": "^9.0|^10.0|^11.5.3"
"phpunit/phpunit": "^10.0|^11.5.3|^12.0"
},
"autoload": {
"psr-4": {
Expand Down
Loading