arraypress / wp-object-utils
Find a post, term or user by whatever you were handed: an id, a slug, a name, an email or the object itself.
Requires
- php: >=8.3
Requires (Dev)
- phpcompatibility/phpcompatibility-wp: ^2.1
- phpunit/phpunit: ^12.0
- squizlabs/php_codesniffer: ^3.13.5
- wp-coding-standards/wpcs: ^3.4
Suggests
None
Provides
None
Conflicts
None
Replaces
None
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.