Gutenberg Custom Post Block
WordPress FSE is still in its infancy and its functionality for archive loops is really limited. We needed a query loop that displays different content under different circumstances, which neither the Query Loop block or the Post Template block supports. So the solution was to create a custom block.
The block creation shows the usual schema.
Layout
The project layout is the following:
.
..
block.json
index.js
init.php
[partials]
|..template-one.php
\.. template-default.php
Partials is a directory, where the template files are kept as PHP name, following the convention template-{name}.php
block.json:
Only the essential parts are highlighted
{
"$schema": "https://schemas.wp.org/trunk/block.json",
"apiVersion": 2,
.....
"usesContext": ["postId", "postType"],
"parent": ["core/post-template"]
.....
"editorScript": "file:./index.js",
}
index.js:
I’ve actually copied the way the vanilla Gutenberg block extract the title, featured image and categories from their defualt post-template blocks.
import { registerBlockType } from "@wordpress/blocks";
import { store, useEntityProp } from "@wordpress/core-data";
import { useSelect } from "@wordpress/data";
import metadata from "./block.json";
const { name, attributes, title, parent, icon } = metadata;
import './scss/style.scss';
registerBlockType(
name,
{
title,
icon,
parent,
attributes,
usesContext: ["postId", "postType"],
edit (props) {
const {postId, postType} = props.context;
const [title] = useEntityProp('postType', postType, 'title', postId);
const [featuredImage] = useEntityProp('postType', postType, 'featured_media', postId);
let [catId] = useEntityProp('postType', postType, 'categories', postId);
catId = catId ? catId[0] : null;
let featuredImg = '';
let category = '';
if (catId) {
category = useSelect((select) => {
const categories = select('core').getEntityRecords('taxonomy', 'category', { term_id : catId });
if (!categories) {
return '';
}
for (const [_, catObj] of Object.entries(categories)) {
if (catObj.id === catId) {
return catObj.name;
}
}
});
}
const media = useSelect(
(select) =>
featuredImage &&
select( store ).getMedia( featuredImage, { context: 'view' } ),
[featuredImage]
);
if (media) {
featuredImg = (
<img src={media.media_details.sizes.medium.source_url} />
);
}
return (
<article className="custom-post is-admin">
<div className="custom-post__link">
<div className="custom-post__meta">
<span className="custom-post__meta-field category">
{category}
</span>
</div>
<h3 className="custom-post__title">
{title}
</h3>
<figure className="custom-post__featured">
{featuredImg}
</figure>
</div>
</article>
);
},
save() {
return null;
}
}
);
index.php
And since this is a dynamic block – we need a callback function, which in our case is custom_post. For this to work – we need a custom block initialization, in the project I implemented this we had this in the theme. You will need to figure how to load a block on your own (GIYF) 🙂
<?php
function custom_post($attributes, $content, $block)
{
if (empty($block) || empty($block->context) || empty($block->context['postId'])) {
return '';
}
$post_id = $block->context['postId'];
if (empty($post_id)) {
return "";
}
$CustomPost = new CustomPost($post_id);
return $CustomPost->process_card();
}
class CustomPost
{
private int $post_id;
private string $template;
public function __construct($post_id)
{
$this->post_id = $post_id;
$this->template = "minimal";
}
public function process_card() : string
{
//this is the main place where you execute the logic which determins which template to use
switch($something) {
case "one":
$this->template = "one";
break;
.........
default:
$this->tempalte = "default";
}
return $this->get_template();
}
private function get_template() : string
{
$path = __DIR__ . "/partials/template-{$this->template}.php";
ob_start();
if (file_exists($path)) {
require $path;
} else {
require __DIR__ . "/partials/template-minimal.php";
}
$result = ob_get_clean();
return empty($result)
? ''
: $result;
}
}
Templates
The templates looks something like this
<?php
$figure_class = "custom-post__featured";
$featured_image = get_the_post_thumbnail_url($post_id, 'medium');
$title = get_the_title($post_id);
$url = get_permalink($post_id);
?>
<article class="custom-post blog">
<a href="<?php echo $url; ?>" class="custom-post__link">
<h5 class="custom-post__title">
<?php echo $title; ?>
</h5>
<figure class="<?php echo $figure_class; ?>">
<?php if (!empty($featured_image)) : ?>
<img
src="<?php echo $featured_image; ?>"
alt="<?php echo $title; ?>"
>
<?php endif; ?>
</figure>
</a>
</article>
Keep in mind that every parameter you pass to this template needs to be defined in the get_template method in the above class.