flying/struct
flying/struct lets PHP and Laravel projects define typed, immutable data structures: declare fields once; hydrate them from arrays or requests, validate values, and convert back to arrays or JSON. Lightweight, framework-friendly, and easy to drop into existing codebases.
This release is a maintenance/compatibility release for flyingdr/struct, not a feature release — but it contains a BC break, so treat the upgrade as a deliberate step rather than a routine composer update.
Minimal steps:
composer require flyingdr/struct:^0.3.@var Int → @var Integer@var String → @var StrInt / String in property docblocks or annotations.Where to look first:
1b03ec9…) is the authoritative diff for the rename; skim it before upgrading a large codebase so you know exactly which identifiers changed.First use case in a Laravel app: define a struct for an inbound payload or an outbound API shape so you stop passing raw associative arrays around.
use FlyingDR\Struct\Struct;
class CreateUserStruct extends Struct
{
/** @var Integer */
public $id;
/** @var Str */
public $name;
/** @var Str */
public $email;
}
$user = new CreateUserStruct($request->validated());
The struct gives you typed, named properties instead of $data['name'] sprinkled across controllers and jobs.
Use structs as a boundary object, not as a domain model. The sweet spot is at the edges of your app: request payloads, job payloads, third-party API responses, and event data. Instantiate the struct once, validate/sanitize at that edge, then pass the typed object inward. This keeps your services free of array_key_exists noise.
Pair with Form Requests rather than replacing them. Let Laravel's validation own the rules and let the struct own the shape:
public function store(StoreUserRequest $request)
{
$struct = new CreateUserStruct($request->validated());
CreateUserJob::dispatch($struct);
// ...
}
Because the struct is serializable, it travels through queues cleanly and arrives typed on the other side.
Centralize a DTO namespace. Put structs in app/Structs (or app/DataTransferObjects) and keep one struct per payload shape. Resist the urge to make them polymorphic; a flat, boring struct is easier to reason about and easier to map to an Eloquent model or API Resource.
Map explicitly at the seams. Write a small toArray()/toModel() hop in your controller or a dedicated mapper rather than teaching the struct about Eloquent. That keeps the struct reusable for non-DB flows (webhooks, imports) and avoids coupling your transport layer to your persistence layer.
Compose for nested payloads. For a payload with a nested address object, define a separate AddressStruct and construct it inside the parent's factory method. This keeps each struct testable in isolation and makes the JSON shape obvious.
Use them in tests as fixtures. Because structs are typed and self-documenting, they make excellent test inputs: new CreateUserStruct(['id' => 1, 'name' => 'x', 'email' => '[email protected]']) is clearer than a bare array, and a bad fixture fails fast on the type check.
Int and String were invalid class names under PHP 7, so they became Integer and Str. Any struct still annotated with the old names will silently miss type handling or throw — either way, audit every docblock. A grep -rn "@var \(Int\|String\)\b" across app/ is the fastest sweep.^0.2 to ^0.3 is a breaking change under semver pre-1.0, so composer update in CI will fail rather than silently break production. Prefer pinning and upgrading on purpose.How can I help you explore Laravel packages today?