cline/struct

Lean, attribute-driven Laravel data objects with explicit validation and Eloquent casting support

Maintainers

Package info

github.com/faustbrian/struct

pkg:composer/cline/struct

Statistics

Installs: 1 653

Dependents: 8

Suggesters: 0

Stars: 0

Open Issues: 0

2.1.2 2026-03-20 03:44 UTC

This package is auto-updated.

Last update: 2026-03-20 03:46:11 UTC


README

GitHub Workflow Status Latest Version on Packagist Software License Total Downloads

struct

Lean, attribute-driven Laravel data objects for applications that want the useful parts of spatie/laravel-data without the magic-heavy surface area.

Requirements

Requires PHP 8.4+

Installation

composer require cline/struct

Optional features:

composer require cline/math
composer require cline/money
composer require cline/numerus
composer require cline/phone-number
composer require cline/postal-code
composer require ramsey/uuid
composer require cline/semver

Install cline/math when you want first-class arbitrary-precision BigNumber, BigInteger, BigDecimal, or BigRational DTO properties.

composer require cline/money

Install cline/money when you want first-class Money, RationalMoney, or MoneyBag DTO properties, or the #[AsMoney(...)] attribute for scalar currency amounts.

composer require cline/numerus

Install cline/numerus when you want first-class Numerus DTO properties or numeric normalization attributes such as #[Round], #[Clamp], and #[Abs].

composer require cline/phone-number

Install cline/phone-number when you want first-class PhoneNumber DTO properties, including scalar local-number hydration with #[AsPhoneNumber(regionCode: ...)].

composer require cline/postal-code

Install cline/postal-code when you want first-class PostalCode DTO properties, including scalar postal-code hydration with #[AsPostalCode(country: ...)].

composer require cline/semver

Install cline/semver when you want first-class Version and Constraint DTO properties for semantic version payloads.

Struct also ships built-in deterministic string transformation attributes such as #[Trim], #[SnakeCase], #[Slug], #[Limit], #[Replace], and extraction attributes like #[After], #[Before], and #[Between]. It also ships missing-value generators such as #[Uuid], #[Ulid], #[Random], and #[Password] for DTO fields that should be created only when the input key is absent. Install ramsey/uuid if you want to use #[Uuid(...)]. String transforms and generators now live under Cline\Struct\Attributes\Strings, while compatibility aliases remain available at Cline\Struct\Attributes. Collection transforms now live under Cline\Struct\Attributes\Collections, including helpers such as #[Collections\\Reverse], #[Collections\\Unique], #[Collections\\Slice], and #[Collections\\OnlyKeys]. Struct also ships first-class detached Illuminate\\Support\\Collection property support through #[AsCollection(...)], separate from DataList and DataCollection, including callback-based collection attributes such as #[Collections\\Filter(...)], #[Collections\\Map(...)], #[Collections\\GroupBy(...)], #[Collections\\UniqueBy(...)], and #[Collections\\MapInto(...)]. Collection-returning transforms also include attributes such as #[Collections\\Where(...)], #[Collections\\Pluck(...)], #[Collections\\Flatten(...)], #[Collections\\ChunkWhile(...)], #[Collections\\MapToGroups(...)], #[Collections\\CountBy(...)], #[Collections\\Diff(...)], #[Collections\\Merge(...)], #[Collections\\Pipe(...)], #[Collections\\Tap(...)], and conditional wrappers like #[Collections\\When(...)]. Derived collection results and generated collection sources live under Cline\\Struct\\Attributes\\CollectionResults and Cline\\Struct\\Attributes\\CollectionSources, for methods such as #[Contains('posts', ...)], #[After('posts', ...)], #[ToJson('posts')], #[Reduce('totals', ...)], #[Wrap(source: 'name')], and #[FromJson(source: 'payload')]. For deferred traversal, Struct also ships LazyDataList and LazyDataCollection with explicit #[AsLazyDataList(...)] and #[AsLazyDataCollection(...)] attributes. These lazy wrappers keep Struct-owned collection semantics and typed item hydration, but they are transport-focused and intentionally reject Attributes\\Collections\\* transforms in v1. Struct also now supports detached Illuminate\\Support\\LazyCollection DTO properties through #[AsLazyCollection(...)], with a strict-lazy transform subset such as #[Collections\\Map(...)], #[Collections\\Filter(...)], #[Collections\\Skip(...)], and #[Collections\\Take(...)]. Derived CollectionResults may read from lazy sources, but eager-only collection transforms are rejected on LazyCollection properties.

Context-Aware Hydration

Hydration hooks can opt into whole-DTO context when a cast or attribute needs to inspect the raw payload or sibling values that have already been resolved.

use Cline\Struct\Contracts\ContextualCastInterface;
use Cline\Struct\Metadata\PropertyMetadata;
use Cline\Struct\Support\PropertyHydrationContext;

final class SlugCast implements ContextualCastInterface
{
    public function get(PropertyMetadata $property, mixed $value): mixed
    {
        return $value;
    }

    public function getWithContext(
        PropertyMetadata $property,
        mixed $value,
        PropertyHydrationContext $context,
    ): mixed {
        return ($context->resolvedProperties['mode'] ?? null) === 'strict'
            ? mb_strtolower((string) $value)
            : (string) $value;
    }

    public function set(PropertyMetadata $property, mixed $value): mixed
    {
        return $value;
    }
}

PropertyHydrationContext exposes the DTO class, current property, raw input, and the sibling properties resolved so far. The context is passed as an immutable argument during hydration; Struct does not mutate shared cast instances with request-scoped data.

Documentation

Change log

Please see CHANGELOG for more information on what has changed recently.

Contributing

Please see CONTRIBUTING and CODE_OF_CONDUCT for details.

Security

If you discover any security related issues, please use the GitHub security reporting form rather than the issue queue.

Credits

License

The MIT License. Please see License File for more information.