Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Entity Laravel Package

pollora/entity

Pollora Entity brings a fluent, Eloquent-style entity layer to WordPress development, letting you model posts, terms, and users as expressive PHP objects. Query, relate, and manipulate content with clean, type-safe APIs while retaining full compatibility with WordPress internals.

View on GitHub
Deep Wiki
Context7

Getting Started

This is a small, attribute-driven wrapper around register_post_type() and register_taxonomy(). It gives you a class per entity instead of an array of arguments, which is the main thing you get over hand-rolled register_post_type() calls.

Minimal steps:

  1. composer require pollora/entity (requires PHP 8.1+ for attributes; WordPress 5.9+ realistically).
  2. Make sure your theme/plugin autoloads entity classes before init runs — a require in functions.php or a PSR-4 autoloader wired into your plugin bootstrap.
  3. Declare an entity class and register it on init.
use Pollora\Entity\Attributes\PostType;
use Pollora\Entity\Attributes\Taxonomy;

#[PostType(
    name: 'book',
    singular: 'Book',
    plural: 'Books',
    public: true,
    hasArchive: true,
    showInRest: true,
    supports: ['title', 'editor', 'thumbnail', 'excerpt']
)]
#[Taxonomy(
    name: 'genre',
    singular: 'Genre',
    plural: 'Genres',
    postTypes: ['book'],
    hierarchical: true,
    showInRest: true
)]
final class Book {}

Then register once, typically from your plugin bootstrap:

add_action('init', function () {
    \Pollora\Entity\Entity::register(Book::class);
});

Where to look first: src/Attributes/ for the actual attribute signatures (this package is pre-1.0 in spirit — verify parameter names against source rather than trusting blog posts), then tests/ for the canonical registration examples. First real use case is almost always "I need a book CPT with a genre taxonomy and REST/Gutenberg support," which is exactly the two-attribute example above.

Implementation Patterns

One class per entity, grouped by domain. Put Book, Author, Genre in app/Entities/ (or src/ContentTypes/). This keeps the argument array in one place and makes the registration call a one-liner per class.

Prefer attributes for declaration, fluent setters for conditionals. Attributes can't express runtime logic (e.g. "public only if a feature flag is on"). Keep the attribute for the stable shape and adjust at registration time:

$cpt = \Pollora\Entity\PostType::make('book')
    ->labels(['name' => __('Books', 'textdomain')])
    ->public(get_option('books_enabled') === 'yes')
    ->register();

Bulk registration in a service provider. If you're on Pollora/Laravel, register entities in a dedicated provider rather than in functions.php; scan a directory or list classes explicitly. Explicit lists beat directory scanning — scanning adds filesystem I/O on every request and hides ordering.

Taxonomies attached to core post types. The same #[Taxonomy] works with postTypes: ['post']. This is often the highest-value use: giving the default post type a proper taxonomy without writing a 40-line register_taxonomy call.

Keep querying plain WordPress. This package registers entities; it does not query them. Use WP_Query(['post_type' => 'book', 'genre' => 'sci-fi']) or get_posts(). Don't expect an Eloquent-style layer — if you want that, that's Pollora's ORM/Model layer, a separate concern.

Testing. Registration is a side effect on global state. For unit tests, assert the generated args array (build the entity, call the args getter) rather than booting WordPress. For integration tests, run registration inside WP_UnitTestCase and assert post_type_exists('book') and taxonomy_exists('genre').

Gotchas and Tips

  • Timing is the classic failure. register_post_type() before init triggers _doing_it_wrong and produces "invalid post type" in the admin. Register on init (default priority is fine; use 9 only if something else must read it earlier).
  • Rewrite rules. New CPTs with hasArchive/custom slugs 404 until rewrite rules are flushed. Flush once on plugin activation or deploy (flush_rewrite_rules() after registration), never on every request. If you're iterating locally, wp rewrite flush is your friend.
  • Autoload timing. Attributes only work if the class is loaded. If you register a class string, the package must be able to resolve/reflect it — an unloaded class means a silent no-op or a reflection error. Ensure your autoloader is registered before init.
  • Reserved names. post, page, attachment, revision, nav_menu_item, action, author, order, theme are taken. Taxonomy names are capped at 32 characters, and the reserved-term list (category, tag, post_tag, etc.) will bite you. Validate names before you ship.
  • REST / block editor. Set showInRest: true (and show_in_rest on the taxonomy) or the entity won't appear in Gutenberg's sidebar or the REST API. This is the single most common "it registered but I can't see it" issue.
  • Labels. Auto-generated labels from singular/plural are fine in English and wrong in most other languages. Supply a labels() array with __() calls for anything user-facing. Remember menu_name, all_items, add_new_item, search_items.
  • Menu icon and position. For admin UX, set menuIcon (a Dashicons slug or base64 SVG) and menuPosition; two CPTs defaulting to position 25 will collide and one will silently move.
  • Capabilities. The default capability_type => 'post' means anyone who can edit posts can edit your CPT. For anything with editorial workflow, set capability_type, map_meta_cap: true, and grant caps on activation.
  • Debugging. get_post_types(['_builtin' => false], 'objects') and get_taxonomies() show what actually registered. post_type_exists() catches typos. Hook register_post_type_args / register_taxonomy_args to inspect or patch args late.
  • Extension points. Subclass and override the args-building method if you need project-wide defaults (e.g. always show_in_rest and a standard icon); write a custom attribute if you have a repeated cluster of settings. Avoid forking the package for small tweaks.
  • Maturity warning. Three stars and zero dependents means you are the QA team: pin an exact version in composer.json, read the source before relying on a documented option, and expect API churn. Wrap registration behind your own thin function so a future swap to plain register_post_type() is a one-file change.
  • Deactivation hygiene. If entities are registered by a plugin, unregister_post_type() on deactivation is not automatic — content becomes orphaned and invisible. Decide deliberately whether the CPT belongs to the theme or the plugin (it belongs to the plugin).
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
frankyso/ipaymu
3mdikram/worldmoneywords
snowsoft/config
itmosoft/laravel-subscriptions
larva/laravel-ai-qwen
adachsoft/api-version-bundle
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core