Module 2 · Typed content: post types, meta and roles
Duration: 60 min · Prerequisites: module 1, and knowing what a WordPress meta and a role are.
By the end of this module you declare a content type, its fields and its rights once, in PHP classes, and Pollora derives the WordPress registration.
1. A post type, one class
Section titled “1. A post type, one class”use Pollora\Attributes\PostType;use Pollora\Attributes\PostType\HasArchive;use Pollora\Attributes\PostType\ShowInRest;use Pollora\Attributes\PostType\Supports;
#[PostType]#[HasArchive]#[ShowInRest]#[Supports(['title', 'editor'])]class Event {}The slug and the singular and plural names come from the class name: Event gives event, BookReview gives book-review. Auto-discovery finds the class: no register_post_type() call.
In development, after adding a class: php artisan discovery:clear. To generate one: php artisan pollora:make:post-type Event.
2. Typed meta
Section titled “2. Typed meta”A WordPress meta is a string: its key is repeated everywhere and every read casts it again. With Pollora the meta is a typed PHP property of the same class.
use Pollora\Attributes\Meta;
#[PostType]#[ShowInRest]class Event{ #[Meta(showInRest: true, label: 'Capacity', rules: ['min:0', 'max:5000'])] public int $capacity = 0; // key: capacity
#[Meta(showInRest: true)] public ?CarbonImmutable $startsAt = null; // key: starts_at}- The property type gives the meta type, its initial value gives the default (what an absent meta reads as), its name gives the key in snake_case.
- Pollora derives
register_meta(), sanitization and the REST schema. - A property with neither a default nor a nullable type is refused at discovery.
use Pollora\Support\Facades\Meta;
$event = Meta::of(Event::class, $postId);
$event->capacity; // int$event->capacity = 250;$event->save(); // written through update_metadata()WordPress still owns the storage: get_post_meta($id, 'capacity', true) still returns the string '250'. But a write through Meta::of() is checked at once: $event->capacity = 6000 throws a MetaValidationException before anything is written.
Two commands to check: php artisan pollora:meta:list (every meta, by class) and php artisan pollora:meta:audit (stored values that cannot be read as their type).
3. Roles declared in code
Section titled “3. Roles declared in code”use Pollora\Attributes\Role;use Pollora\Attributes\Role\Grants;use Pollora\Attributes\Role\Without;
#[Role('event_manager', label: 'Event manager', inherits: 'author')]#[Grants('moderate_comments')]#[Without('publish_posts')]final class EventManager {}This role starts from an author’s capabilities, can moderate comments and cannot publish posts.
The code is the only source of truth. Pollora does not write these roles to the database: it injects them in memory on every request. Change the class and deploy, the role follows everywhere, with nothing to migrate. Revert with Git and the previous rights are back.
Safety rules refuse, at discovery, what would grant the wrong rights:
- a sensitive capability (
manage_options,edit_users…) withoutallowSensitive: true; - redeclaring a core role (
editor,author…) with#[Role]: use#[ModifyRole]; - a capability both granted and removed by the same class.
#[Without] removes a capability, it never sets it to false: WordPress merges a user’s roles and an explicit denial would depend on their order.
To see the result: php artisan pollora:roles:list and php artisan pollora:roles:show event_manager.
Check what you remember
Section titled “Check what you remember”A post type slug
Which slug does Pollora generate for #[PostType] class BookReview {}?
-
bookreview -
book-review -
book_reviewWordPress accepts underscores, but Pollora generates the slug in kebab-case from the class name.
The slug, singular and plural are derived from the class name.
A meta key
Under which key is public ?CarbonImmutable $startsAt = null; marked #[Meta] stored?
-
startsAt -
starts_at -
_starts_atA key starting with
_is protected: WordPress hides it. It requires an explicitcapability.
The property name gives the key in snake_case, unless the key parameter picks another.
Writing an invalid value
A capacity meta has rules: ['max:5000']. What happens with $event->capacity = 6000;, then with update_post_meta($id, 'capacity', 6000);?
- The first throws a
MetaValidationException, the second writes the value - Both throw an exception
WordPress’s functions cannot refuse a value: they only apply the type’s sanitization.
- Both write the value, the error only shows on read
rules apply to writes from PHP (Meta::of(), models) and from REST.
Where a declared role lives
You delete the EventManager class and deploy. What happens to the event_manager role?
- It stays in the database until removed by hand
- It disappears: the code is the only source of truth
Pollora injects roles in memory on every request. Users who had it keep the slug but get no capability from it.
- It becomes a
subscriberrole
Pollora also undoes what a plugin may have written back to the database for a role removed from the code.
Sensitive rights
Which declarations are refused at discovery?
-
#[Grants('manage_options')]withoutallowSensitive: true -
#[Role('editor', …)]to redeclare a core role -
#[Without('publish_posts')]on a role inheriting fromauthorThat is the normal use of
#[Without]: removing an inherited capability. -
#[Role('event_manager', inherits: 'author')]
A core role is modified with #[ModifyRole].
Quiz Progress
0 / 0 questions answered (0%)
0 correct
Your score
0%
0 / 0 correct
Exercises
Section titled “Exercises”Exercise 02 · The capacity of an event
Section titled “Exercise 02 · The capacity of an event”lab/bin/lab reset 02, then edit app/Cms/PostTypes/Event.php.
Goal. Declare capacity as a typed meta: an integer, 0 by default, exposed in the REST API, never above 5000 (and not negative).
lab/bin/lab check 02 checks that the meta is registered as integer with show_in_rest, that it reads and writes with its PHP type, and that a value of 6000 is refused.
Exercise 03 · An “event manager” role
Section titled “Exercise 03 · An “event manager” role”lab/bin/lab reset 03, then edit app/Cms/Roles/EventManager.php.
Goal. Turn this class into the event_manager role, labelled “Event manager”, that starts from an author’s capabilities, can moderate comments and cannot publish posts.
lab/bin/lab check 03 checks that the role exists with its label, the inherited capabilities, and the presence or absence of the two capabilities.
Going further
Section titled “Going further”- Post Types and its attribute reference
- Typed Meta: arrays, objects, validation, REST
- Roles & Capabilities: post types with their own rights,
#[ModifyRole], migrating an existing role
Answered: 0 / 0 (0%)