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 Command Laravel Package

wp-cli/entity-command

WP-CLI commands for managing WordPress comments, menus, options, posts, sites, terms, and users. Create, update, delete, list, and moderate entities from the command line, with support for metadata, custom fields, and block notes.

View on GitHub
Deep Wiki
Context7

Getting Started

This package is not something you composer require into a Laravel app — it is a bundle inside WP-CLI. If wp runs against a WordPress install, wp post, wp comment, wp term, wp user, wp option, wp menu, wp site, and wp cap are already available. The 105 stars are misleading: it ships in the default WP-CLI phar, so it is present on virtually every WordPress host you will touch, which makes it much lower-risk than a random 105-star dependency.

Minimal steps:

  1. Confirm it is there and see the surface:
    wp --info
    wp help post
    wp help post list
    
  2. Read the help for the specific subcommand, not the README. The README in this repo is generated from command docblocks, so wp help <entity> <subcommand> is always the authoritative, version-matched reference.
  3. Learn the three output switches immediately — they are the whole reason this is scriptable:
    wp post list --format=json          # machine-readable payload on STDOUT
    wp post list --field=ID            # one column, newline-separated
    wp post list --format=ids          # space-separated, xargs-friendly
    wp post create ./content.html --post_type=page --porcelain   # just the new ID
    

First use case for a Laravel developer: inspecting and mutating WordPress content while you are building the Laravel side of a headless/adjacent install. Typical first command is an export you can feed into an importer:

wp post list --post_type=post --post_status=publish \
  --fields=ID,post_title,post_name,post_date,post_content \
  --format=json > posts.json

Then json_decode(file_get_contents('posts.json'), true) in a Laravel command. No SQL, no plugin, no auth layer.

Implementation Patterns

JSON in, JSON out, exit code checked. Treat every wp call as an external HTTP-ish dependency. From Laravel, use Symfony\Component\Process\Process with an argument array (never string interpolation — post content with quotes will bite you):

$process = new Process([
    'wp', 'post', 'create', './draft.html',
    '--post_type=page',
    '--post_status=draft',
    '--porcelain',
    '--path=/var/www/wordpress',
]);
$process->run();

if (! $process->isSuccessful()) {
    throw new RuntimeException($process->getErrorOutput());
}

$postId = (int) trim($process->getOutput());

Note that --path (and --url on multisite) is usually how you point wp at a site from a Laravel process whose working directory is unrelated.

Seeder / bootstrap scripts for a fresh site. The * generate commands plus --porcelain compose into idempotent-ish fixture setup:

wp user create editor [email protected] --role=editor --user_pass="$PW" --porcelain
wp post create ./home.html --post_type=page --post_title=Home --post_status=publish --porcelain
wp menu create "Primary"
wp menu item add-post primary 12 --title="Home"
wp menu location assign primary primary
wp option update blogdescription "Staging copy"
wp option update show_on_front page
wp option update page_on_front 12

Bulk operations via --format=ids + xargs. This is the canonical pattern and appears all over the package examples:

# Trash every spam comment
wp comment delete $(wp comment list --status=spam --format=ids)

# Add a meta flag to each generated comment
wp comment generate --format=ids --count=3 --post_id=123 \
  | xargs -d ' ' -I % wp comment meta add % reviewed 1

Prefer xargs -d ' ' -I % (or -n1) over bare xargs so IDs are not re-split oddly and so one failure does not silently shift the rest.

Read-modify-write on structured options. wp option + --format=json is the safe way to touch array-valued options without hand-rolling serialization:

wp option get my_plugin_settings --format=json \
  | jq '.features.enabled = true' \
  | wp option update my_plugin_settings --format=json

Same applies to meta: wp post meta update <id> <key> --format=json stores a properly serialized array rather than the literal string ["a","b"].

Content from files and STDIN. wp post create <file> takes the post body from a file, and meta/option commands read the value from STDIN when the value argument is omitted. That is how you avoid shell-quoting hell for HTML and JSON payloads:

wp post create ./body.html --post_title="Release notes" --porcelain
cat settings.json | wp option update my_settings --format=json

Deploy-time reconciliation. On deploy, run a small script that uses exists/get as a probe, then create-or-update. wp comment exists <id> prints success and exits 0/1; for other entities, wp post get <id> --field=ID with exit-code checking is the equivalent:

if ! wp post get "$ID" --field=ID >/dev/null 2>&1; then
  wp post create ./page.html --import_id="$ID" --porcelain
fi

Multisite as a first-class concern. wp site list --fields=blog_id,url,archived,deleted --format=json gives you the network inventory; wp site create --slug=demo --title=Demo and wp site empty <id> --yes handle tenant provisioning and reset. Every subsequent command needs --url=<site> or it will run against the network's main site.

Use it as a hook-respecting mutation layer, not a SQL shortcut. Every write goes through wp_insert_post(), wp_update_term(), etc., so plugin hooks, cache invalidation, revisions, and slug deduplication all behave exactly as they do in the admin. If you are currently doing DB::statement('UPDATE wp_posts ...') from Laravel, this is the safer replacement — at the cost of ~1–2s of WordPress bootstrap per invocation.

Wrap it, do not scatter it. In a Laravel codebase, put a thin WordPressCli service around Process that fixes --path, forces --format=json/--porcelain, applies --yes in non-interactive contexts, and maps non-zero exits to exceptions. Raw exec("wp post list") calls sprinkled around are how you end up with silent empty arrays when the binary is missing.

Gotchas and Tips

  • STDOUT is data, STDERR is humans. Success:/Warning:/Error: lines go to STDERR, records go to STDOUT. Do not 2>&1 and then json_decode; you will get parse failures the first time something warns. Check isSuccessful() separately.
  • Aliasing fields. List commands accept --field=ID as an alias for comment_ID/post_ID/term_id, but --fields= wants the real column names. Mixing the two silently drops columns.
  • Be explicit about limits. These commands delegate to WP_Query/WP_Comment_Query/WP_User_Query, which have their own defaults. A "full export" that omits --posts_per_page=-1 or --number=<n> will quietly truncate. Add --format=count to a second call if you want a cheap assertion that you got everything.
  • delete is not always delete. Posts and comments go to trash without --force. Bulk cleanup scripts that omit --force look successful and leave content in the trash bin forever.
  • --yes in CI, and guard the destructive ones. wp site empty, wp post delete --force, wp db reset-adjacent flows will happily proceed non-interactively with --yes. Gate those scripts behind an explicit environment check rather than letting a deploy pipeline call them by accident.
  • Wrong package for the job. wp search-replace, wp db *, wp media *, wp transient *, wp cache *, wp cron *, and wp scaffold * live in other WP-CLI packages. Reaching for entity-command to do a URL migration or import an image will waste an hour.
  • Meta semantics differ. add appends (and allows duplicate keys), update creates-or-updates, set is the "just make it so" verb. wp comment meta get <id> <key> without --single returns an array even for a single value, which trips up strict === comparisons.
  • Taxonomy term updates need --by. wp term update <taxonomy> <term> interprets <term> as an ID unless you pass --by=slug, and slug-vs-name ambiguity is the most common "it updated the wrong term" bug.
  • Version skew is real. The bundled entity-command version tracks your WP-CLI version, and newer features (e.g. the WordPress 6.9+ --comment_type=note workflows in the README) need both a recent WP-CLI and a recent WordPress. Pin wp cli update --version=x.y.z in CI images so scripts do not break on a host's PHP/WP-CLI pairing.
  • Bootstrapping cost. ~1–2s per invocation. A loop of 500 wp post meta update calls takes ~15 minutes. Batch with wp eval-file (a PHP file that loops and calls the same APIs in one boot) when the count gets large; keep xargs for tens of items.
  • Contributing back. The README is generated — edit the command docblock, not README.md. Run the narrow check: composer phpcs -- <file>, composer phpstan, then composer behat -- features/<name>.feature:<line> with NO_COLOR=1 and WP_CLI_TEST_QUIET=1 exported. composer test reinstalls WordPress per Behat scenario and is a PR-time check, not a two-line-change check.
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.
se7enxweb/flysystem
lampwick/laravel
jeslage/statamic-emojis
micahdshackelford/rmap-laravel
draftsmaninc/draftsman
square-bit/laravel-volition
himito/laravel-mailman
htinlynn/genius-helper
maco-studios/hephaestus
elmogy/larafast
xeviant/laravel-paystack
gorilladash/laravel-website-sdk
martialbe/flysystem-oss
chondal/3312client
jeroen-g/flickr
opscale-co/nova-genbi
itsjustvita/laravel-retention
tequilarapido/trackit
gregsphotography/site-builder-filament
cabaoctavio/laravel-query-log