Repository navigation
Conversation
|
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 If you're merging code through a pull request on GitHub, copy and paste the following into the bottom of the merge commit message. To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook. |
|
Size Change: +120 kB (+1.39%) Total Size: 8.72 MB 📦 View Changed
|
1758f48 to
6271427
Compare
| * REST API: Gutenberg_REST_Field_Collections_Controller class | ||
| * | ||
| * @package gutenberg | ||
| * @since 20.8.0 |
There was a problem hiding this comment.
Nit: We should update the proper version before mergin.
| * @since 20.8.0 | ||
| */ | ||
|
|
||
| if ( ! defined( 'ABSPATH' ) ) { |
There was a problem hiding this comment.
I don't think we need this check.
| * @return array Collection parameters. | ||
| */ | ||
| public function get_collection_params() { | ||
| $params = parent::get_collection_params(); |
There was a problem hiding this comment.
Nit: We probably only want 'context' => $this->get_context_param(), from get_collection_params. We don't support search or pagination.
| supportsPagination: true, | ||
| }, | ||
| { | ||
| label: __( 'Field Collection' ), |
| label: __( 'Field Collection' ), | ||
| name: 'fieldCollection', | ||
| kind: 'root', | ||
| baseURL: '/wp/v2/field-collections', |
There was a problem hiding this comment.
Even though view context seems to be the default, should we be explicit like icons and fonts(baseURLParams: { context: 'view' },)?
| 'filterBy' => false, | ||
| ); | ||
|
|
||
| gutenberg_register_field_collection( |
There was a problem hiding this comment.
Should we wrap this in a rest_api_init hook?
| // Check if collection with same ID already exists. | ||
| foreach ( $gutenberg_field_collections as $existing_collection ) { | ||
| if ( $existing_collection['id'] === $id ) { | ||
| return; |
There was a problem hiding this comment.
Should we add a _doing_it_wrong here?
| } | ||
|
|
||
| // Global storage for registered field collections. | ||
| global $gutenberg_field_collections; |
There was a problem hiding this comment.
It's definitely fine to start with a global, but have you considered a registry?
| ); | ||
| } ); | ||
| ( fieldCollections ?? [] ).forEach( ( collection: any ) => { | ||
| if ( |
There was a problem hiding this comment.
It's fine if we don't handle the universal (name:null) in here, but we should add a TODO comment not to forget it with the follow ups.
|
I'm going to leave this one in the backburner for a few days and will focus on the view/form config first. |
227da21 to
cf8a102
Compare
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
All fields except the content preview are now provided by server-side field collections: hand-written collections for the core post types and the dynamic postType-default registrar for every other REST-enabled post type. The preview field stays client-side because its render depends on editor internals the collections' script modules cannot import. Post types registered after init priority 100 no longer get any fields, as they have no server-side collection and the client-side fallback is gone. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Move the field collections out of @wordpress/fields into a new
@wordpress/field-collections package that owns a dedicated, editor-free
data store (core/field-collections) and a useFieldCollections hook.
Previously the merged field registry lived in the core/editor store, so any
screen needing field definitions had to depend on @wordpress/editor. The new
package fetches definitions from GET /wp/v2/field-collections, lazily imports
each collection's extension script module, and merges by field id — depending
only on data/api-fetch/element/compose/url/private-apis (+ dataviews types).
- Move collections/ to the new package; re-point extensions.ts to named
@wordpress/fields imports; rename module handles to
@wordpress/field-collections/{loader,postType-*}.
- Move wpScriptModuleExports + wpCopyFiles to the new package; update the PHP
glob path and loader id. Registration API, REST controller, and loader
enqueue stay in lib/.
- Carve the collection-field merge out of registerPostTypeSchema (keeps actions
+ the editor-only preview field); usePostFields now combines editor fields
with useFieldCollections, preview-first.
- Remove the now-unused root/fieldCollection core-data entity.
- Add the package to the private-apis allowlist and root tsconfig.
Tests: 12 JS unit tests (reducer/selector/resolver/hook) and the existing
field-collections PHPUnit suite (24 tests) pass.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ns onto generator Add two filters so consumers can tailor any field collection — including an auto-generated one — without re-registering it: - gutenberg_field_collection_fields ( $fields, $id, $kind, $name ): remove, reorder, relabel, or add serializable field definitions. - gutenberg_field_collection_modules ( $modules, $id, $kind, $name ): append script modules of non-serializable extensions. To support multiple contributors, a collection's single fields_module string becomes a fields_modules array (REST, store types, hook, preload). The client loads every module, concatenates their extensions, and merges all entries matching a field id so a later module overrides an earlier one property by property (last wins). Refactor the post type collections onto the default generator as the single baseline: - Delete post, wp_navigation, wp_template_part — the generator reproduces them exactly and the default union module already covers their extensions. - Convert page, wp_template, wp_block to filter-based overrides that only tweak the title field plus a slim extensions module for its title behavior. - Keep attachment standalone (bespoke; opts out by registering its own collection, which the generator then skips). Update tests (new coverage for both filters and multi-module merge) and the README. The generic REST controller tests move off real post-type names onto a synthetic entity, since the override files register persistent filters that survive the test's collection-global reset. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
cf8a102 to
77a42d4
Compare
|
Flaky tests detected in 045514f. 🔍 Workflow run URL: https://github.com/WordPress/gutenberg/actions/runs/27697541734
|
Add an SVG (source) and PNG (rendered, for GitHub) diagram illustrating how a field is defined in two halves — serializable definitions in PHP and non-serializable extensions in JavaScript — registered server-side, exposed via REST, and re-merged in the client by useFieldCollections before screens consume the resulting Field[]. Embed the PNG near the top of the README. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
| * | ||
| * @see packages/vips/src/loader.ts — the reference pattern | ||
| */ | ||
| export {}; |
There was a problem hiding this comment.
This is intentionally empty. It only exists so that there's a handle (@wordpress/field-collections/loader) to which "attach" the scripts that will be dynamically loaded.
In use-field-collections.ts, the collections load their non-serializable field extensions with a dynamic import:
const module = await import( handle );where handle is a string like 'core/media-fields-module'.
For the browser to resolve that specifier to an actual URL at runtime, the handle must be present in the page's import map. And for WordPress to print import-map entries for them, the modules need to be reachable from the dependency graph of an enqueued module. Hence, this empty loader.
This is another place where we do the same.
The reason I went for an empty handle instead of making the modules a dependency of some site-editor's script is that the field modules can be loaded outside the site editor. Think of examples like the settings page, connectors, comments page, etc. We cannot assume every place that needs fields collections will be part of the site editor.
| function gutenberg_register_default_post_type_field_collections() { | ||
| $post_types = get_post_types( array( 'show_in_rest' => true ) ); | ||
|
|
||
| foreach ( $post_types as $post_type ) { | ||
| if ( ! empty( gutenberg_get_field_collections( 'postType', $post_type ) ) ) { | ||
| continue; | ||
| } | ||
|
|
||
| gutenberg_register_field_collection( | ||
| "core/{$post_type}-fields", | ||
| 'postType', | ||
| $post_type, | ||
| gutenberg_get_default_post_type_fields( $post_type ), | ||
| '@wordpress/field-collections/postType-default' | ||
| ); | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
If there's no collection defined for an existing post type, we automatically register a collection for it based on its declared supports.
The extensibility section of the PR describes the mechanisms a 3rd party can use to create or tweak collecitons. Essentially:
- A 3rd party can register a collection for an existing post type and this default would be ignored.
- A 3rd party can hook into any of the available filters to tweak an existing collection (e.g., to add/update/remove a field).
| excerptField, | ||
| featuredImageField, | ||
| dateField, | ||
| parentField, | ||
| passwordField, | ||
| commentStatusField, | ||
| pingStatusField, | ||
| discussionField, | ||
| slugField, | ||
| statusField, | ||
| authorField, | ||
| titleField, | ||
| templateField, | ||
| templateTitleField, | ||
| pageTitleField, | ||
| patternTitleField, | ||
| notesField, | ||
| scheduledDateField, | ||
| formatField, | ||
| postContentInfoField, | ||
| stickyField, |
There was a problem hiding this comment.
All the logic about whether to load this fields now lives in PHP at packages/field-collections/src/collections/postType-default/fields.php.
|
I've pushed the latest I had and updated the PR description with the current state as well as documented some parts via comments. This API has been inspired by prior prototypes implemented by @youknowriad, @gigitux, and other folks in the Woo realm. It's also vibe coded and not fully human reviewed yet. The focus right now is to understand how to support all the use cases we want via the API surface. I won't have the availability to push this forward to make it part of WordPress 7.1, so, unless someone else picks it up it should have to wait until next cycle. @ntsekouras is taking care of coordinating everything for DataViews/DataForm in WordPress 7.1. |
Part of #74865
What?
Introduces a Field Collections API — a server-side registration system for defining a collection of fields for an entity.
A field collection is a PHP structure that includes:
rendermethod of a field).Why
Part of #74865
How
http://<site>/wp-json/wp/v2/field-collections?kind=KIND&name=NAMEuseFieldCollections( kind, name )JavaScript hook (code) that request the collection data (REST Endpoint), imports dynamically the WordPress script modules for the collection (JavaScript extensions), and merges it together for consumers to use.Field Collection definitions for core/Gutenberg:
Extensibility
The goal is to make it easy to tweak the small things and the complex ones possible.
The API introduces a way for 3rd parties to register their own collections, see example for
postType-attachment. Additionally, we wanted to auto-register fields for all (Custom) Post Types, while providing a mechanism to extend the defaults. The API supports three use cases:A plugin registers a CPT and wants to register its own collection. Uses
gutenberg_register_field_collection. Example.A plugin wants to add/update/remove an existing field of a collection. For example, the
postType/pagewants to disable hiding thetitlefield. It hooks into thegutenberg_field_collection_fieldsfilter and updates its output:rendermethod for thetitlefield. It needs to prepare a module script and attach it to the list of filter modules for that collection:Status
Prototype, not ready to ship.
This API is inspired by prior prototypes implemented by @youknowriad, @gigitux, and other folks in the Woo realm. It's also vibe coded and not fully human reviewed yet. The focus right now is to understand how to support all the use cases we want via the API surface.
Open questions:
@wordpress/fieldsand@wordpress/media-fieldsto@wordpress/field-collections.registerEntityFieldAPI that was available only when Gutenberg was active).Test