Search by

elephentity / codegen-wordpress

hsimah

The WordPress builder for Elephentity: compiles the IR into the physical schema WordPress needs. Build-time only.

v0.3.0 2026-09-20 22:05 UTC

README

The WordPress builder for Elephentity.

Implemented in Rust. Build a checkout with cargo build --release --locked, or install the executable on PATH with cargo install --path . --locked. The bin/eleph-gen-wordpress checkout launcher uses target/release/eleph-gen-wordpress (or a debug build during development). It never falls back to PHP. Installed Cargo binaries need neither PHP nor Cargo to run.

It reads one JSON request on stdin — the compiled spec, plus the target's configuration — and writes one JSON response on stdout: a path and a body per file. It never touches the filesystem. Signing and writing happen in elephentity-codegen, after this exits.

echo '{"elephentity":1,"irVersion":"1.1","target":"wordpress","config":{},
       "outputDirectory":"out","schema":{}}' | ./bin/eleph-gen-wordpress

That fails on the empty schema, which is the point: it should be obvious how.

Installing it

# From this repository:
cargo install --path . --locked

# Or, when using the Composer distribution:
composer require --dev elephentity/codegen-wordpress
cargo build --release --locked --manifest-path vendor/elephentity/codegen-wordpress/Cargo.toml

Use "builder": "eleph-gen-wordpress" for a Cargo installation, or the Composer launcher as shown below. Then name it in eleph.json:

{
  "targets": {
    "wordpress": {
      "builder": "vendor/bin/eleph-gen-wordpress",
      "output": "generated/wordpress"
    }
  }
}

Only where the project's project.yml names driver: wordpress — configuring this target for another driver is a response with an error, not a file.

What it provides

  • The wordpress driver, and the rules it imposes on storage.handle — a 20-character, lowercase-slug limit — so a spec is validated against this driver's own declaration rather than one hardcoded into the compiler.
  • The Taxonomy pattern (embedded in rust/provides.json): a project may use: it with nothing under spec/patterns/.
  • The physical schema elephentity/wordpress's adaptor loads at boot: table definitions, edge placements, post type and taxonomy registration arguments.

It depends on nothing of Elephentity's

The builder owns its typed wire IR in rust/ir.rs and emits runtime class names as strings. Protocol and IR versions are checked before reading the schema. Static capability declarations are embedded from rust/provides.json; their golden describe responses ensure the compiler sees the same integration, driver, and pattern contracts.

Working on it

cargo fmt --check
cargo clippy --all-targets -- -D warnings
cargo test --locked
./tools/php composer ci

Rust sources live in rust/. Each builder owns its IR types and version gate; there is no runtime dependency on the compiler or another builder. PHP in src/ and the bin/eleph-gen-wordpress-reference executable is retained as a migration oracle for the existing tests. Production entrypoints run Rust only. PHPStan still checks the reference and acceptance tests at level max.

The golden fixtures

tests/fixtures/golden/*/ holds a committed request and the exact response it produces, asserted byte for byte through the real binary. When a deliberate change moves them, regenerate and read the diff — it is the clearest description available of what the change did to every project's generated tree.

Admin views and linked posts

Elephentity records own their identity and relationships. The WordPress driver stores records in custom tables. Admin list/detail templates are generated by default; WordPress post linking is off by default. These choices are independent.

Declare project settings in spec/project.yml:

integrations:
  wordpress:
    adminTemplates: true
    linkPosts: false

Override either setting on an entity (the project must declare wordpress first):

entity: Item
storage:
  table: item
  handle: item
integrations:
  wordpress:
    linkPosts: true

Omitted or null entity settings inherit the project setting. Without a project setting, adminTemplates defaults to true and linkPosts defaults to false. showInAdmin: false still suppresses admin pages. Account and taxonomy entities retain their native WordPress storage and do not get these templates or linked posts.

Admin templates Linked posts Generated behavior
true false Elephentity list/detail pages; no post type or post-link column
true true Elephentity list/detail pages; linked post type with native post UI hidden
false true Native WordPress post UI; no generated entity templates
false false Storage only; no admin pages or post type

The builder emits admin-pages.php and admin/<Entity>/{list,detail}.php. Register Eleph\WordPress\Admin\Pages::fromManifest($path, $runtime)->register() on admin_menu, alongside the existing post-type registration on init. Both list/detail files must exist to register an admin page. Templates render through the runtime, use entity IDs, enforce read policies, escape output, and require the manage_options capability. Views are read-only and lists are paginated.

When post linking is enabled, an entity's storage.handle names its post type. The builder adds a nullable, unique wp_post_id column and a posts mapping to storage-manifest.php. The adaptor creates a draft post on each entity insertion, using title, name, or the entity name as its initial title. A WordPress creation failure fails the entity insertion. No post ID field belongs in the domain spec; wp_post_id is storage-owned and cannot also be declared by a field or edge. Entity foreign keys continue to reference entity id, never wp_post_id or WordPress post_parent. Post content and subsequent title changes are not synced.

OrphanGuard, hooked on before_delete_post, now clears the link when a post is deleted. It preserves the record so external WordPress deletion cannot bypass Elephentity's relationship rules. Legacy hand-declared postId fields are ordinary fields and should be removed from specs when adopting the integration. Existing records are not backfilled with posts. Existing database columns are not dropped by disabling generation; review the migration plan when changing these settings.

Deploy the updated WordPress runtime with the generated files: older runtimes do not support the new manifest argument or admin classes. Clog demonstrates default unlinked records and an Item entity that opts into linked posts.