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.
Architecture fit. pollora/entity is a thin, object-oriented wrapper over WordPress's register_post_type() / register_taxonomy() array APIs, packaged as a Composer library. It is designed to be consumed by a Laravel-style application container rather than dropped into a classic plugin bootstrap. That makes it a strong architectural fit only where we already have (a) Composer-managed dependencies, (b) a framework service-provider lifecycle, and (c) a policy that WordPress registration configuration belongs in code, not in the database via CPT-UI/ACF. In those environments it replaces brittle 40-line argument arrays with a typed, testable builder — a genuine maintainability win. In a legacy mu-plugin or theme-functions monolith it is an additional abstraction layer with no adjacent framework to justify it.
Integration feasibility. High on paper: composer require pollora/entity, bind a provider, declare entities, done. The real diligence is dependency weight — confirm whether it pulls illuminate/support, illuminate/contracts, or a container implementation, because that determines whether we are adopting "a CPT helper" or "a fragment of the Laravel ecosystem." Also confirm the PHP floor (likely 8.1+), the WordPress minimum, and whether registration is deferred to init internally or left to the caller. The latter is the single most common source of production breakage.
Technical risk. Assessed as medium-high, driven almost entirely by ecosystem maturity rather than code quality: 3 stars and 0 dependents means no independent production validation, no Stack Overflow corpus, and a near-certain single-maintainer bus factor. The 2026-10-07 release date suggests active maintenance but also a young, still-shifting API surface. Secondary risks: incomplete pass-through of register_post_type arguments (labels, capability_type/map_meta_cap, rest_base, supports, template), rewrite-rule flushing semantics, multisite behavior, WP-CLI and cron contexts where the container may not be booted, and coexistence with plugins that register the same slugs. MIT licensing is a meaningful mitigant — we can fork.
Key questions for the maintainer / spike.
register_post_type() invoked on init, and at what priority? Is it idempotent?meta_box_cb, rest_controller_class, capabilities)?register_post_meta / register_term_meta, or only post types and taxonomies?show_in_rest opt-in or opt-out? Is the generated schema correct for block editor consumption?wp-env/PHPUnit testability story.Stack fit. Adopt where the target application is Laravel + WordPress (Pollora, hybrid Laravel/Corcel backends) or Sage/Acorn with Composer autoloading and Blade. Do not adopt for client sites built on a page-builder + CPT-UI workflow; the abstraction fights the site's operational model.
Migration path. Do not rewrite the whole content model. Sequence it as:
register_post_type / register_taxonomy calls, including those coming from third-party plugins, and mark each as owned or external.ContentTypeRegistrar interface with two implementations: legacy and pollora/entity. All application code depends on the interface, never on the package directly. This is the exit strategy and costs roughly a day.get_post_type_object() output for legacy vs. new and diff; assert REST schema equivalence via /wp-json/wp/v2/types.Compatibility. Pin an exact minor version and commit the lockfile. Explicitly test against the WordPress versions in our support matrix plus the current release. Resolve source of truth conflicts up front: if CPT-UI, ACF, or a plugin also registers the same slug, decide which wins and document it. Add a CI check that fails if a post type is registered twice.
Sequencing. Week 1: technical spike and maintainer Q&A; Week 2: adapter seam + pilot CPT; Week 3: parity harness + public CPT migration; Week 4: taxonomies, docs, and a "how to drop to raw args" runbook for the team. Total ~4 engineer-weeks for a mid-sized content model, gated on the spike outcome.
Maintenance. This becomes a dependency we own whether or not the upstream maintainer does. Budget ~2 hours/month to track releases and review diffs. Pin versions; never allow ^ floating in production. Mirror the repo internally (or vendor it into packages/) so an upstream archive or force-push cannot break our build. MIT means a fork is always available, but a fork is a permanent headcount tax — treat it as a contingency, not a plan.
Support. There is no commercial support tier and effectively no community. Escalation is "we read the source." That is acceptable for a small, single-purpose library with a narrow surface area; it would be unacceptable for anything touching auth, payments, or data integrity. Ensure at least two engineers on the team can read the package source; single-owner knowledge is the real operational risk here.
Scaling. Runtime cost is negligible — this is boot-time configuration, not a hot path. The meaningful scaling dimension is cognitive: the abstraction pays off at roughly 5+ post types, where consistent label generation, capability mapping, and REST config stop being copy-paste. Beyond ~50 post types, the bottleneck is WordPress itself (admin UI, query performance), not this library.
Failure modes to monitor.
init, or the container not booted in WP-CLI/cron/REST contexts. Symptom: 404s, missing admin menus, empty REST types endpoint. Mitigate with a boot-time assertion that every declared entity is present in get_post_type_object().capability_type/map_meta_cap mapping resulting in editors unable to publish. Write an integration test per post type asserting role capabilities.Ramp-up. Laravel-fluent engineers: ~2 hours to productivity. WordPress-native engineers: ~1 day, mostly spent learning where the abstraction ends and raw WordPress begins. Required onboarding artifacts: (1) the adapter interface and why it exists, (2) a mapping table of package builder methods to underlying register_post_type keys, (3) the escape hatch — how to pass an unmodeled argument — and (4) the parity test harness so changes are validated rather than eyeballed.
Recommendation. Proceed with a time-boxed spike and the adapter seam in place. The opportunity is real — content-model-as-code is a defensible pattern and the package's surface area is small enough to audit — but at 3 stars and 0 dependents this must be adopted as vendored, pinned, and wrapped, never as a load-bearing dependency taken on faith. Reassess at the 6-month mark against upstream release cadence and whether dependents grow beyond single digits.
How can I help you explore Laravel packages today?