elephentity / wordpress
WordPress storage adaptor for Elephentity. The only package permitted to reference WP symbols.
Requires
- php: >=8.3
- elephentity/runtime: ^0.8
Requires (Dev)
- friendsofphp/php-cs-fixer: ^3.64
- php-stubs/wordpress-stubs: ^6.7 || ^7.0
- phpstan/phpstan: ^2.0
- phpunit/phpunit: ^11.0 || ^12.0
Suggests
None
Provides
None
Conflicts
None
Replaces
None
README
The WordPress storage adaptor for Elephentity
— custom tables for entity fields, post types where the WordPress ecosystem actually
needs one, taxonomies as edges, wp_users as an account backend. The only package
permitted to name a WP_* symbol.
composer require elephentity/wordpress
You will also want the build-time builder that produces the manifest this package
loads: composer require --dev elephentity/codegen-wordpress.
What this is
A runtime package: the adaptor, the manifest loaders, the migration planner, the
registrars, the verifier. It implements
Eleph\Runtime\Storage\StorageAdaptor
and requires nothing but elephentity/runtime.
What this is not
Not a code generator. The compiler that turns a spec into the physical schema this
package loads lives in
elephentity-codegen-wordpress
— a separate, build-time-only repository that depends on nothing of Elephentity's, so
it never ships to production. This package and that one are held in step by a version
gate on the wire format, not a shared classpath: see
.llms/cross-repo.md.
Working in this repository
There is no local PHP. Everything runs in a container:
./tools/php composer ci # style, static analysis, tests
./tools/php composer style:fix
./tools/php vendor/bin/phpunit --filter SomeTest
composer ci must pass before committing. PHPStan runs at level max with no
baseline exclusions — the framework's whole claim is that generated code is provably
typed, and an exception here undermines that for the one package doing real I/O.
Before you commit anything that crosses a repository boundary
.llms/cross-repo.md is the closed list of what crosses. If
you changed something on it, open an issue on each repository it reaches, before or
with the push. .llms/README.md has the rule and
.llms/issue-template.md the shape.
Renaming a class here is invisible to every test in this repository or in
elephentity-codegen-wordpress. The builder emits the class name as a string, not an
import — see Runtime.php there. It only fails in a real project, at boot, after
generating. Regenerating clog in
elephentity-examples is
what catches it.
Generated admin pages and optional posts
The WordPress builder's integrations.wordpress settings default to
adminTemplates: true and linkPosts: false. Hook generated pages on admin_menu:
use Eleph\WordPress\Admin\Pages; add_action('admin_menu', static function () use ($runtime): void { Pages::fromManifest(__DIR__ . '/generated/wordpress/admin-pages.php', $runtime)->register(); });
The views require manage_options and load records through the runtime's read
policies. Applications supplying a parent menu slug must register that parent menu.
With templates enabled, the builder hides native post screens for linked entities.
Enabling linkPosts on an entity with storage.handle adds an integration-owned
wp_post_id column. WordPress::adaptor() consumes the manifest's posts mapping
and creates a draft post alongside each new entity. The entity keeps its own ID;
foreign keys and join tables continue to use Elephentity IDs. WordPress insertion
errors prevent entity insertion. Entity insertion errors clean up the new post.
The runtime's transaction must use the same WordPress database connection; WordPress
hooks can have external effects that a database rollback cannot undo.
OrphanGuard::onPostDeleted() clears wp_post_id instead of deleting entity rows.
This keeps external post deletion from bypassing Elephentity relationship rules.
Deleting an entity does not delete its linked post; post publication, updates,
backfilling existing records, and post retention remain application decisions.