Leveraging Gutenberg to manage dynamic content in WordPress plugins

Gutenberg is at it’s core a React app. If you don’t know React, you better react to the changing landscape and go pick out a crash course in React if you want to be relevant in WordPress development going forward. Well, to be fair I’m not saying that is exactly how it is (or how it should be). I will say however, that the landscape of plugins is very likely to be dominated in the coming years by software that has deep Gutenberg integration. And without the React and JSX knowledge to built that, you’re going to have little chance of competing in most categories of plugins. This is because Gutenberg provides us (as WordPress plugin developers) with a lot of tools we can leverage to make our plugins better and easier to use.

Case in point for this is utilizing Gutenberg as a “repeater solution” to enable users to make repeat selections. At SaberWP we did this recently in our Saber Teams plugin. The idea is we need to “connect” 2 objects, the “Team object” and the “Person object” in order to create a “Team Member” which is a single person associated with a single team. Our goal of course is not just to render out a team, but to have a dynamic and versatile data structure behind it suitable for API usage and as a foundation to build out other features. We’re thinking in terms of scaling the solution, so we want an object structure that can scale. Using a bridge object (team_member) allows us to do that. Now this means that the user needs an interface in which to do the following process:

  1. Search and find existing people (a custom post type “person”).
  2. Select a person to add to the team.
  3. View that person now successfully added to the team.
  4. Have the option to remove that person from the team.
  5. Have the option to replace the person added to the team with another person.
  6. Be able to repeat this process indefinitely.

How does Gutenberg help us support this functionality? Because Gutenberg inherently supports “repetition”, we can view the nesting capability of Gutenberg blocks as our solution. What we do is make a pair of blocks that work together, a container block that houses our inner blocks and then the inner block itself which represents the object model we want to repeat.

  1. Container block in our project is named “Team Member Container”. It has InnerBlock support (meaning it enables the user to nest blocks inside of it). It also has a restriction to only allow the block type that we want to nest inside it. Finally it has a template setting so that when Gutenberg loads the first nested block already appears ready to interact with.
  2. Nesting repeatable block in our project is named “Team Member”. This block provides the search functionality of pulling a list of people and showing them to the user, it handles a selection click and once a person is selected it renders them using ServerSideRender component and the “Person Render” block (that is built separately in the plugin from this repeater pair).

Now if you follow the concept this far and you’ve got your dev hat on, you might be thinking wait a minute… how does this really save the data in a scaleable way, I mean the blocks get saved automatically by Gutenberg in Post Content (post_content) but how would that translate into being able to search the members, present the data in an API or any number of other use cases? The answer is that we’re only using Gutenberg as our UX and we’re tying it to the background data services with API calls. When a user makes a selection in our nest Team Member block, we create a “team_member” post. This is a regular CPT post and we use post meta to store the foreign key details which in this case are the person_id (Person object selected) and the team_id (Team currently being edited in Gutenberg). The result is that our data structure and save process looks like this:

  1. Team post e.g. ID = 823 is edited in Gutenberg. The Gutenberg template loads with the Team Members selection blocks in place and the editor is locked to prevent removing the template or adding new blocks.
  2. User adds members to the team. After each person selection an API call is made to the endpoint /team/823/member/create/{person_id}. This creates a relationship post (team_member) with the team_id and person_id saved as post meta. Note that if post meta was not scaleable enough given more intense search requirements or large volumes of data, you can use a custom database table here or push to a document storage engine.
  3. When team data is fetched a meta query is used to find “team_member” posts that match the team_id. We then load the “person” posts from the list of “team_member” posts. While this may not be performant at high volumes it works well and is reasonably performant for this type of plugin (because teams are normally 5-10 people/posts).

The most challenging part of this solution is ensuring that your visual presentation of the “repeatable objects” stay accurate compared to the data you’re storing. This includes making sure when a “change object” is handled that the underlying data is updated, and that when an object is removed the corresponding post is deleted as well. Although there are a few complexities to this, it’s still incredibly easy to do in comparison to building out the entire editing interface from scratch.

As a bonus, guess what else your Gutenberg powered repeater interface can support natively? Sorting. That’s right consider how blocks are repositioned in the editor. Existing blocks can drag and dropped into new positions or the block menu can be used with the arrow buttons to shift the block up or down in the stack of blocks at the same level. To store this ordering data all you have to do is tap into the block position change event and save the new order indexes for each block in the container.

Similar Posts