The lengths to which I will go to “Do It Wrong™”

First, let’s get one thing straight: WordPress is built on PHP. The Gutenberg/Block Editor team may love React more than anyone else outside of Facebook, but ultimately WordPress is still built on PHP, and the WordPress developer community is built of PHP developers.

It doesn’t have to be PHP. I mean, PHP is kind of a garbage language if I’m honest. But the point is, there is server-side processing happening. It is what makes it possible to dynamically assemble page output, and create separate data, functionality, and design layers in a web application. But for reasons I barely understand and certainly do not agree with, the Gutenberg team decided to make templates pure HTML. No PHP allowed. I feel like they’ve separated the wrong layers.

Anyway, the problem I’ve run into here is that I have perhaps become a bit too dependent upon the one place in Gutenberg where you can still write PHP: block patterns. Block patterns are PHP files, not HTML files like templates or template parts. So naturally I’ve been tempted to misuse block patterns. (Although it didn’t really become apparent to me that I was misusing them, until I was past the point of no return.)

I have always used Advanced Custom Fields extensively in my WordPress site development. And in the Gutenberg era, it’s made it possible for me to create custom blocks while still working primarily in the familiar world of PHP.

I’m getting better at creating block patterns, but today I discovered a way that I may be fundamentally misunderstanding them. I’m really kind of using them as template parts, I guess, but as a cheat way to get access to PHP. Specifically, I’ve been putting block patterns into my templates. Not as ways of dropping in a pre-formatted set of editable blocks in the Block Editor itself, but as “hardcoded” ways to include some PHP-driven elements in the non-editable parts of my page templates.

This has all been working perfectly well until today, when I decided I wanted to be able to drop the contents of an old-school ACF field into a block pattern I created. This pattern is for displaying some of the meta data about a post: post date, categories and tags, and now a new custom field: the byline.

Don’t say I should just use author fields; if you don’t understand a context where a news post might have an author who is not the person who is entering the post in WordPress (and who, in fact, does not even have a WordPress login) I don’t know what to tell you.

I figured, OK, this will be a no-brainer. I’ll just put this into the block pattern PHP file:

the_field('byline');


Oops… that didn’t work. Let’s try this:

the_field('byline', $post);


Hmm… still didn’t work. Oh sure, I need to get the global variable first:

global $post;
the_field('byline', $post);


Wow, that doesn’t even work. Well, what about this?

the_field('byline', get_queried_object_id());


Take a guess.

So, here’s the problem. Block patterns have no context. When a block pattern gets inserted into a page, then it does have context. So if I were to go and edit the post, and insert this block pattern into the actual post content, then it would display the data. (I guess… I didn’t try that until several steps later.) But that isn’t what I want. I don’t want the client to have to remember to insert this “post meta” block pattern at the top of every post. I want it in the template. That’s the whole point.

But if the block pattern is directly in the template, it doesn’t know the current post ID. More importantly, it has no way to even access the current post ID. I’m Doing It Wrong™.

Fine, I know I’m doing it wrong. But once again I feel like WordPress itself is fundamentally wrong here.

And I’m determined to do it my way, even if it’s “wrong,” dammit.

After probing several further layers deep on this (including trying to use the ACF shortcode in the block pattern — which is where I realized it does work if you insert the block pattern into the actual post content, but not in the template), I determined that there was really only one way to do it wrong that still actually works.

I created a custom ACF block designed solely to just output the value of any arbitrary custom field.

This is so unbelievably WRONG I can taste it. But it’s the only way I’ve found to do what, in all of my experience across nearly two decades of working with WordPress and PHP in general, seems like it should be a really freaking easy thing to do.

So, please, DO NOT UNDER ANY CIRCUMSTANCES DO WHAT I AM ABOUT TO SHOW YOU HERE. You’ve been warned.

OK. First, you need to have Advanced Custom Fields Pro installed, so you can use ACF Blocks. In your theme’s functions.php file, do this:

register_block_type(dirname(__FILE__) . '/blocks/acf-diw');


Then you need to make sure you have this hierarchy of files in your theme:

blocks/
	acf-diw/
		acf-diw.css
		acf-diw.php
		block.json


We’re not actually going to put anything in acf-diw.css at this point, but you might want it later.

Here’s what goes in the block.json file:

{
	"name": "acf/acf-diw",
	"title": "ACF DIW",
	"description": "",
	"style": "file:./acf-diw.css",
	"script": "",
	"category": "",
	"icon": "editor-code",
	"apiVersion": 2,
	"keywords": [],
	"acf": {
		"mode": "preview",
		"renderTemplate": "acf-diw.php",
		"postTypes": []
	},
	"supports": {
		"inserter": false
	},
	"styles": []
}


Then in your acf-diw.php file you need this. Note this is the absolute bare minimum code you need for this to work; I would recommend actually using the get_block_wrapper_attributes() function to allow you to include CSS classes, styles, an ID, etc. in the output — and my actual version does have that. This is just to key you in on what specifically makes this trick work. It’s not highly secure, but I think it’s reasonably safe, specifically because we’re checking to make sure we’ve passed in the name of a field, and that there’s actually a custom field on this page/post with that name, and we’re sanitizing the output.

<?php
global $post;
if (!empty($block['data']['field']) && $field_value = get_field(esc_attr($block['data']['field']), $post)) {
	echo '<span>' . wp_kses_post($field_value) . '</span>';
}


OK, now with all of that in place, here’s what you can put into a block pattern file and actually get the output of an ACF custom field that exists on that page/post:

<!-- wp:acf/acf-diw {"name":"acf/acf-diw","data":{"field":"byline"}} /-->


Replace “byline” with the name of your ACF field.

It works. But you didn’t hear it from me.


Side note: This does not work properly when it’s actually inserted into the page content as a regular block. As configured, it is strictly for use in block pattern files where you want to break what block patterns are apparently intended for. (That’s why we have the "inserter": false line in the JSON file.) You could build this in a way that would make it work properly as an inserted block, but why bother? That is, expressly, what the ACF shortcode is for.