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

Schema Laravel Package

nette/schema

Nette Schema is a lightweight PHP library for validating and normalizing structured data. Define schemas to check arrays and objects, coerce values, and generate clear error messages for configs, APIs, and form input.

View on GitHub
Deep Wiki
Context7

Technical Evaluation

Architecture fit

  • nette/schema is a framework-agnostic schema definition and validation library (Nette component, but zero Nette framework coupling; single runtime dependency on nette/utils). It fits Laravel as a boundary layer — an anti-corruption layer at the point where untrusted or unstructured data enters the application (bootstrap config, JSON API payloads, third-party webhook bodies, YAML/JSON import jobs).
  • It deliberately overlaps Laravel's Validator and FormRequest. It is not a replacement: Laravel's validator is rules-array-driven, request-scoped, and integrates with MessageBag/422 responses. nette/schema is schema-as-code — immutable, composable, type-object based, with normalization and default-merging built in. The overlap is a governance problem, not a technical one.
  • Strongest architectural argument: declarative schemas are testable artifacts and can be reused across config loading, DTO hydration, and CLI commands, which Laravel's string-keyed rules arrays do not support cleanly.
  • Weakest architectural argument: no Laravel-native integration exists (no service provider, no Rule bridge, no container bindings). All integration glue is ours to own.

Integration feasibility

  • composer require nette/schema (target ^1.3). No conflicts with Laravel's dependency tree; PHP 8.1+ requirement aligns with Laravel 10/11/12. Blocked only for legacy apps on PHP 8.0 or Laravel ≤9.
  • Public surface is small and stable: Expect, Processor, Structure, ValidationException, Message. A competent Laravel engineer is productive in half a day.
  • Effort estimate: license clearance (days, calendar-bound), spike + config adapter (1–2 engineer-days), error/response mapping adapter (2–3 days), pilot endpoint + DTO convention (1 sprint), documented standard + rollout (1–2 sprints). Total realistic path to a defended production footprint: one quarter, part-time, for config scope only — longer if API ingress is in scope.

Technical risk

  • License: NOASSERTION. This is the single blocking item. The Nette ecosystem is conventionally BSD-3-Clause, but GitHub's classifier did not recognize the file. Do not adopt until legal explicitly clears it. Flag in the ADR; if it fails, fallback candidates are opis/json-schema (if OpenAPI is the source of truth) or respect/validation.
  • Two validation dialects. If nette/schema lands for APIs without an explicit boundary policy, the codebase will have FormRequest rules in some controllers and Expect::structure() in others. That's a durable consistency tax and a reviewer burden.
  • Schema drift vs. contract of record. Hand-written Expect schemas are invisible to OpenAPI/TypeScript clients. If the org's API contract lives in OpenAPI, this package introduces a second source of truth for the same payload. That's a product-level risk (client breakage, stale docs), not just a code risk.
  • Silent data-shape hazards. Documented gotchas with real incident potential: Expect::array() strips numeric keys (silent data loss), default() does not fire on present-but-null keys, transform() used for side effects. These are training and code-review controls, not blockers.
  • Overly strict schema deployed to live clients is a self-inflicted outage vector. Requires additive-only schema evolution discipline.
  • Vendor concentration. Effectively a single-maintainer project. Mitigating factor: it is a transitive dependency of nette/di, nette/application, Latte, and much of the PHP ecosystem, so real-world install base is orders of magnitude larger than the low dependents figure reported here (that metric is not trustworthy for this package). Abandonment risk is low; direction risk is non-zero. Fork cost is small given the codebase size.
  • No partial-result mode. Failure is exception-based; you get all messages from one process() call, but there's no streaming/collect-and-continue API. Fine for request validation; awkward for bulk import pipelines where you want per-record error reports without exception control flow.

Key questions

  1. Is OpenAPI / JSON Schema the system of record for any API contract we'd validate here? If yes, this package is the wrong tool for that surface and we should scope it strictly to config/internal boundaries.
  2. What is the actual scope: config bootstrap only, API ingress, or bulk data pipelines? These have very different risk profiles.
  3. Has legal cleared a BSD-3-Clause / NOASSERTION dependency, and who signs off?
  4. Are we willing to run a second validation dialect with a written boundary policy, or do we need a single house style?
  5. DTO strategy: castTo() into plain readonly classes, or spatie/laravel-data, or neither? This decision is harder to reverse than the package choice itself.
  6. Who owns the schema definitions in six months, and are schemas covered by tests (valid + invalid fixtures) as a merge requirement?

Integration Approach

Stack fit

  • Laravel 10/11/12, PHP 8.1–8.5: clean fit. Laravel ≤9 / PHP 8.0: out of scope, do not attempt.
  • No global state, no facades, no container requirements, no config publishing — so it can be introduced additively without touching the framework's validation pipeline. Existing FormRequest behavior remains untouched until we deliberately change it.
  • nette/utils overlap with other packages is possible but benign; verify in composer why nette/utils during the spike.

Migration path (phased, reversible)

  1. License + spike (no production code). Clear legal, composer require in a scratch branch, validate one real config array, confirm no dependency conflicts, run the existing test suite.
  2. Config bootstrap validation. Highest value, lowest blast radius: schema-validate the merged config tree in a bootstrap step or dedicated service provider before service resolution. Purely additive; a failure here is a deploy-time fail-fast, which is a product win (bad config caught before traffic).
  3. Error-mapping adapter. Build the one piece of glue that matters: convert ValidationException::getMessages() (dot-path + code + message) into (a) Laravel's 422 JSON error envelope and (b) MessageBag for Blade forms. This adapter is the reusable asset; the package itself is commodity.
  4. Pilot one API endpoint behind a feature flag. Only an endpoint where nested payload validation currently produces poor error messages. Measure: error-message clarity, client-reported 4xx quality, lines of validation code removed.
  5. Publish an internal standard, then expand. Written rule: schema-based validation for nested/composite payloads and config; FormRequest for flat, form-bound, user-facing inputs. No wholesale migration.
  • Explicitly out of scope for v1: migrating existing request validation, replacing Validator for simple forms, and generating client SDKs from schemas.

Compatibility

  • Runtime coexistence with the Laravel validator is frictionless. The friction is entirely at the error-surface and DTO boundary — hence step 3 before step 4.
  • PHPStan-friendly (upstream runs static analysis clean at a high level); adopt a project-level phpstan.neon baseline so schema misuse is caught in CI, not production.

Sequencing / dependencies

  • Legal clearance blocks everything. Run it in parallel with the spike, not after.
  • The DTO decision (castTo vs. external data-object library vs. plain arrays) must be made before the pilot endpoint, because retrofitting DTOs after consumers depend on array shapes is a breaking change.
  • Contract-of-record decision (OpenAPI vs. code) must be made before any API-surface adoption, because it determines whether this package is a fit at all.
  • Pin ^1.3, commit the lockfile, and add Composer audit + a documented upgrade cadence (this package ships minor releases frequently; budget a low-effort quarterly bump).
  • Record the whole thing in an ADR so a future team doesn't re-litigate it.

Operational Impact

Maintenance

  • Package-side churn is low and conservative: 1.x semver, small surface, recent releases consisting of static-analysis cleanup, phpDoc, and PHP 8.4/8.5 support. Upgrade cost is near-zero.
  • The real ongoing cost is our glue: the error-mapping adapter, the DTO convention, the schema test fixtures, and the boundary policy. Budget roughly 10–15% of one engineer for the first two quarters, tapering to near-zero maintenance plus review time.
  • Schema definitions become a shared asset with multiple owners — that requires a code-ownership convention (config schemas vs. API schemas) or they will decay.

Support

  • No commercial support, no SLA. Support channels: GitHub issues, Nette community forum, documentation site. Documentation quality is good but Nette-flavored (examples assume Nette DI/services), so it doesn't map 1:1 to Laravel idioms.
  • Requires an internal SME — realistically one to two engineers who own the adapters and the standard. Single-SME risk should be mitigated by the written cookbook and by making schema test fixtures mandatory in review.
  • Onboarding cost for a new engineer: half a day to first working schema, one to two weeks to fluent use of merge()/extend()/AnyOf()/castTo()/ranges/patterns/error codes.

Scaling

  • Validation is in-process, CPU-bound, no I/O, no shared state — horizontal scaling characteristics are neutral. Per-request cost is negligible for typical payloads.
  • Real scaling concern is schema construction in hot paths: building Expect::structure() trees per request for large structures adds avoidable allocation. Construct once (static property, cached service, container singleton) and reuse.
  • Large list payloads (Expect::listOf over thousands of elements) and deeply recursive structures are the cost peaks; set payload size limits at the HTTP layer rather than relying on the schema.
  • No caching layer, no external service, no connection pool — nothing new to operate, monitor, or page on.

Failure modes

Mode Impact Mitigation
ValidationException uncaught on a code path lacking the adapter 500 instead of 422 for bad client input Mandatory adapter in the base API exception handler; test coverage per endpoint
Overly strict schema shipped to live clients Client breakage / outage Additive-only schema evolution; version schemas; stage-rollout and contract tests
Overly permissive schema Validation theater — no protection but false confidence Adversarial fixtures (valid and invalid) required in review
Silent normalization surprises (Expect::array key stripping, null vs missing defaults) Data-quality incidents, hard to trace Explicit cookbook examples, listOf vs array guidance, lint rule / review checklist
Schema drift from OpenAPI/docs Stale docs, client mismatches Decide contract of record before adoption; if code wins, generate docs from schemas
transform() used for side effects Hidden I/O, non-deterministic behavior, test flake Written rule: transform() is pure coercion only
Package stops receiving PHP-version patches Blocked framework upgrades Small codebase enables fork/vendor; transitive ubiquity creates ecosystem pressure; monitor at each PHP upgrade
License status unresolved at audit Forced removal mid-project Block adoption on legal clearance up front

Ramp-up

  • Day 1: install, validate the app config, read the error-code list.
  • Week 1–2: idioms (Structure::merge()/extend()/skipDefaults(), AnyOf, castTo, ranges/patterns), plus our error-mapping adapter and the boundary policy.
  • Ongoing: every schema change passes through a fixture-based unit test. That single convention is what keeps this package cheap; without it, a second validation dialect becomes technical debt within two quarters.
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.
takashato/laravel-vietqr
aitor24/laralang
kraenzle-ritter/entity-relations
dindigital/din-image
pomirleanu/image-colors
sawmainek/laravel-api-generator
delatbabel/jsonresponses
mouhamedfd/permissions-generator
eduplus/qr
laravel2580/idehelpers
delatbabel/viewpages
anavel/gettext
muhamadrezaar/highcharts
delatbabel/keylists
frankkessler/salesforce-laravel-oauth2-rest
anavel/foundation
wdmtech/enviricons
eii/laravel-captcha
muka/shape-reader
tdt/json