Search by

arraypress / wp-object-utils

arraypress

Find a post, term or user by whatever you were handed: an id, a slug, a name, an email or the object itself.

Package info

github.com/arraypress/wp-object-utils

Homepage

pkg:composer/arraypress/wp-object-utils

Statistics

Installs: 11

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

v1.0.0 2026-08-25 18:41 UTC

This package is auto-updated.

Last update: 2026-08-26 08:45:13 UTC


README

Find a post, term or user by whatever you were handed: an id, a slug, a name, an email, or the object itself.

The problem

Core has a different function per field, so calling one means deciding first what kind of thing you are holding:

get_post( $id );
get_page_by_path( $slug );
get_term_by( 'slug', $value, $taxonomy );
get_user_by( 'email', $value );

A CSV column headed category will not tell you which it is. Neither will a shortcode attribute, a REST parameter or a settings field — all three happily contain an id from one site and a slug from the next.

They also fail differently. get_term() returns a WP_Error for a term that does not exist, and a WP_Error is an object — so if ( $term ) is true and the next line reads ->term_id off an error.

Usage

use ArrayPress\ObjectUtils\{ Posts, Terms, Users };

Posts::find( 42 );                        // id
Posts::find( 'hello-world' );             // slug
Posts::find( 'Hello World' );             // title
Posts::find( $post );                     // straight back

Terms::find( 'shoes', 'product_cat' );    // id, slug or name
Users::find( 'ada@example.test' );        // id, email, login or nicename

One way in, and one kind of nothing: every miss is null.

Narrowing

Posts::find( 'a-product', 'product' );            // slug and title only
Posts::find( 'draft-one', 'any', 'publish' );     // by status too

The post type narrows the slug and title lookups but never the id one — an id is unique across every type, so restricting it would refuse a post the caller has already identified exactly.

Several at once

Posts::find_many( [ 42, 'hello-world', 'nothing' ] );        // [ 42 ] — deduped, misses dropped
Terms::find_many( [ 'shoes', 21 ], 'product_cat', true );    // WP_Term objects

Searching, for an autocomplete

Posts::search_options( 'blue', 'product' );   // [ [ 'value' => 10, 'label' => 'Blue Shirt' ], … ]
Terms::search_options( 'sho', 'product_cat' );
Users::search_options( 'ada' );               // 'Ada Lovelace (ada@example.test)'

An empty search returns nothing rather than everything — a blank box should not render every product on the site.

Labels are built to be pickable. A post with no title gets #13 rather than an empty option nobody can select, and a user carries their email, because two customers called "John Smith" are otherwise indistinguishable and picking the wrong one attaches an order to a stranger.

Shorthands

Posts::id( 'hello-world' );      // int|null
Posts::exists( 'hello-world' );  // bool

Cost

An id costs no query. A slug costs one. A title costs one when its slug still matches — which is the usual case, because sanitize_title() is applied on the way in — and two when the post has been renamed since.

Requirements

  • PHP 8.3+
  • WordPress

License

GPL-2.0-or-later.