pollora / meta
Typed WordPress meta declared with attributes: register_meta(), typed reads and writes, rules, schemas for UI drivers. Runs with or without Laravel.
Requires
- php: ^8.3
- nesbot/carbon: ^3.0
- pollora/attributes: ^0.1 || ^1.0
- pollora/discovery: ^0.1 || ^1.0
- pollora/hook: ^2.0.1
- psr/container: ^2.0
- psr/log: ^3.0
Requires (Dev)
- antecedent/patchwork: ^2.2
- brain/monkey: ^2.7
- laravel/pint: ^1.22.1
- mockery/mockery: ^1.6.12
- pestphp/pest: ^3.8.2
- phpstan/phpstan: ^2.1.16
- pollora/colt: ^10.0.1
- szepeviktor/phpstan-wordpress: ^2.0
Suggests
- pollora/colt: Registers a Colt model class declared with a post type attribute as that post type's model
Provides
None
Conflicts
None
Replaces
None
README
Typed WordPress meta, declared with attributes: a property of a class is a meta, its PHP type is the meta's type, its initial value is the default. The package registers it with register_meta(), reads and writes it with its PHP type (an int, a bool, a CarbonImmutable, an enum, a list, a data object), checks its rules on every write, and gives the compiled schemas to the tooling and to the UI drivers (ACF, Meta Box) that build the editing fields.
Part of Pollora, the Laravel framework for WordPress, where it is installed and wired for you: see Typed meta. It runs without Laravel too: in a plugin, a Bedrock site or any PHP project.
Installation
composer require pollora/meta
Requires PHP 8.3+ and WordPress. It builds on pollora/attributes (the #[Meta] attribute), pollora/discovery and pollora/hook.
Declaring
use Pollora\Attributes\Meta; use Pollora\Attributes\PostType; #[PostType('event')] class Event { #[Meta(showInRest: true, label: 'Capacity', rules: ['min:0', 'max:5000'])] public int $capacity = 0; #[Meta(showInRest: true)] public bool $soldOut = false; #[Meta] public ?CarbonImmutable $startsAt = null; }
#[Meta] takes key, showInRest, label, description, sanitize, capability, revisions, rules, single, items, control, group, hints, media and public; see the attribute's own documentation in pollora/attributes. #[PostMeta], #[TermMeta], #[UserMeta] and #[CommentMeta] declare the meta of a type you do not own.
Reading and writing
$event = $meta->of(Event::class, $postId); // MetaAccessor::of(), Meta::of() in a Pollora application $event->capacity; // int $event->fill(['capacity' => 250, 'soldOut' => true])->save(); $event->set('capacity', 12)->save(); // set(), fill() and save() return the record
A write goes through WordPress's meta API, under the declared key. A value that breaks a rule throws MetaValidationException and nothing is written; a stored value that cannot be read as its type reads as the property default.
The rules are checked by a MetaValidatorInterface. In a Pollora application it is Laravel's validator (min:0, max:5000, in:…, email…); without Laravel, pass your own to MetaAccessor, or none: the rules are then not checked.
Schemas and UI drivers
MetaAccessor::schemas() and schemaFor('post', 'event') give the compiled MetaSchemas. A package that builds editing fields implements MetaUiDriver and registers it by name with MetaAccessor::extend('acf', AcfDriver::class); MetaUiDriverConformance::check($driver) checks it against the contract from any test framework.
Wiring it outside Pollora
Discover the classes that declare meta, then build the accessor with the same pieces the framework's provider builds:
use Pollora\Discovery\Domain\Models\DiscoveryLocation; use Pollora\Discovery\Infrastructure\Services\DiscoveryEngine; use Pollora\Hook\Adapter\Out\WordPress\Action; use Pollora\Meta\Application\Services\MetaAccessor; use Pollora\Meta\Application\Services\MetaSchemaBuilder; use Pollora\Meta\Application\Services\MetaSchemaRepository; use Pollora\Meta\Domain\Services\MetaValueCaster; use Pollora\Meta\Infrastructure\Adapters\WordPressMetaRegistry; use Pollora\Meta\Infrastructure\Adapters\WordPressMetaStore; use Pollora\Meta\Infrastructure\Services\MetaDiscovery; $caster = new MetaValueCaster; $builder = new MetaSchemaBuilder; $schemas = new MetaSchemaRepository; (new DiscoveryEngine) ->addLocation(new DiscoveryLocation('MyPlugin\\', __DIR__.'/src')) ->addDiscovery('meta', new MetaDiscovery($builder, $schemas, new WordPressMetaRegistry(new Action, $caster))) ->run(); $meta = new MetaAccessor($schemas, $builder, new WordPressMetaStore, $caster);
The meta are registered with WordPress on init. The Eloquent accessors (HasTypedMeta), the Laravel validator, the pollora:meta:* commands and the pollora:doctor check stay in the framework.
Testing
composer test # unit tests, PHPStan, Pint
License
MIT. See LICENSE.