ralfhortt/wp-post-meta

WordPress post meta utilities for admin list tables

Maintainers

Package info

github.com/Horttcore/wp-post-meta

pkg:composer/ralfhortt/wp-post-meta

Transparency log

Statistics

Installs: 131

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

dev-main 2026-08-17 07:41 UTC

This package is auto-updated.

Last update: 2026-08-17 07:41:37 UTC


README

WordPress Post Meta

Tests codecov PHP Version Latest Version License

WordPress Post Meta Utilities

Fluent Composer package for registering post meta with the REST API, admin list tables, the block editor, and block bindings — without extending classes or writing boilerplate.

PHP 8.1+ · WordPress 5.0+ (block bindings require 6.5+)

composer require ralfhortt/wp-post-meta

Quick start

Register on init. Chain field definitions with optional admin and editor integrations, then call register():

use RalfHortt\Meta\Meta;

add_action('init', function () {
    Meta::for(objectSubtypes: 'product')
        ->addString(key: 'sku', description: 'Product SKU')
        ->addNumber(key: 'price', description: 'Product price')
        ->addBoolean(key: 'featured', description: 'Is featured')
        ->addColumn(key: 'sku', label: __('SKU', 'plugin'), sortable: true)
        ->addColumn(
            key: 'price',
            label: __('Price', 'plugin'),
            render: fn($postId) => '$' . number_format((float) get_post_meta($postId, 'price', true), 2),
            sortable: true
        )
        ->showInQuickEdit(keys: ['sku', 'price'])
        ->showInEditor(keys: ['sku', 'price'], title: __('Product Details', 'plugin'))
        ->asBlockBindingSource(keys: [
            'sku' => fn($value) => "SKU: {$value}",
            'price' => fn($value) => '$' . number_format((float) $value, 2),
        ])
        ->needsCapability(capability: 'edit_posts')
        ->register();
});

REST responses include typed meta under meta:

{ "id": 123, "meta": { "sku": "PROD-123", "price": 99.99, "featured": true } }

Meta class

Meta::for(objectType: 'post', objectSubtypes: 'product') creates a registrar for one or more post types. Every integration below is optional — add only what you need.

Field types

Method REST type Storage / notes
addString() string Plain text
addText() string Multiline (textarea in quick edit / editor)
addBoolean() boolean Converts true/false'1'/'0'
addInteger() integer Casts to int
addNumber() number Casts to float
addArray() array Returns [] when empty; not supported in quick edit, editor, or bindings
addDate() string ISO 8601 in API; inputFormat for storage ('Y-m-d' or 'timestamp')
add() any Low-level; accepts custom getCallback, updateCallback, authCallback

Authorization

needsCapability(capability: 'edit_posts') applies an auth callback to all fields that do not already have one. For per-field rules, pass authCallback to add():

->needsCapability(capability: 'manage_options')  // all fields

->add(key: 'cost', type: 'number', authCallback: fn($allowed, $context, $objectId) =>
    current_user_can('manage_shop') && $objectId > 0
)

updateCallback can return WP_Error to reject invalid writes (e.g. SKU format validation).

Admin & editor integrations

Method Purpose
addColumn(key, label, render?, sortable?) Admin list table column
showInQuickEdit(keys) Inline edit on list table (string, number, integer, boolean)
showInEditor(keys, title, metaBoxId?, context?) Gutenberg sidebar panel (context: side or normal)
asBlockBindingSource(keys, render?) Block binding source per field (WP 6.5+)

Block bindings register sources as wp-post-meta/{key} (underscores → dashes). Bind in block markup:

<!-- wp:paragraph { "metadata": { "bindings": { "content": { "source": "wp-post-meta/sku" } } } } -->
<p>Fallback</p>
<!-- /wp:paragraph -->

Pass a render callback for prefixes, suffixes, or formatting: fn(mixed $value, int $postId): ?string. Keyed arrays set per-field renderers; the second render argument applies to a single key or a list of keys. Array/object fields are skipped. On WordPress < 6.5, binding registration is skipped automatically.

Call showInEditor() multiple times to create separate panels. Array/object field types are skipped in the editor and bindings.

Columns class

Standalone admin columns without REST meta registration:

use RalfHortt\Meta\Columns;

Columns::for(postTypes: 'product')
    ->add(key: 'sku', label: __('SKU', 'plugin'))
    ->addCurrency(key: 'price', label: __('Price', 'plugin'), currencySymbol: '$')
    ->addDate(key: 'event_date', label: __('Date', 'plugin'), format: 'F j, Y')
    ->addBoolean(key: 'featured', label: __('Featured', 'plugin'))
    ->addImage(key: 'thumbnail', label: __('Image', 'plugin'), width: 50, height: 50)
    ->addList(key: 'tags', label: __('Tags', 'plugin'), separator: ', ', limit: 3)
    ->sortable(keys: ['price', 'event_date'])
    ->register();

Use add(key, label, render) for custom HTML output. sortable(keys) and priority(value) configure list behavior.

API reference

Meta

Factory Meta::for(objectType?, objectSubtypes?)
Fields addString, addText, addBoolean, addInteger, addNumber, addArray, addDate, add
Auth needsCapability(capability)
Admin addColumn, showInQuickEdit, showInEditor, asBlockBindingSource
Register register()

Columns

Factory Columns::for(postTypes)
Columns add, addCurrency, addDate, addBoolean, addImage, addList
Config sortable(keys), priority(value)
Register register()

All methods return self for chaining. Named arguments are supported throughout.

Development

composer test              # Run tests (160 passing)
composer test:coverage     # Requires Xdebug or PCOV

Changelog

v2.0.0 — Fluent API rewrite, REST meta registration, column integration, type helpers, PHP 8.1+ named arguments.

v1.0.0 — Initial release (abstract class pattern).

License

MIT · Ralf Hortt — mail@ralfhortt.dev