WordPress / gutenberg

The Block Editor project for WordPress and beyond. Plugin is available from the official repository.
https://wordpress.org/gutenberg/
Other
10.17k stars 4.05k forks source link

Connecting block attributes and custom fields & Block Bindings API for WP 6.5 #53300

Closed SantosGuillamot closed 3 months ago

SantosGuillamot commented 11 months ago

Part of:

Related to:


EDIT: Connecting block attributes and custom fields, and the block bindings API tracking issues have been merged into this epic because they are highly related and it seems it is easier to follow having everything under the same discussion.

This issue is meant to keep track of and discuss how to connect block attributes with custom/meta fields and how Block Bindings API should work. It includes a list of tasks needed for this, and we will share updates about the progress here.

For more context about the reasoning of the block bindings API, you can take a look at its original tracking issue.

Progress

This is a list of things to consider/questions related to the Block Bindings API that will be updated as we go along:

WordPress 6.5 release process

Click to see the list of issues addressed during 6.5 release cycle --- A couple of things that are important: * **This project depends on the Block Bindings API**: [link](https://github.com/WordPress/gutenberg/issues/54536). * **We will start supporting just a few core blocks attributes** that can be increased in a later phase. We can start with: * Paragraph content. * Heading content. * Image URL. * Button link and content. ### Fixes/updates for Gutenberg 17.6 RC (Tuesday 30th) - [x] Decide the final name of the public class and methods included in [this pull request](https://github.com/WordPress/wordpress-develop/pull/5888). - [x] Migrate all the changes suggested in [this wordpress-develop pull request](https://github.com/WordPress/wordpress-develop/pull/5888) to Gutenberg, including the naming decision. [#58383](https://github.com/WordPress/gutenberg/pull/58383) - [x] Decide the final shape of the bindings object used in the blocks and make the necessary changes: [#58337](https://github.com/WordPress/gutenberg/pull/58337) - [x] Backports to WordPress. - [x] Add more tests for the bindings, especially in the editor. - [x] Add validation in the `WP_Block_Bindings_Registry` class like others like `WP_Block_Type_Registry` are doing: [link](https://github.com/WordPress/wordpress-develop/blob/2526eab870476dd825758ead7cc48ddccfede138/src/wp-includes/class-wp-block-type-registry.php#L48-L100). ### Fixes/updates for WordPress 6.5 beta 1 (February 13th) - [x] Review the accessibility of the editor with the accessibility team, including the suggestions made: [link](https://github.com/WordPress/gutenberg/issues/58595). - https://github.com/WordPress/gutenberg/pull/58687#pullrequestreview-1867359839 - [x] Migrate to Gutenberg the changes made in this core pull request: [link](https://github.com/WordPress/wordpress-develop/pull/6016). PR: [#58683](https://github.com/WordPress/gutenberg/pull/58683) - [x] Fix a bug with `strcasecmp` reported [here](https://github.com/WordPress/wordpress-develop/pull/5888#discussion_r1475692597). - [x] Decide if we should add a `WP_Block_Binding_Source` class and handle validation there: [link](https://github.com/WordPress/gutenberg/pull/58337#discussion_r1469227481). PR: [#6042](https://github.com/WordPress/wordpress-develop/pull/6042) / Core changeset: [57562](https://core.trac.wordpress.org/changeset/57562) - [x] Refactoring the processing of block bindings into two steps: [link](https://github.com/WordPress/wordpress-develop/pull/5888#discussion_r1462922587). This would solve [this issue](https://github.com/WordPress/gutenberg/issues/58425) with the empty images in pattern overrides. PR: [#6059](https://github.com/WordPress/wordpress-develop/pull/6059) - [x] Add initial documentation. Core changeset: [57560](https://core.trac.wordpress.org/changeset/57560) - [x] Remove unused `setAttributes`. PR: [#58806](https://github.com/WordPress/gutenberg/pull/58806) - [x] Lock editing by default when the source used in the bindings is not registered in the client and adapt pattern overrides. PR: [#58787](https://github.com/WordPress/gutenberg/pull/58787) ### Fixes/updates during beta phase - [x] Fix the way context is added as discussed [here](https://github.com/WordPress/gutenberg/pull/58554). [Core](https://github.com/WordPress/wordpress-develop/pull/6079) / [Gutenberg PR](https://github.com/WordPress/gutenberg/pull/58554) - [x] Remove unnecessary `queryId` in context: [link](https://github.com/WordPress/gutenberg/commit/fd467521e4ddca70cc7bcd33f6719a0d507cbd52#r137779780). Solved as part of [Core](https://github.com/WordPress/wordpress-develop/pull/6079) / [Gutenberg PR](https://github.com/WordPress/gutenberg/pull/58554) - [x] Create a guide about using block bindings. Discussion: [219](https://github.com/WordPress/developer-blog-content/discussions/219). Guide [part 1](https://developer.wordpress.org/news/2024/02/20/introducing-block-bindings-part-1-connecting-custom-fields/). - [x] Fix metadata attribute not being preserved after block transforms: [link](https://github.com/WordPress/gutenberg/issues/59086). PR: [#59179](https://github.com/WordPress/gutenberg/pull/59179) - [x] Fix query loop not working as expected in the editor when blocks are bound. PR [#59283](https://github.com/WordPress/gutenberg/pull/59283) - [x] Improve the accessibility of the block bindings: - https://github.com/WordPress/gutenberg/issues/58674 - https://github.com/WordPress/gutenberg/issues/58673 - [x] Review the `use-bindings-attributes` hook and improve its implementation. As part of that, we should address [link](https://github.com/WordPress/gutenberg/pull/58085#discussion_r1464611692). PR with the refactoring: [#58895](https://github.com/WordPress/gutenberg/pull/58895/) - [x] Fix insert button when pressing enter in bound blocks. [#59361](https://github.com/WordPress/gutenberg/pull/59361) - [x] Don't show protected meta fields. PR [#59326](https://github.com/WordPress/gutenberg/pull/59326) / Core ticket [#59326](https://core.trac.wordpress.org/ticket/60651) - [x] [Dev note](https://make.wordpress.org/core/2024/03/06/new-feature-the-block-bindings-api/) covering how extenders can use the block bindings. - [x] Review the current UX and make further improvements: [link](https://github.com/WordPress/gutenberg/issues/58978). - https://github.com/WordPress/gutenberg/pull/59185 - https://github.com/WordPress/gutenberg/pull/59434 - https://github.com/WordPress/gutenberg/pull/59477

WordPress 6.6 planning

This section will be used to keep track of the issues that could potentially be addressed for WordPress 6.6.

As explained in this comment, the idea is to keep the scope limited and work on more functionalities if there is time at the end of the release cycle.

Initial plan

Keep in mind that this is not set in stone and it might change. The initial idea is to focus on these aspects:

Polish the existing code

And work on new aspects like:

Out of scope for 6.6

Trying to keep the scope limited for this short release, the idea is to work on these functionalities only once the previous points are finished:

SantosGuillamot commented 11 months ago

Initial experimental MVP

We can start with a simple experimental version, that can be activated through Gutenberg → Experiments, to kick off the project. We can aim to just:

<!-- wp:paragraph
    { 
        "connections": {
            "attributes": {
                "content": { 
                    "source": "meta_fields",
                    "value": "my_custom_field"
                }
            }
        }
    }
-->

Keep in mind that this is just an example, and we have to discuss how the API should look. Additionally, it might require adding support for these connections in the block itself.

For this first experimental MVP, I would leave out of the scope:

Mamaduka commented 11 months ago

By the way, the meta attributes source was only deprecated in WP 5.4, and core still maintains backward compatibility.

I wasn't actively involved in the project then, so I don't remember the reason for the deprecation. We should probably dig up old PRs and check.

SantosGuillamot commented 11 months ago

I wasn't actively involved in the project then, so I don't remember the reason for the deprecation. We should probably dig up old PRs and check.

Thanks a lot for sharing! 🙂 I've been taking a look at the old PRs and it seems the deprecation was added in this pull request: link. I've been reading the different conversations and, as stated in the Dev Notes, it seems it was deprecated in favor of the EntityProvider. Which is supposed to "provide a more flexible and powerful way for building blocks that source data from different properties of WordPress entities and data." But I wasn't involved in the project either, and I couldn't find too much information, so I might be missing something.

Anyways, I believe the case here is slightly different than the meta attributes source. In this case, we want that block attributes that are not usually connected to meta fields, can do it if the user wants. If we take the paragraph content as an example, it uses source: html. In most cases, users will want to use the paragraph block as it is used right now but, if wanted, this would offer the possibility to connect it to a meta field (Post Excerpt for example) through a UI in the editor.

Apart from that, blocks using the old meta source would keep working as well. It's just that those attributes wouldn't have the UI to connect to a different meta field. If we consider this a need, we could explore how to support that too in the future.

michalczaplinski commented 11 months ago

I've opened the PR to allow connecting the url attribute of the Image block to Custom Fields:

gziolo commented 11 months ago

By the way, the meta attributes source was only deprecated in WP 5.4, and core still maintains backward compatibility.

@mcsf should be the best person to contact, in case you need to know the details. Here is the initial implementation https://github.com/WordPress/gutenberg/pull/2740.

mcsf commented 11 months ago

It's been a long time and my memory is fuzzy, but I believe the original meta source was deprecated due to a combination of:

youknowriad commented 11 months ago

I think the main problem of the meta source compared to this new proposal is that it requires folks to actually write a new block by explicitly marking a given attribute as a "meta" attribute. While this new proposal allows setting the source as an attribute of existing blocks (no need to write new blocks).

glendaviesnz commented 11 months ago

@SantosGuillamot, @Poliuk I have added a tracking issue for partial syncing, which is going to have some overlap with this work.

SantosGuillamot commented 11 months ago

I wanted to share an update on the work done till now:

Done

The first implementation to connect the paragraph content with custom fields has been merged. It is a really basic MVP that will let us build the rest on top of it. Bear in mind that it's just an MVP to test the technical part, it will change, especially the Editor UX. This is a quick demo of how this initial version works:

_I created a custom field "customtext" with a value "Custom Text Value"

https://github.com/WordPress/gutenberg/assets/34552881/81d7c542-4dc4-4ae4-92cd-418428ddb276

Apart from that, there is an ongoing PR to be able to connect the Image block URL as well: link.

Additionally, @glendaviesnz has started experimenting with the possibility of using this mechanism for the Partially synced patterns, and he created a tracking issue for that work: link. According to the first impressions, it seems it could work.

Next steps

The potential immediate next steps could be:

michalczaplinski commented 10 months ago

To reflect the general nature of the feature that goes beyond just custom fields, @SantosGuillamot and I propose to call it "Block Bindings".

The name should not be taken as final, but more as a codename that gives us a way to refer to the combined effort in a consistent way.

sc0ttkclark commented 10 months ago

Majorly agree with thinking beyond custom fields here, binding could be useful for other use cases too

SantosGuillamot commented 10 months ago

After thinking about it and working on the first implementation of custom fields, I believe we can differentiate between Block Bindings API and Custom Fields:

Having this distinction should help clarify each project's scope and how they are related. Let us know if it is clear enough.

I've updated the opening comment to reflect that and moved part of the discussion to the Block Bindings API issue.

SantosGuillamot commented 8 months ago

Initial version for WordPress 6.5

We want to include an initial version of the binding between Custom fields and Blocks in WordPress 6.5. We have already experimented with the implementation as explained before, and we feel confident enough to start working on a version meant to be included in WordPress 6.5.

Goal

As a reminder, this feature aims to create a simple way of connecting block attributes and Custom fields/metadata. Imagine something like this:

Bear in mind that the UI still needs to be discussed, this is just for explanation purposes

https://github.com/WordPress/gutenberg/assets/34552881/9cb4808c-4928-40b8-a7cd-db75fd0f04db

These bindings are especially helpful when the value of the metadata changes depending on the context. For example, in a Query Loop or inside a template. Imagine a user wants to create a Query Loop of movies like this one:

Movies query loop

Right now, they would need to create custom blocks for the release date, the runtime, the trailer button, or any other info related to the movie.

With this initial version, users could bind blocks with custom fields, whose values can easily be changed for each movie, simplifying the process a lot. They would just use a Query Loop with core blocks connected to custom fields:

Screenshot 2023-11-10 at 16 00 54

Proposal for 6.5 release

The whole Custom Fields & Blocks project could be pretty big as it covers many things. I believe we can start small and implement new functionalities progressively. This way, we add value to the users and receive feedback as soon as possible.

I'm sharing the phases that I consider realistic for 6.5. But as I mentioned, we can approach them one by one and get as far as possible. If we only reach phase 2, that would already be much better than what we have now and can be improved in later releases.

I'm including some orientative things/questions that we should consider for each phase, although we will edit this issue once we progress and discover what other things we need to do, and some might be postponed to later phases.

Progress and updates relative to these tasks will be shared in the opening post.

1. Be able to connect block attributes and custom fields without a UI

The idea is that users can add the binding through a new "bindings" block attribute. A couple of things that are important at this point:

With that in mind, this is the list of things to consider:

2. Include a basic UI to create these bindings where we can read the metadata value

We can start with a basic dropdown as the one shown in the video without the possibility of editing its value.

3. Add the possibility to edit the value of the meta field directly through the Editor

4. Add support for more core blocks we consider important

Once we feel confident enough about the solution, we can focus on adding support to more core blocks.


With these phases, there is already a lot of work for 6.5. For the next releases, we can keep working on this, work on the remaining tasks, and explore the possibility of addressing new ones like:

Anyway, we can analyze this deeply once 6.5 is out, and we have a better picture. Or even reconsider including some of them in 6.5 if we feel they are important enough.

I've updated the opening post to reflect this

lgladdy commented 8 months ago

@SantosGuillamot This looks good! How do you think existing meta will work with this? Will there be some way to mark a meta field as one which should be shown in the list of bindable fields?

ACF currently uses the legacy metabox support for page-wide meta, but if we can add a "progressive enhancement" way for meta to be updated live as users change fields, we could do that so their changes from ACF are visible on bound attributes immediately - this would require a JS api for trigging meta value update events which is subscribed to by each block which uses one.

1. Be able to connect block attributes and custom fields without a UI

Mechanism for block attributes to opt-in for these bindings.

Assuming this will be a way for other blocks and third party blocks to allow an attribute of theirs to be selected from the "link" button from a meta field?

2. Include a basic UI to create these bindings where we can read the metadata value

Mechanism to get the available metadata fields in the Editor.

The REST makes sense here I think, but there should be a way for third party plugins (like ACF) to add fields to it. For legacy data and field location reasons, ACF doesn't use register_field - so we'd have to add support for this by hooking in our fields as available here.

Should there be a way to specify which fields are available to which blocks? For example you'd only want compatible image fields to be possible to set as the source of an image.

decodekult commented 8 months ago

Sorry for the half-spam here. This same ability is provided by Toolset over its Dynamic Sources.

I was the team leader for Toolset when this was developed back in 2020-2021, if anyone here is interested I could name a couple of devs who could provide precious feedback on implementation ideas and edges found during that phase.

SantosGuillamot commented 8 months ago

@lgladdy Thanks a lot for those questions! I'll try to address them one by one but I wanted to note first that we still have to discuss and decide most of the things 🙂

How do you think existing meta will work with this? Will there be some way to mark a meta field as one which should be shown in the list of bindable fields?

On the PHP side, we considered accessing the value of the meta fields using the get_metadata function or get_post_meta. Are ACF accessible through that function?

On the editor side, one idea was to use the REST API to get the list of available fields for a given context. I assume that any field exposed there should be available in the list of bindable fields.

Apart from that, if plugins need something more custom, we were thinking of creating a mechanism to add different sources in the Block Bindings API. For example, we could explore the possibility of having an "ACF" source if we feel that a better approach.

Anyways, it's something we have to explore deeper, and we have to understand better how plugins like ACF work.

this would require a JS api for trigging meta value update events which is subscribed to by each block which uses one.

This could make sense 🙂 Adding it to the list of things to consider.

Mechanism for block attributes to opt-in for these bindings.

Assuming this will be a way for other blocks and third party blocks to allow an attribute of theirs to be selected from the "link" button from a meta field?

The end goal is that any block can decide which attributes can be connected. But for this first version, we will limit it to just a few core blocks where everything is under control. Making the bindings work in any block is a bit trickier and should be done cautiously. There is more info about this in the block bindings API issue.

but there should be a way for third party plugins (like ACF) to add fields to it. For legacy data and field location reasons, ACF doesn't use register_field - so we'd have to add support for this by hooking in our fields as available here.

Totally agree here. We have to ensure that whatever we provide works as smooth as possible with plugins like ACF.

Should there be a way to specify which fields are available to which blocks? For example you'd only want compatible image fields to be possible to set as the source of an image.

We should definitely explore that possibility and analyze how to handle types and that validation. In my opinion, that could be done in a later phase. In the meantime, we can use the own block as the "validator". For example, if I connect an image to a field that is not a URL, the block will fail and return an error. I created a quick video to show what I mean:

https://github.com/WordPress/gutenberg/assets/34552881/6b4ef9c7-46ee-4438-b5a2-ed33dca7927f


Sorry for the half-spam here. This same ability is provided by Toolset over its Dynamic Sources.

I was the team leader for Toolset when this was developed back in 2020-2021, if anyone here is interested I could name a couple of devs who could provide precious feedback on implementation ideas and edges found during that phase.

@decodekult That looks great! All the feedback we receive, regarding the UX, things to consider, or the development, is more than welcome 🙂

lgladdy commented 8 months ago

Thanks for the reply @SantosGuillamot! This is all logical.

I'm more than happy to help work on the code here too, and will try to make sure ACF is good to go at launch too. I'm passionate about making meta work better in the block editor, so anything I can do to help here I will!

On the PHP side, we considered accessing the value of the meta fields using the get_metadata function or get_post_meta. Are ACF accessible through that function?

On the editor side, one idea was to use the REST API to get the list of available fields for a given context. I assume that any field exposed there should be available in the list of bindable fields.

Specifically with your question on ACF here, yeh - get_post_meta will give you ACF's raw database value, so we'd want to add a layer on top here (maybe by a final return filter on the PHP side?) to intercept and translate accordingly (that's things like return formats for data/time strings etc)

Apart from that, if plugins need something more custom, we were thinking of creating a mechanism to add different sources in https://github.com/WordPress/gutenberg/issues/54536#:~:text=2.-,Get%20the%20value%20from%20the%20source%20defined%20in%20the%20binding.,-Depending%20on%20the. For example, we could explore the possibility of having an "ACF" source if we feel that a better approach.

This would be cool, not just for ACF but the other meta plugins. If plugins could register themselves as a source for meta, and can handle the meta available to that location somehow.

SantosGuillamot commented 8 months ago

Glad to hear that and thank you for your feedback!

maybe by a final return filter on the PHP side?

One of the points I mentioned in the opening post was "Explore the possibility of creating a filter to modify the value of the meta field returned.", which I believe is what you mean here. I include it in the "Future releases" section, but if we are in good track and we feel it is important enough we can coordinate and try to include it 🙂

This would be cool, not just for ACF but the other meta plugins.

Great to know, it's something we must consider since the beginning.

tresorama commented 8 months ago

What if core "post meta (aka custom fields)" and "ACF integration" will be coded so that both are consumer of the same API used to expose custom fields dynamic data to blocks?
Treating WP as a consumer-of the API could be a way to have this API looks agnostic, facilitating third party integration.

Core WP produce a new API that is used to expose custom fields dynamic data in block toolbar based on context. Core WP produce also an adapter that consume the API to expose "post_meta" , "post_title" and so on...

ACF, and any other plugin, then need to produce an adapter for its own plugin.

In this way, core WP can produce a contract and be also the first consumer of the API, so that problem that would eventually arise for a third-party consumer will be soon tackled.

gziolo commented 8 months ago

@SantosGuillamot, thank you for posting the detailed update with accompanying visuals that help to tell the whole story. While we have enough to do the more advanced prototyping in the Gutenberg plugin behind the feature flag for experiments, it will be initially limited to everything available in WordPress core internally. Obviously, it's a trade-off, but that would help us iterate fast and land a UI that could get shipped in WordPress 6.5 if it provides enough user value. In the case of post meta, it's all simple today as it's covered with the existing REST API endpoint that is integrated with @wordpress/core-data through @wordpress/api-fetch so to modify the value on the client you need very little code, example pseudo-code:

import { useEntityProp } from '@wordpress/core-data';

const [ meta, updateMeta ] = useEntityProp( 'postType', 'post', 'meta', 123 );

updateMeta( {
    ...meta,
    my_key: 'foo',
} )

In parallel, we should continue the discussion started and concentrate our efforts on building a compelling API that allows us to integrate any data source. From the technical perspective, it would be convenient to tap into the existing concept of entities inside @wordpress/core-data that is integrated very deeply with the block editor. The first step would be identifying the unique requirements for this integration. @lgladdy mentioned the ability to run a custom filter for the value on the server before rendering the value. The supporting question would be how we can apply the same transformation in the editor in case we would like to show the preview, or is it fine to proceed with the raw value?

@sc0ttkclark, how does all that fit into the vision for the Fields API project? @tresorama , could this API be mostly based on PHP and the adapter would be the REST API endpoint that works on the client the same way how post meta in the example above?

The biggest advantage of using @wordpress/core-data is that it integrates automatically with the editor, offering a way to persist the changes on the server in a way where users have full control over it when about to save changes in the editor:

Screenshot 2023-11-15 at 09 46 53

For the full context, this is the code that initalizes the configuration for all post types so it's possible to use their REST API endpoints with useEntityProp or useEntityRecord interfaces:

https://github.com/WordPress/gutenberg/blob/52e9eb2950647c2439d4365fcae867dc01ea0b15/packages/core-data/src/entities.js#L278-L333

lgladdy commented 8 months ago

The supporting question would be how we can apply the same transformation in the editor in case we would like to show the preview, or is it fine to proceed with the raw value?

Hey @gziolo - specifically for ACF here, I think it's fair to assume anyone wanting to consume ACF meta in the block editor would expect values to be transformed by their requested return formats, so having a specific filter where we can modify the results going into the block editor via the REST API would be good (this may already exist, but I'm not sure if there is available context is specific to rendering in the block editor)

We do already support returning all ACF meta in the REST API in an acf key, so we could potentially do something on page load in JavaScript to pre-fill all of our formatted meta into the core-data cache as meta?

Also, it's worth mentioning, ACF users could potentially have substantial amounts of fields in meta for a page that will appear via get_post_meta, so some way to handle that without showing every possible option feels important here in v1 to avoid that list of connectable meta fields being hundreds of entries long.

dmsnell commented 8 months ago

In this way, core WP can produce a contract and be the first consumer of the API, so that problem that would eventually arise for a third i party consumer will be soon tackled.

@tresorama shared the perfect words for what I wanted to express. Same with these from @gziolo

concentrate our efforts on building a compelling API that allows us to integrate any data source

One concern I have with these mockups is that they are already a bit tight on their fit to a specific use-case, which is meta fields for a single attribute on a block. From the get-go I see the need to address blocks having multiple bound attributes - something already exposed by @michalczaplinski's exploration in #53488 because it really doesn't make sense to replace an image's URL without providing the ability to supply a proper alt description or title.

Further, as we build the UI for the use-case of meta fields we may miss that different data sources will have different UI needs. I think that somewhere you already mentioned, @SantosGuillamot, the need to store various attributes for a given binding, and possibly a validator. These can be surfaced in a general way or in an over-specific way to meta fields.

One approach I can imagine here is to focus not on building meta fields, but building a registration mechanism to supply sources, and then also build an implementation for meta fields that registers. This should hopefully surface some additional constraints like validation and passing arguments and providing fallback values and updating the editor. If we can't accomplish what we need through our interface, then we have to adjust the interface.

As with Bits, there's likely going to need to be separate registration: one on the server and one inside the editor.

register_block_binding_source( 'core/custom-field', … );

register_block_binding_source( 'core/query-value', … );

These registrations need to provide a replacement value when called, given their respective arguments, and might provide information for an API call, might provide a name and label for display in the editor. They could list or enqueue any respective code that needs to run in the editor.

So in the mockups with the custom field meta key, that would be something built through this generic interface. In my head there's almost perfect harmony in the UI needs for that side panel area as there are for when adding a link inside a paragraph or for the block controls above it. We could create a new area that lists all active block bindings for a block, and for each binding add its registered controls, if there are any.

Maybe to toss around some concrete ideas, based on the explorations already shared, we might see something like this…

wp_register_script(
    'wp_block_binding__core/query-value',
    __DIR__ . '/editor.js',
    $deps
);

register_block_binding_source(
    arary(
        'name'          => 'core/query-value',
        'label'         => __( 'Query Value' ),
        'description'   => __( 'Use a property from the current post or item in a query loop.' ),
        'provider'      => 'wp_query_value_binding_provider',
        'editor_script' => 'wp_block_binding__core/query-value' 
    )
);
import { registerBlockBinding } from '@wordpress/block-bindings';

registerBlockBinding( {
    name: 'core/query-value',
    controls: QueryValueControls,
    getAvailableBindings: ({block, context, attrs}) => context.isPost ? [ … ] : [ … ],
    bindingPreview: ({block, context, attrs}) => …,
    getFallbackValue: …
} );
sc0ttkclark commented 8 months ago

I love the direction of this, it looks fantastic! This is what I believe many plugins enabling embedding of Dynamic Content within a block could benefit from too.

GenerateBlocks and Stackable Blocks both allow connecting fields. I've built integrations that extend them to offer even more comprehensive and flexible options for embedding things based on the additional context that Pods itself knows about those configurations too. I believe we'd benefit from that filter that @lgladdy mentioned towards improving the content that is embedded (not just the raw meta value).

I also agree that we need some ability to limit what fields are available to be used/shown here.

Another item not mentioned but that would be helpful here would also be to define the "label" shown instead of just the meta_key name. Could be as simple as adjusting register_meta() to support a label too, or just filtering the list of meta keys for that object type.

@gziolo Regarding the Fields API, I see any effort we do from that side would most likely be towards enabling things like this and improving the APIs used to register the field configurations via PHP/JS. Everything I see here isn't stepping on the toes of that project as it stands and would definitely benefit from the things we're doing there too.

SantosGuillamot commented 8 months ago

Thanks a lot everyone for the great feedback! ❤️

The way I've been thinking about it lately is that we might need two different extension mechanisms:

  1. Mechanism to extend the "metadata" source.
  2. Mechanism to register new sources.

It seems to me that they are slightly different use cases, and it could make sense to differentiate them.

1 . Mechanism to extend the "metadata" source.

This would be just a way to add new items to the list of fields we show in the interface. This way, there would be just one unique UI tailored for "metadata". Other plugins dealing with metadata, and even core solutions like site data or post meta, would just hook into this list and add new items. We would end up with a complete list including custom fields, post meta, site meta, ACF...

As part of this mechanism to extend the "metadata" source, each plugin could maybe add more validation for their fields.

2 . Mechanism to create new sources.

This mechanism to create new sources would be part of the Block Bindings API and not part of the Custom Fields implementation.

This one looks more complex to me because each source might be completely different. For example, a new "pattern" source will potentially be used for partially synced patterns.

The way I see it, if we are able to provide the first mechanism and serve the needs of external plugins dealing with metadata, the use cases for custom sources would be much lower, and they will probably require a pretty custom solution.

For that reason, I would say that we should focus first on making sure the Block Bindings API understands new sources BUT don't provide a mechanism to extend the UI. Each of them would need to come up with their own way of adding the proper attributes to blocks as we are doing for "metadata" or they are doing for the "pattern" one. For example, they could add a new element to the blocks toolbar if they feel that's the best way for their use case. Or create a totally different interface.

This is all assuming the first mechanism is enough for metadata.


Regarding the UI of custom fields shown in my comment, I would like to emphasize a couple of things:

cc: @michalczaplinski

youknowriad commented 8 months ago

Can you expand more about the reasoning because for me, I think the first one is just a special case of the second one.

SantosGuillamot commented 8 months ago

I believe I was trying to distinguish them more from a user/extender perspective than from the technical implementation.

Imagine I have a plugin that creates new custom fields. I would probably just want to ensure that the fields created with my plugin appear in the list of the "metadata" source. I would probably want to reuse their UI and just add new items, not registering a new source with a similar UI just for my use case. That's what I was referring to in the first mechanism.

Another use case would be that, as a developer, I need to connect block attributes to a totally different source with a different UI. For example, that's the case for partially synced patterns, where they are connecting block attributes to the content defined in the pattern. Or imagine I want to connect, for any reason, block attributes to a shortcode or a custom PHP snippet. It wouldn't make sense to reuse the logic created for metadata and it would make sense to create a new source.

In my head, it made sense to distinguish them because the use cases for the second mechanism seem less common to me, and they will probably require more custom solutions.

Anyway, I am still trying to connect everything and I am not convinced how it should work. Just sharing to get more thoughts 🙂

youknowriad commented 8 months ago

Imagine I have a plugin that creates new custom fields. I would probably just want to ensure that the fields created with my plugin appear in the list of the "metadata" source. I would probably want to reuse their UI and just add new items, not registering a new source with a similar UI just for my use case. That's what I was referring to in the first mechanism.

To be honest for me, it's not very clear whether they should be in the same UI. Also, I believe the UI bit is not important at all for this project (at least initially). It's going to be a challenge for the user to understand what the UI is doing (connecting to sources). So I think for the start, the project is more aimed towards developers creating block variations, new block bindings (though even this could be private to start with as we implement the first ones).

In that sense, I don't think adding a "shortcut API" is the best path to start personally.


I'm guessing the question comes down to:

tresorama commented 8 months ago

Just sharing a pseudo-code POC.

I wanted to begin from an extender perspective, the "who" want to add a dynamic data source to the core API. Example of extender can be:

I wanted to iterate more on this before posting but maybe can be already beneficial even in this draft state...

NOTE: I haven't touched any Guteberg related code in months, so maybe I'm missing something "obvious" about how to use the API. Especially in the Data Fetching part, if I remember correctly there was a method to retrieve from the redux store outside of React, bypassing the limitations of React Hooks. If anyone has any suggestions to point me in the right direction that would be great

https://gist.github.com/tresorama/bf512a84b6a32faafadd1600664118d1?permalink_comment_id=4763241#gistcomment-4763241

dmsnell commented 8 months ago

For example, a new "pattern" source will potentially be used for https://github.com/WordPress/gutenberg/issues/53705.

It seems like partially-synced patterns and block bindings are divergent enough that designing this system to support it is an invitation for scope creep and I worry that it could broaden the requirements in a way that is unworkable.

I'd rather we focus on this primary use-case and the necessary API it needs for developers to hook into it:

Replace the content in a rendered block that corresponds to an attribute defined in its block.json file with new content generated during the render.

Custom fields and anything else of the same kind of data could be provided together, and I don't see why Core's custom field provider couldn't itself be extendable, but other sources requiring more setup (e.g. a stock ticker or a foto feed) would provide their own registration.

talldan commented 8 months ago

It seems like partially-synced patterns and block bindings are divergent enough that designing this system to support it is an invitation for scope creep and I worry that it could broaden the requirements in a way that is unworkable.

I agree they are very divergent in terms of the data sources. As someone working on partial syncing, I think it's absolutely fine to start developing the binding API with custom fields and other post data as the primary target. I personally find it very hard to assess what opportunities there are for partial syncing using the bindings API without something more concrete. Having a more concrete API can also help with the development of partial syncing. At the moment the work being done is without guidelines or constraints, so it has naturally diverged. Imposing some constraints wouldn't be a bad thing.

Even with the divergence, I think there's still some overlap. The rewriting of block HTML server side is a system both custom fields and partial syncing can leverage, so it's worth considering some integration points.

Maybe to toss around some concrete ideas, based on the explorations already shared, we might see something like this…

My main suggestion is to delay exposing any UI parts of the API. UI is always in flux, if API users are able to directly pass react components in it makes it much more challenging to change later on.

youknowriad commented 8 months ago

In my initial Poc PR, I actually had an API for defining custom sources (private) for both frontend and backend. Not sure why something like that wasn't considered as a base.

michalczaplinski commented 7 months ago

In my https://github.com/WordPress/gutenberg/pull/51375, I actually had an API for defining custom sources (private) for both frontend and backend. Not sure why something like that wasn't considered as a base.

You're right @youknowriad. It probably should not have been "simplified" when I merged the current Block Bindings experiment 🙂 .

That said, the implementation details of the current GB experiment should be taken with a grain of salt. They're merely suggestive of what will actually be merged into GB as final API.

SantosGuillamot commented 7 months ago

Do we want "post fields", "site fields", "template fields" maybe (I can't think of anything else) to be part of the same "block binding source" or do we want each of this to be a separate "block binding source". I feel it's not important to answer this today, because I think all of these sources are "core provided sources".

I agree with this. I am not sure either if everything should be together, but that can be decided later as they are in core.

I'd love to understand more what "other fields source" plugins should add here?

I was thinking of plugins storing values in a different place than metadata, but thinking more about it we should probably start with just values stored at wp_postmeta. Although we might want to provide some filters for that. For example, there might be some fields that plugins don't want to be bindable or maybe they want to provide a more "user-friendly" name in the UI. For example, `Regular price" instead of "_regular_price".

Anyways, I see those functionalities can be discussed in a later phase once we have more information of what is needed.


Going back to my previous point, in my opinion we can start with just support for the "metadata" source that extenders may filter if needed. With a UI that fits this use case.

I believe a mechanism to create new sources makes total sense. However, at this initial phase, opening a mechanism to register new sources that can extend the UI seems a bit more complex to me and it should probably be done cautiously. Two questions that come to my mind regarding this:

Having said that, I'm still processing all the information and I'd like to note that I'll personally dive into a prototype of the metadata source to understand everything better and trying to explain my previous points.

dmsnell commented 7 months ago

Not sure why something like that wasn't considered as a base.

@youknowriad that's a good PR, thanks for the link. as to why? probably because it wasn't apparent enough and because the primary description is "more to come" 😆

I think it's absolutely fine to start developing the binding API with custom fields and other post data as the primary target. I believe a mechanism to create new sources makes total sense. However, at this initial phase, opening a mechanism to register new sources that can extend the UI seems a bit more complex to me and it should probably be done cautiously.

this might also be my misunderstanding of the text here, but I'm still nervous about the way we talk about focusing on custom fields or post meta. I don't want us to think that extending the API is a design element we can defer until after we have post meta working; that is exactly what will lead us to build an API that only serves post meta and won't be reusable to other things, like querying data from custom tables, from API calls, and from other processing.

I'm nervous because these things aren't incompatible, but if we mean the one thing it's an approach that's good to avoid.

We don't have to build out all the API support, but I think it's critical that we focus on the interface and not be misled by rushing to get post meta connected. Post meta might be the first API consumer, but the API should not be designed for post meta.

@youknowriad's work is a good start; curious though why the registration starts with return array(…) vs. calling some registration function. was that haste/prudence to avoid creating a registration function? I would expect the first example to look something like add_action( 'init', … ) and then some call to register_block_binding_source()

talldan commented 7 months ago

@dmsnell It was probably bad wording. The API does needs to serve the broader goals, my wording is more reflective of the issue we're discussing this on being labelled "Connecting block attributes and custom fields", so I'm thinking about it in the context of custom fields being the first thing that might be integrated with the API.

In my https://github.com/WordPress/gutenberg/pull/51375, I actually had an API for defining custom sources (private) for both frontend and backend. Not sure why something like that wasn't considered as a base.

Could a description be added to the PR? That might be why it didn't get much traction.

youknowriad commented 7 months ago

@youknowriad's work is a good start; curious though why the registration starts with return array(…) vs. calling some registration function. was that haste/prudence to avoid creating a registration function?

Just a random choice to kind of map the JS part (where we export a config) rather than call a registration function. It seems more flexible to me to allow the caller to do what it wants with the config but to be honest, it's not an important choice here, just a quick random choice for the sake of the POC/

Could a description be added to the PR? That might be why it didn't get much traction.

Indeed, and my bad for the lack of it. I saw the PR as a throwable prototype and didn't invest much in it and I tried and failed to convey the API aspect in the discussions and issues.

talldan commented 7 months ago

Indeed, and my bad for the lack of it. I saw the PR as a throwable prototype and didn't invest much in it and I tried and failed to convey the API aspect in the discussions and issues.

Thanks for taking the time to add one ❤️

SantosGuillamot commented 7 months ago

FYI: I started this prototype to explain better how I imagine the project and help clarify the remaining questions/issues and the potential next steps. My initial idea is to merge that pull request under the experimental flag, after some polishing and review. From there, we could work on the next steps on top of it. This way, for anyone interested in testing it we can start as soon as possible.

SantosGuillamot commented 7 months ago

Update

I've been working on fixing some of the issues mentioned in the prototype, and I added more functionalities. I've decided to split the prototype into smaller pull requests so they are easier to review, and we can keep the conversations separate.

They are focused on creating different block bindings sources like "Post meta", "Site data", "Taxonomy meta", "Post data". Additionally, they provide the mechanisms to allow sources to easily create their UI in the editor and the logic in PHP.

These are the pull requests I created, with a brief explanation and what I consider the remaining tasks. Although I believe we could work on them after merging because everything is under an experimental flag.

1 . Add block bindings PHP registration mechanisms and "Post meta" source

https://github.com/WordPress/gutenberg/pull/57249

It adds the PHP mechanisms to register new sources and to replace the HTML based on the block attributes. Additionally, it adds support for the "Post meta" source and adapts the existing experiment for partially synced patterns.

2. Add components for the editor UI and create basic UI for the existing sources (Built on top of 1)

https://github.com/WordPress/gutenberg/pull/57258

It adds a basic UI to create bindings and the editor mechanisms to allow users to extend that UI, providing BlockBindingsFill, BlockBindingsFieldsList components, and a updateBlockBindingsAttribute helper. Additionally, it uses those mechanisms to show the "Post meta" source.

Remaining tasks for the prototype

3. Add block bindings "Site data" source (built on top of 2)

https://github.com/WordPress/gutenberg/pull/57259

Using the mechanisms from the previous PRs, it adds support for the "Site Data" source.

Remaining tasks for the prototype

4. Add block bindings "Post data" source (built on top of 2)

https://github.com/WordPress/gutenberg/pull/57260

Using the mechanisms from the previous PRs, it adds support for the "Post Data" source.

Remaining tasks for the prototype

5. Add support for creating bindings in templates (built on top of 2)

https://github.com/WordPress/gutenberg/pull/57315

It adds support to creating bindings in templates in the editor. Additionally, it modifies the "Post meta" source to ensure it works there and it is shown only in the relevant templates.

Remaining tasks for the prototype

6. Add taxonomy meta source in templates (built on top of 3)

https://github.com/WordPress/gutenberg/pull/57343

It adds support to the "Taxonomy meta" source in the templates where it is relevant.


It is important to keep in mind that this is just a prototype under an experimental flag. It is expected to change.

After the prototype

From there, once the prototype is ready and people can test it, I'd like to work on other tasks to keep pushing the project forward:

fabiankaegy commented 6 months ago

Just wanted to share that we talked about this feature in the WordPress 6.5 Hallway Hangout yesterday: https://make.wordpress.org/core/2024/01/17/hallway-hangout-lets-explore-wordpress-6-5-recap/

Specifically, we discussed how inline editing of the meta values would be a good end goal here.

Thinking about a price for example. We want to store it in meta so that we can display it both in a query loop and on the single post in different locations. But to a user it shouldn’t matter at all. For them editing it inline on the page template should feel as if it all just is in content.

You can view the discussion here: https://youtu.be/9q1Moih0u1Y?si=LiGDFyrfaaEgAKYZ&t=2551


An additional piece of feedback I wanted to share is that a limitation of the block level connections is that often we want to display a piece of meta with an adjacent label. But because we have no way to conditionally render an adjacent block depending on whether a meta value is set or not this will lead to orphaned labels which isn't ideal. Because of this limitation, we have opted to often building small atomic custom blocks for meta fields which include the label field right within it so that the label also doesn't render if the value is not set.

SantosGuillamot commented 6 months ago

Thanks a lot for sharing! Really interesting discussion and questions 🙂

Specifically, we discussed how inline editing of the meta values would be a good end goal here.

Yes, one of the goals is to be able to edit the value of the meta fields directly from the editor. However, that implies a bigger discussion and technical implementation so we decided to focus first on making it work as "read-only".

But because we have no way to conditionally render an adjacent block depending on whether a meta value is set or not this will lead to orphaned labels which isn't ideal.

That's really interesting use case. I'd like to think a bit more about it, but I believe that a combination of bindings and states could help approach it in the future:

In the meantime, and until we have something like inline tokens/bits, I was considering adding a before/after option in the bindings to allow users to create texts like: "This post has been written by { post_author }". Maybe we could add some logic/option there to not show the content if the value of the meta field is not set.

I'd love to know your thoughts 🙂 Although I'm not sure if we'll have time to implement any of this for 6.5.

fabiankaegy commented 6 months ago

By the way, the issue or "orphaned" labels is not limited to block connections. It also exists for things like the post modified date as shown here: https://github.com/WordPress/gutenberg/issues/57915

fabiankaegy commented 6 months ago

I'd love to know your thoughts 🙂 Although I'm not sure if we'll have time to implement any of this for 6.5.

Yeah I think all of this makes sense and the gradial approach with read only values to begin with also seems reasonable. My comments are not so much about 6.5 but the feature evolution in general after that :)

tresorama commented 6 months ago

By the way, the issue or "orphaned" labels is not limited to block connections. It also exists for things like the post modified date as shown here: #57915

One solution can be "block visibility condition" where the user can define an expression that will be evaluated in order to produce a boolean.

if "my-custom-field" then SHOW_BLOCK
fabiankaegy commented 6 months ago

@ndiego looks like your expertise again is required 😉

annezazu commented 6 months ago

👋🏼 Wanted to be sure to loop back and share a quick recap of a discussion that took place in a hallway hangout this week on 6.5 features. If you all can, please watch starting here to get the full effect. It's about a 10 minute discussion and the following was covered:

“Thinking about a price for example. We want to store it in meta so that we can display it both in a query loop and on the single post in different locations. But to a user it shouldn’t matter at all. For them editing it inline on the page template should feel as if it all just is in content.” – Fabian in the chat.

getdave commented 6 months ago

👋 Hello team. Whats the update on this feature for WP 6.5? Is it likely to land and if so which features (as originally outlined in the roadmap) will make it. Thanks in advance 🙏

cbirdsong commented 6 months ago

It feels like this functionality would also help with this problem? Has that been considered?

michalczaplinski commented 6 months ago

@getdave 👋

  1. Be able to connect block attributes and custom fields without a UI

This should certainly ship in 6.5 and we're working on a backport right now: https://github.com/WordPress/wordpress-develop/pull/5888

The remaining features centered around the UI for Block Bindings are a bit uncertain at the moment. We'd like to ship a basic UI and the prototype in https://github.com/WordPress/gutenberg/pull/57258/ is "working", but it mostly depends on how polished we can make it before 6.5.

michalczaplinski commented 6 months ago

@annezazu Thanks for sharing the feedback and questions!

Can you connect to a field that doesn't have a value set yet? For example, a plugin defining a meta field but an end user hasn't set a value.

Yes, it is currently possible to create a binding between an attribute and a meta field that does not have a value. The post meta "source" is a thin wrapper around get_post_meta(): https://github.com/WordPress/gutenberg/blob/57f604610002fb02d4e646ffe03cb52f09c302cb/lib/experimental/block-bindings/sources/post-meta.php#L17-L18

Can a connection be defined by a theme?

I'm not quite sure I understand the use case but I think that it should be possible! A block binding is just a special attribute on a block. It should be a matter of using a block filter to add those attributes to a block of their liking.

As for the other (more UI-focused) concerns mentioned in the video, we're still far from having anything set in stone in terms of the UI 🙂 The current prototype of the UI is quite basic but that's because we haven't yet dug deep enough to consider all the specific use cases that were mentioned! We'll get there 🙂.