Skip to content

feat: unify CPT and field group definitions in one JSON file (closes #163)#517

Open
faisalahammad wants to merge 1 commit into
WordPress:trunkfrom
faisalahammad:fix/163-unified-file-definitions
Open

feat: unify CPT and field group definitions in one JSON file (closes #163)#517
faisalahammad wants to merge 1 commit into
WordPress:trunkfrom
faisalahammad:fix/163-unified-file-definitions

Conversation

@faisalahammad

@faisalahammad faisalahammad commented Jul 22, 2026

Copy link
Copy Markdown

Summary

Adds an additive reader that lets a single acf-json JSON file declare a custom post type together with the field groups attached to it. Today, declaring a CPT with fields requires authoring two separate files and keeping their location.post_type value in sync with the CPT slug. This PR lets you ship one file per conceptual unit.

Fixes #163

Changes

includes/local-json.php

Before: ACF_Local_JSON::scan_files() discovered every JSON shape by key prefix. Field groups ended up in the store through ACF_Local_JSON::include_fields(), which scans group_*.json files. CPT definitions went through include_post_types() for post_type_*.json files. Nothing connected a CPT file to the field groups whose location rules targeted it.

After: Added ACF_Local_JSON::is_unified_definition() (a static detector for the unified shape) and ACF_Local_JSON::include_unified_definitions() (a new acf_init priority-5 loader). scan_files() now routes unified files to a separate internal slot so the legacy readers do not try to read them. The loader splits the JSON into a CPT and its field groups, registers the CPT, then registers the field groups with a post-type location rule pinned to the owning CPT injected non-destructively. Top-level fields (without field_groups) are auto-wrapped into a synthetic group whose key is group_<slug>_fields.

Why: The order at priority 5 vs the existing register_post_types at priority 6 means field groups land in the local store before the CPT registers, so the runtime location matcher sees them.

schemas/unified-cpt.schema.json (new)

JSON Schema definition for the unified envelope. Pulled in by SCF_JSON_Schema_Validator::validate_required_schemas().

includes/class-scf-json-schema-validator.php

Added 'unified-cpt' to REQUIRED_SCHEMAS so the validator self-check finds the new schema.

tests/php/test-unified-definitions.php (new)

14 tests covering the detector, scan routing, end-to-end load (with both explicit field_groups and auto-wrap), preservation of explicit location rules, coexistence with legacy files, source file recording, and schema validation against both a valid and an invalid fixture.

Testing

Test 1: Unified loader registers CPT and field groups

  1. Add a post_type_book.json to your active theme's acf-json/ directory containing key, title, post_type, and a field_groups array with one group.
  2. Reload any admin page.
  3. Result: A Books entry appears in the WP admin menu and the Book Details field group shows up under Custom Fields with a Show if Post Type == book location rule.

Test 2: Auto-wrap top-level fields

  1. Add a post_type_movie.json with only a top-level fields array (no field_groups).
  2. Reload any admin page.
  3. Result: a synthetic group_movie_fields group is registered and attached to the movie CPT.

Test 3: Legacy files still load

  1. Add a single-object post_type_album.json (CPT payload only) and group_legacy.json (field group payload only) to the same directory.
  2. Reload any admin page.
  3. Result: both register through their existing per-type readers, unchanged from the pre-PR behaviour.

Automated tests: composer test:php runs all 2858 tests including the 14 new ones. PHPStan clean against bin/baseline.neon (regenerated for the new methods). PHPCS reports no new violations on the changed lines (verified via phpcs-changed).

Verification environment

  • PHP 8.x, WorDBless for unit tests
  • All 2858 PHPUnit tests pass
  • PHPStan level 1 with regenerated baseline, zero new errors
  • phpcs-changed on changed lines: clean
  • No breaking changes to existing JSON file shapes; existing themes with post_type_*.json and group_*.json files continue to work.

Screenshot

image

Allows a single JSON file under the acf-json directory to declare a
custom post type together with the field groups attached to it, instead
of the previous split between post_type_*.json and group_*.json files.

The loader runs on acf/init priority 5, before register_post_types at
priority 6, so field groups land in the local store before the CPT
registers. Detection is by shape: a JSON file containing both a
post_type key and either a field_groups array or a top-level fields
array routes through the new path. Legacy single-object files continue
to load through their existing per-type readers.

Top-level fields without an explicit field_groups entry are auto-wrapped
into a single synthetic group whose location rule pins it to the owning
CPT. Existing user-supplied location rules are preserved alongside the
injected rule.

Closes WordPress#163.
@github-actions

Copy link
Copy Markdown

The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the props-bot label.

Core Committers: Use this line as a base for the props when committing in SVN:

Props faisalahammad, priethor.

To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Implement alternative, unified file-based definitions

1 participant