outcomer/symfony-json-schema-validation
Symfony integration for validating JSON documents against JSON Schema. Adds validator constraints and services so you can enforce schemas in forms, requests, or domain objects with familiar Symfony validation workflows.
Install and register. The bundle follows Symfony Flex conventions:
composer require outcomer/symfony-json-schema-validation
If Flex doesn't auto-register it, add Outcomer\JsonSchemaValidation\OutcomerJsonSchemaValidationBundle to config/bundles.php. The bundle pulls in opis/json-schema transitively — do not install opis/json-schema yourself with a conflicting constraint.
Look here first:
config/packages/outcomer_json_schema_validation.yaml after running bin/console config:dump-reference outcomer_json_schema_validation — this shows the exact schema-definition shape your version expects.bin/console debug:container | grep json_schema — confirm the validator service ID, since bundles of this size have historically renamed aliases between minor versions.First use case — validate a webhook/API payload. Define a schema in config, point it at a JSON file, then inject the validator into a controller or message handler and validate the decoded request body before mapping it to a DTO. This is the highest-value daily use of the bundle: rejecting malformed external payloads with structured errors instead of ad-hoc isset() chains.
Named schemas referenced from config. Keep schemas as files under config/schemas/ and reference them by name in the bundle config rather than inlining JSON in YAML. This keeps the schema diff-able, reusable across endpoints, and IDE-highlighted. A typical entry looks like a map keyed by name, with either an inline schema: object or a file:/path pointing at %kernel.project_dir%.
Constraint-style validation on DTOs. If your version exposes a validator constraint, apply it to the request DTO / value object (attribute, or annotation on older PHP). Pair this with #[MapRequestPayload]-style deserialization so schema validation runs as part of Symfony's normal validator pipeline — this means $form->isValid(), validator->validate($dto), and automatic 400s with violations all work without bespoke error handling.
Service-style validation for raw arrays. In controllers and command/message handlers, decode the body (json_decode($body, true, 512, JSON_THROW_ON_ERROR)) and pass the array to the injected validator against a named schema. This is what you want when the payload is not backed by a DTO — e.g. polymorphic payloads where the type discriminator selects the schema, or webhook handlers that must log raw input.
Schema reuse via $ref. Split large schemas into common/address.json, common/money.json and $ref them from the endpoint schemas. Keep them in one directory so relative refs resolve; if the bundle has a configurable schema root/base URI, set it once in config rather than embedding absolute paths.
Testing pattern. Validate fixtures in a functional test (good-payload → no violations, bad-payload → expected pointer in the error path). Because the library surfaces JSON-pointer-style error paths, you can assert on /properties/email rather than on free-text messages — far less brittle than asserting on Symfony violation messages.
Version schemas per API version. Name keys like orders.v1, orders.v2 and resolve the schema name from a route parameter or header. This makes breaking API changes explicit and testable without deploying schema changes to old clients.
composer.json, and consider whether writing a ~30-line service around opis/json-schema directly would be simpler and more durable. The bundle mostly wires config to OPIS — little of its value is irreplaceable.$schema (or an explicit option). if/then/else, const, and unevaluatedProperties will be ignored under the wrong draft rather than erroring — schemas that "pass" may not be validating what you think. Assert your draft explicitly in each schema file.$ref resolution and base URI. Relative refs resolve against the schema's base URI, which for a file: source is often the file's directory — but some wiring passes raw strings where OPIS has no base. Symptom: "unable to resolve reference" or silent fallback. Fix by loading schema files with a consistent base URI, or inlining $id at the top of each schema.keyword, pointer, property and a message. If you skip the bundle's violation conversion, $errors = $validator->validate($dto) will hold raw OPIS objects and getMessage() / form rendering will break. Wrap errors into ConstraintViolation (or Symfony's own Json constraint if you swap) before returning a 400.cache:clear handles it; if you point at files, OPIS may cache compiled copies under var/cache — clear after editing, or you'll debug a schema change that "isn't applied".Json constraint. Two JSON validators in one codebase (Symfony's Json for syntax, this bundle for schema) is fine, but using both for schema validation produces inconsistent error shapes in the same response. Pick one for schema work and route syntax-only checks through json_decode(..., JSON_THROW_ON_ERROR).required or pattern rules, and without the payload plus the JSON pointer you cannot tell a client bug from a schema bug.How can I help you explore Laravel packages today?