Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Simple Data Objects Laravel Package

std-out/simple-data-objects

Lightweight, attribute-driven DTOs for PHP 8.4+ with zero runtime reflection and compiled per-class closures for fast hydration and serialization. Works standalone or in Laravel 12–13.

View on GitHub
Deep Wiki
Context7

Product Decisions This Supports

Build vs. buy on the DTO / payload layer. Every Laravel product eventually grows a boundary layer: FormRequests, API Resources, hand-rolled fromArray() constructors, or a DTO library. This package is a credible "buy" option for that layer, and the decision it most directly informs is whether to keep hand-rolling hydration/serialization or adopt an attribute-driven library. The pitch is concrete: constructor properties plus attributes replace boilerplate, and fromValidated() collapses validate → pipe → cast → hydrate into one call. v2.8.0 materially changes the economics of this decision by adding code generation (vendor/bin/sdo-generate, DataClassGenerator::fromValue()/write(), and make:data --from-json|--from-xml|--from-model). The "buy" cost was previously boilerplate savings minus learning curve; now the learning curve itself is partly automated — you scaffold from a real payload instead of writing the attribute DSL cold.

Brownfield migration and integration scaffolding. The most immediately useful new decision this informs is how cheaply can we seed the boundary layer on an existing system? Point sdo-generate at a captured sample response, a webhook body, or a partner feed and get one class per object shape with nested DTOs, #[DataCollection] lists and key mapping inferred. That turns "we'd have to rewrite 40 payloads first" from a quarter-scale project into a day, and makes the tool relevant to third-party API integrations — a roadmap category where you consume someone else's schema rather than design your own.

XML and legacy-partner ingestion. XML attribute / text / element inference is a genuinely differentiating capability and a product-level unlock: partner feeds, SOAP-era integrations and sitemap/config ingestion are usually the reason a team writes a bespoke parser. If your roadmap has any XML source, this is now a stronger reason to look than the performance story alone.

Feature and roadmap bets on performance-sensitive paths. The compiled-closure, zero-reflection hot path (plus MetadataRegistry::setStoragePath() and sdo-warm for deploy-time pre-warming) makes this relevant wherever payload volume is a real cost center: high-throughput APIs, queue workers deserializing jobs, webhook ingestion, report exports. If your roadmap includes scaling traffic without scaling fleet size, this is a legitimate lever — but benchmark against your payload shapes via the companion benchmark repo, not the README table.

Streaming and bulk-ingestion initiatives. lazyCollection() and flat ~12 KB peak memory on 100k-row imports is an architectural claim (no full materialization), and that's the kind of thing that unblocks a CSV/ETL feature that would otherwise require chunking infrastructure or a separate service. This is arguably the strongest product story here: it enables a feature rather than just optimizing one.

API contract and frontend-type generation. jsonSchema() and sdo-typescript turn your DTOs into a single source of truth for JSON Schema (draft 2020-12) and .d.ts output. For teams with a TypeScript frontend, that's a roadmap item — shared contract generation — that would otherwise be a bespoke script or a separate codegen tool. With v2.8.0 this closes a loop: sample payload → generated DTO → generated schema/TS, all from one artefact.

PATCH semantics and partial-update correctness. The Optional type distinguishing "key absent" from "key sent as null" is a small feature with outsized product impact: it's the difference between PATCH silently wiping fields and PATCH behaving correctly. --optional now propagates that automatically during generation — keys missing from some objects in the sample become T|Optional, so the generated class round-trips the sample exactly. If your API surface has partial updates, this removes a class of bugs that's expensive to find in production.

Testing and error-UX decisions. fromResult() accumulating all field errors with dot-paths (including nested DTOs and collections) is a user-facing decision — you can return complete validation feedback in one round-trip instead of drip-feeding errors. Worth weighing against your existing validation error contract.

Consolidation of a fragmented internal pattern. If multiple teams have each written their own DTO/hydrator, adopting one library is a standardization decision. This package's attribute set (Cast, Rules, Pipe, Flatten, Hidden, MapPropertyName, Discriminator, WrapIn, TransformKeys) is broad enough to absorb most bespoke cases — polymorphic hydration via #[Discriminator] and #[DataCollection] in particular cover patterns people usually hand-roll. Generation makes the standard teachable: new teams copy a generated class rather than reading docs.

When to Consider This Package

Adopt (or at minimum spike) when most of these hold:

  • You're on PHP 8.4+ and Laravel 12–13 — this is a hard floor, not a soft preference. On older PHP this is a non-starter.
  • You have a genuine hot path: high request volume, large queue throughput, or bulk import/export. The performance argument is the primary differentiator; without a hot path you're paying adoption risk for a benefit you won't feel.
  • You want validation to work outside a booted Laravel app — useful for CLI tools, workers, or a service that may later split out of the monolith.
  • You have a TypeScript frontend and want generated contracts from the same metadata, rather than duplicating types.
  • You accept attribute-heavy, declarative DTOs as a team convention. This is a real style commitment — though v2.8.0 lowers the cost of entry substantially, since the first draft comes from a real payload and make:data --from-model=Order --id=1 --with=items shows the pattern on your own domain.
  • You're prepared to pin the version, vendor or fork if necessary, and wrap the library behind your own thin interface so a swap is a contained change.

Look elsewhere when:

  • PHP version, framework version, or upgrade cadence doesn't match. This is the fastest disqualifier.
  • Adoption signal matters to your risk model. Still a very thin ecosystem — no Stack Overflow corpus, no third-party tutorials, no community that has already hit your edge case. Generation reduces onboarding cost but not bus-factor risk; weigh them separately.
  • You need deep Eloquent integration or a large catalog of pre-built casts, rules, and integrations. Note --from-model reads a real record but still emits DTOs, not Eloquent-coupled models — if you want the ORM to do the hydration, this is the wrong tool.
  • You need commercial support, a security-response SLA, or a multi-maintainer governance model. MIT with a small maintainer footprint means you own the risk.
  • Your DTO usage is light AND your payloads are unstable — a dozen simple payloads is now cheap to scaffold, but if the shapes churn every sprint you're regenerating and reviewing more than you'd write.
  • You need guaranteed backward compatibility across major versions without reading changelogs. 2.8.0 is explicitly backward compatible with 2.7.x and purely additive, but the project's overall cadence is fast and has altered semantics before (1.7.2 changed from()/fromJson() behaviour).

Caution on the new generation features — treat output as a draft, not a spec. Inference is sample-driven: types come from observed values, --optional only knows about keys missing from that sample, and nulls, empty arrays, stringly-typed numbers and mixed-type fields will be under-specified. Generated classes need a review pass and a second sample (an edge-case payload) before they go near production. The generator is an accelerator for the spike, not a substitute for modelling.

Recommended adoption path: timeboxed spike (now closer to a few days than two weeks) on one real, performance-relevant slice. Start by running sdo-generate against a captured payload from that slice — it gives you a working DTO, a feel for the attribute DSL, and a concrete artefact to review with the team in the first hour. Then benchmark against both your current code and the popular alternative on your own payloads. Gate full rollout on the spike clearing your latency/memory targets and on a documented fallback: DTOs behind an internal interface, library pinned, fork procedure written down.

How to Pitch It (Stakeholders)

To executives: This is a small, MIT-licensed PHP library that replaces the hand-written and library-based glue code between HTTP input, validation, and our internal data objects, and as of the latest release it also writes that glue code for us from a sample payload. Three things make it worth a look rather than a pass. First, it's measurably faster — the authors publish 20–60× throughput advantages over the most popular alternative in this category, with a public benchmark repo so we can verify on our own payloads. Less CPU per request means more headroom per server, a direct cost lever. Second, it generates JSON Schema and TypeScript types from the same definitions, reducing frontend/backend contract drift. Third, the new generator turns onboarding from a training exercise into a command: point it at a real API response or partner feed and it produces the classes, which cuts the cost and risk of trying it. It's a bounded, reversible decision: MIT license, no vendor lock-in, standard Composer dependency, isolated behind an internal interface so it can be swapped. The caveat I'd flag up front is unchanged: it's a young project with a very small maintainer footprint and essentially no known dependents. Generated code is a starting draft, not a validated data contract — it needs engineering review. That's why I'm recommending a scoped evaluation on one real workload rather than an org-wide standard, with the version pinned and a fork path documented.

To engineering: The technical case is a compiled per-class closure for from()/toArray() — plain properties become direct array reads, zero reflection and zero dispatch on the hot path. Enable the file cache and pre-warm on deploy with sdo-warm, and every worker starts with opcache-compiled metadata and hydration code. Streaming via lazyCollection() keeps peak memory flat regardless of row count — architecture, not micro-optimization. The feature surface is broad: casts, Laravel rules, middleware-style Pipe preprocessing at class and property level, #[Discriminator] for polymorphic hydration, typed collections with IDE generics, immutable with()/diff(), never-throw fromResult() with dot-path errors, Optional for correct PATCH semantics, and schema/TS generation off the same metadata. Standalone validation works with no Laravel app booted. New in 2.8.0: vendor/bin/sdo-generate <sample.json|sample.xml|-> <out-dir> and DataClassGenerator::fromValue()/write() emit one class per object shape with inferred property types, nested DTOs, #[DataCollection] lists, #[TransformKeys]/#[MapPropertyName] key mapping and XML attribute/text/element handling; --optional emits T|Optional for keys absent from some sample objects so the sample round-trips; make:data gains --from-json, --from-xml and --from-model=Order --id=1 --with=items, the last generating nested classes from loaded relations. Additive and backward compatible with 2.7.x. What you're accepting: a hard PHP 8.4+ floor, an attribute-heavy DSL that's a real convention commitment, a fast-moving release cadence (read the changelog), a very small community, and ownership of the dependency if it goes quiet. Also accept that inferred types are only as good as the sample — budget a review pass and a second, adversarial sample before trusting generated classes in production. Mitigations: wrap it behind our own interface, pin the version, keep the fork procedure in the ADR, and validate the performance claims on our payloads before committing.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
robertmarney/lara-hierarchial-collections
simonschaufi/rule-doc-generator
emissaoja/emissaoja
blackbricksoftware/gravityforms-civicrm-form-processor
al-rahabi/fancy-crud
jinas/dhivatar-component
mltstephane/laravel-quality
bernskiold/laravel-talentlms
bernskioldmedia/laravel-form-architect
youbar/easy-crud
c-point-labs/laravel-hostyourai
dbravoan/dbcms-admin-theme-dbcms
dbravoan/dbcms-theme-db-cyber
dbravoan/dbcms-blocks-basic
dbravoan/dbcms-module-content-audit
dbravoan/dbcms-module-ai-assistant
paygate-to/paygate-php
padosoft/askmydocs-connector-base
defstudio/supercharged-carbon
bernskioldmedia/laravel-matomo