{"id":29651290,"url":"https://github.com/arraypress/wp-post-utils","last_synced_at":"2025-10-12T09:37:59.410Z","repository":{"id":304351836,"uuid":"1012446057","full_name":"arraypress/wp-post-utils","owner":"arraypress","description":"A lean WordPress library for working with posts and post operations.","archived":false,"fork":false,"pushed_at":"2025-07-13T14:19:13.000Z","size":15,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-09-02T21:19:33.048Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/arraypress.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2025-07-02T10:47:41.000Z","updated_at":"2025-07-13T14:19:16.000Z","dependencies_parsed_at":"2025-07-12T15:31:50.944Z","dependency_job_id":"28ae03d9-5045-4147-91eb-24614fb83780","html_url":"https://github.com/arraypress/wp-post-utils","commit_stats":null,"previous_names":["arraypress/wp-post-utils"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/arraypress/wp-post-utils","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/arraypress%2Fwp-post-utils","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/arraypress%2Fwp-post-utils/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/arraypress%2Fwp-post-utils/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/arraypress%2Fwp-post-utils/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/arraypress","download_url":"https://codeload.github.com/arraypress/wp-post-utils/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/arraypress%2Fwp-post-utils/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":279010938,"owners_count":26084837,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-12T02:00:06.719Z","response_time":53,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-07-22T05:06:31.502Z","updated_at":"2025-10-12T09:37:59.394Z","avatar_url":"https://github.com/arraypress.png","language":"PHP","readme":"# WordPress Post Utilities\n\nA lightweight WordPress library for working with posts and post operations. Provides clean APIs for post retrieval, content management, meta operations, and bulk actions with value/label formatting perfect for forms and admin interfaces.\n\n## Features\n\n* 🎯 **Clean API**: WordPress-style snake_case methods with consistent interfaces\n* 🔍 **Built-in Search**: Post search with value/label formatting for forms\n* 📋 **Form-Ready Options**: Perfect value/label arrays for selects and admin interfaces\n* 🔗 **Term Management**: Easy term assignment and relationship management\n* 📊 **Meta Operations**: Simple post meta handling with type safety\n* 🎨 **Flexible Identifiers**: Use IDs, slugs, titles, or objects interchangeably\n* ⚡ **Bulk Operations**: Efficient bulk status changes, deletions, and updates\n* ➕ **Post Creation**: Create single or multiple posts with flexible options\n\n## Requirements\n\n* PHP 7.4 or later\n* WordPress 5.0 or later\n\n## Installation\n\n```bash\ncomposer require arraypress/wp-post-utils\n```\n\n## Basic Usage\n\n### Working with Single Posts\n\n```php\nuse ArrayPress\\PostUtils\\Post;\n\n// Get post by ID\n$post = Post::get( 123 );\n\n// Get post by identifier (ID, slug, or title)\n$post = Post::get_by_identifier( 'hello-world' );\n$post = Post::get_by_slug( 'hello-world' );\n$post = Post::get_by_title( 'Hello World' );\n\n// Check if post exists\nif ( Post::exists( 123 ) ) {\n\t// Post exists\n}\n\n// Create a new post\n$post = Post::create( 'My New Post', 'This is the content' );\n\n// Create post with additional data\n$post = Post::create( 'My New Post', 'Content here', [\n\t'post_type'   =\u003e 'page',\n\t'post_status' =\u003e 'draft',\n\t'post_author' =\u003e 5,\n\t'meta_input'  =\u003e [\n\t\t'custom_field' =\u003e 'value'\n\t]\n] );\n\n// Create post only if it doesn't exist\n$post = Post::create_if_not_exists( 'Unique Title', 'Content' );\n\n// Get post content and meta\n$title   = Post::get_title( 123 );\n$content = Post::get_content( 123 );\n$excerpt = Post::get_excerpt( 123 );\n$url     = Post::get_url( 123 );\n$slug    = Post::get_slug( 123 );\n$type    = Post::get_type( 123 );\n\n// Get author information\n$author_id = Post::get_author_id( 123 );\n$author    = Post::get_author( 123 ); // Returns WP_User object\n\n// Get post meta\n$meta_value        = Post::get_meta( 123, 'custom_field' );\n$meta_with_default = Post::get_meta_with_default( 123, 'priority', 'normal' );\n\n// Check post status and conditions\nif ( Post::is_published( 123 ) ) {\n\t// Post is published\n}\n\nif ( Post::is_draft( 123 ) ) {\n\t// Post is draft\n}\n\nif ( Post::is_scheduled( 123 ) ) {\n\t// Post is scheduled\n}\n\nif ( Post::is_type( 123, 'page' ) ) {\n\t// Post is a page\n}\n\nif ( Post::is_type( 123, [ 'post', 'custom_post' ] ) ) {\n\t// Post is either a post or custom_post\n}\n\nif ( Post::has_thumbnail( 123 ) ) {\n\t$thumbnail = Post::get_thumbnail_url( 123 );\n}\n\nif ( Post::has_content( 123 ) ) {\n\t// Post has content\n}\n\nif ( Post::has_excerpt( 123 ) ) {\n\t// Post has manual excerpt\n}\n\nif ( Post::is_sticky( 123 ) ) {\n\t// Post is sticky\n}\n\nif ( Post::allows_comments( 123 ) ) {\n\t$comment_count = Post::get_comment_count( 123 );\n}\n\n// Content analysis\n$words        = Post::count_words( 123 );\n$reading_time = Post::get_reading_time( 123 ); // minutes\n$age_days     = Post::get_age( 123 );\n\n// Post management\nPost::update_status( 123, 'draft' );\nPost::trash( 123 );\nPost::delete( 123, true ); // force delete\n\n// Dates\n$date     = Post::get_date( 123, 'Y-m-d' );\n$modified = Post::get_modified_date( 123 );\n```\n\n### Working with Multiple Posts\n\n```php\n\u003c?php\n\nuse ArrayPress\\PostUtils\\Posts;\n\n// Get multiple posts\n$posts = Posts::get( [ 1, 2, 3 ] );\n$posts = Posts::get( [ 1, 2, 3 ], 'page' ); // Specific post type\n\n// Create multiple posts\n$result = Posts::create( [\n\t[\n\t\t'post_title'   =\u003e 'First Post',\n\t\t'post_content' =\u003e 'Content for first post',\n\t\t'post_type'    =\u003e 'post'\n\t],\n\t[\n\t\t'post_title'   =\u003e 'Second Post',\n\t\t'post_content' =\u003e 'Content for second post',\n\t\t'post_type'    =\u003e 'page'\n\t]\n] );\n\n// Create posts with shared defaults\n$result = Posts::create( [\n\t[\n\t\t'post_title'   =\u003e 'Post One',\n\t\t'post_content' =\u003e 'Content one'\n\t],\n\t[\n\t\t'post_title'   =\u003e 'Post Two',\n\t\t'post_content' =\u003e 'Content two'\n\t]\n], [\n\t'post_type'   =\u003e 'custom_post',\n\t'post_status' =\u003e 'draft',\n\t'post_author' =\u003e 5\n] );\n\n// Create posts only if they don't exist\n$result = Posts::create_if_not_exists( [\n\t[\n\t\t'post_title' =\u003e 'About Us',\n\t\t'post_type'  =\u003e 'page'\n\t],\n\t[\n\t\t'post_title' =\u003e 'Contact',\n\t\t'post_type'  =\u003e 'page'\n\t]\n] );\n\n// Check results\nif ( ! empty( $result['created'] ) ) {\n\tforeach ( $result['created'] as $post ) {\n\t\techo \"Created post: \" . $post-\u003epost_title . \"\\n\";\n\t}\n}\n\nif ( ! empty( $result['existing'] ) ) {\n\tforeach ( $result['existing'] as $post ) {\n\t\techo \"Post already exists: \" . $post-\u003epost_title . \"\\n\";\n\t}\n}\n\nif ( ! empty( $result['errors'] ) ) {\n\tforeach ( $result['errors'] as $error ) {\n\t\techo \"Error: \" . $error . \"\\n\";\n\t}\n}\n\n// Get posts by identifiers\n$post_ids     = Posts::get_by_identifiers( [ 'hello-world', 'about-us' ] );\n$post_objects = Posts::get_by_identifiers( [ 'hello-world', 'about-us' ], 'any', true );\n\n// Search posts and get options\n$options = Posts::search_options( 'technology' );\n// Returns: [['value' =\u003e 1, 'label' =\u003e 'Tech Article'], ...]\n\n// Get all posts as options\n$all_options = Posts::get_options( 'post' );\n// Returns: [1 =\u003e 'Hello World', 2 =\u003e 'About Us', ...]\n\n// Get posts by author or date range\n$author_posts = Posts::get_by_author( 5 );\n$recent_posts = Posts::get_recent( 10 );\n$range_posts  = Posts::get_by_date_range( '2024-01-01', '2024-12-31' );\n```\n\n### Post Creation Examples\n\n```php\n// Plugin activation - create default pages\nfunction create_default_pages() {\n\t$default_pages = [\n\t\t[\n\t\t\t'post_title'   =\u003e 'Privacy Policy',\n\t\t\t'post_content' =\u003e 'Your privacy policy content here.',\n\t\t\t'post_type'    =\u003e 'page',\n\t\t\t'post_status'  =\u003e 'publish'\n\t\t],\n\t\t[\n\t\t\t'post_title'   =\u003e 'Terms of Service',\n\t\t\t'post_content' =\u003e 'Your terms of service content here.',\n\t\t\t'post_type'    =\u003e 'page',\n\t\t\t'post_status'  =\u003e 'publish'\n\t\t]\n\t];\n\n\t$result = Posts::create_if_not_exists( $default_pages );\n\n\t// Log creation results\n\terror_log( sprintf( 'Created %d pages, %d already existed',\n\t\tcount( $result['created'] ),\n\t\tcount( $result['existing'] )\n\t) );\n}\n\n// Import posts from external data\nfunction import_posts_from_data( $posts_data ) {\n\t$posts = [];\n\n\tforeach ( $posts_data as $data ) {\n\t\t$posts[] = [\n\t\t\t'post_title'   =\u003e $data['title'],\n\t\t\t'post_content' =\u003e $data['content'],\n\t\t\t'post_type'    =\u003e $data['type'] ?? 'post',\n\t\t\t'post_status'  =\u003e $data['status'] ?? 'publish',\n\t\t\t'meta_input'   =\u003e $data['meta'] ?? []\n\t\t];\n\t}\n\n\treturn Posts::create( $posts, [\n\t\t'post_author' =\u003e get_current_user_id()\n\t] );\n}\n\n// Create test content for development\nfunction create_test_content() {\n\t$test_posts = [\n\t\t'Sample Blog Post' =\u003e 'This is a sample blog post for testing.',\n\t\t'About Page'       =\u003e 'This is the about page content.',\n\t\t'Contact Page'     =\u003e 'Contact us at info@example.com'\n\t];\n\n\t$posts_data = [];\n\tforeach ( $test_posts as $title =\u003e $content ) {\n\t\t$posts_data[] = [\n\t\t\t'post_title'   =\u003e $title,\n\t\t\t'post_content' =\u003e $content,\n\t\t\t'post_type'    =\u003e str_contains( $title, 'Page' ) ? 'page' : 'post'\n\t\t];\n\t}\n\n\treturn Posts::create_if_not_exists( $posts_data );\n}\n```\n\n### Meta Operations\n\n```php\n// Get meta with default\n$value = Post::get_meta_with_default( 123, 'priority', 'normal' );\n\n// Update meta only if changed\nPost::update_meta_if_changed( 123, 'status', 'updated' );\n\n// Delete meta\nPost::delete_meta( 123, 'old_field' );\n```\n\n### Bulk Operations\n\n```php\n// Change status for multiple posts\n$results = Posts::change_status( [ 1, 2, 3 ], 'draft' );\n\n// Trash multiple posts\n$results = Posts::trash( [ 1, 2, 3 ] );\n\n// Delete permanently\n$results = Posts::delete( [ 1, 2, 3 ], true );\n\n// Change author\n$results = Posts::change_author( [ 1, 2, 3 ], 5 );\n\n// Check results\nforeach ( $results as $post_id =\u003e $success ) {\n\tif ( $success ) {\n\t\techo \"Successfully updated post {$post_id}\\n\";\n\t} else {\n\t\techo \"Failed to update post {$post_id}\\n\";\n\t}\n}\n```\n\n### Hierarchy Operations\n\n```php\n// Parent-child relationships\n$parent_id = Post::get_parent_id( 123 );\n$children  = Post::get_children( 123 );\n\nif ( Post::is_child_of( 123, 456 ) ) {\n\t// Post 123 is child of 456\n}\n\n// Get children with custom args\n$child_pages = Post::get_children( 123, [\n\t'post_status' =\u003e [ 'publish', 'private' ],\n\t'orderby'     =\u003e 'title',\n\t'order'       =\u003e 'ASC'\n] );\n```\n\n### Search Functionality\n\n```php\n// Basic search\n$results = Posts::search( 'wordpress' );\n\n// Search with specific post types\n$results = Posts::search( 'wordpress', [ 'post', 'page' ] );\n\n// Search with additional arguments\n$results = Posts::search( 'technology', [ 'post' ], [\n\t'posts_per_page' =\u003e 10,\n\t'meta_key'       =\u003e 'featured',\n\t'meta_value'     =\u003e '1'\n] );\n\n// Get search results as options for forms\n$options = Posts::search_options( 'admin' );\n```\n\n## API Reference\n\n### Post Class (Single Posts)\n\n**Core Retrieval:**\n- `get( int $post_id ): ?WP_Post`\n- `get_by_identifier( $identifier, $post_type = 'any', $post_status = 'publish' ): ?WP_Post`\n- `get_by_slug( string $slug, $post_type = 'post' ): ?WP_Post`\n- `get_by_title( string $title, $post_type = 'post' ): ?WP_Post`\n- `get_by_meta( string $meta_key, $meta_value, $post_type = 'post' ): ?WP_Post`\n- `exists( int $post_id ): bool`\n\n**Creation:**\n- `create( string $title, string $content = '', array $args = [] ): ?WP_Post`\n- `create_if_not_exists( string $title, string $content = '', array $args = [] ): ?WP_Post`\n\n**Content \u0026 Basic Info:**\n- `get_title( int $post_id, bool $raw = false ): string`\n- `get_content( int $post_id, bool $raw = false ): string`\n- `get_excerpt( int $post_id, bool $raw = false ): string`\n- `get_url( int $post_id ): string|false`\n- `get_slug( int $post_id ): ?string`\n- `get_type( int $post_id ): string|false`\n- `get_field( int $post_id, string $field )`\n- `get_thumbnail_url( int $post_id ): string|false`\n\n**Author:**\n- `get_author_id( int $post_id ): ?int`\n- `get_author( int $post_id ): ?WP_User`\n\n**Content Analysis:**\n- `count_words( int $post_id ): int`\n- `get_reading_time( int $post_id, int $words_per_minute = 200 ): int`\n\n**Meta Operations:**\n- `get_meta( int $post_id, string $key, bool $single = true )`\n- `get_meta_with_default( int $post_id, string $meta_key, $default )`\n- `update_meta_if_changed( int $post_id, string $meta_key, $meta_value ): bool`\n- `delete_meta( int $post_id, string $meta_key ): bool`\n\n**Status \u0026 Conditionals:**\n- `get_status( int $post_id ): string|false`\n- `is_published( int $post_id ): bool`\n- `is_draft( int $post_id ): bool`\n- `is_private( int $post_id ): bool`\n- `is_scheduled( int $post_id ): bool`\n- `is_sticky( int $post_id ): bool`\n- `is_type( int $post_id, string|array $post_type ): bool`\n- `has_content( int $post_id ): bool`\n- `has_excerpt( int $post_id ): bool`\n- `has_password( int $post_id ): bool`\n- `has_thumbnail( int $post_id ): bool`\n- `allows_comments( int $post_id ): bool`\n\n**Dates \u0026 Analysis:**\n- `get_date( int $post_id, string $format = 'Y-m-d H:i:s' ): string|false`\n- `get_modified_date( int $post_id, string $format = 'Y-m-d H:i:s' ): string|false`\n- `get_age( int $post_id, bool $use_modified_date = false ): int|false`\n- `get_comment_count( int $post_id ): int`\n\n**Hierarchy:**\n- `get_parent_id( int $post_id ): int`\n- `is_child_of( int $post_id, int $parent_id ): bool`\n- `get_children( int $post_id, array $args = [] ): array`\n\n**Actions:**\n- `update_status( int $post_id, string $new_status ): bool`\n- `trash( int $post_id ): bool`\n- `delete( int $post_id, bool $force = false ): bool`\n\n### Posts Class (Multiple Posts)\n\n**Core Retrieval:**\n- `get( array $post_ids, $post_type = 'any', array $args = [] ): array`\n- `get_by_identifiers( array $identifiers, $post_type = 'any', bool $return_objects = false ): array`\n- `get_by_author( int $author_id, array $args = [] ): array`\n- `get_recent( int $number = 5, array $args = [] ): array`\n- `get_by_date_range( string $start_date, string $end_date, array $args = [] ): array`\n\n**Creation:**\n- `create( array $posts, array $defaults = [] ): array`\n- `create_if_not_exists( array $posts, array $defaults = [] ): array`\n\n**Search \u0026 Options:**\n- `search( string $search, array $post_types = ['post'], array $args = [] ): array`\n- `search_options( string $search, array $post_types = ['post'], array $args = [] ): array`\n- `get_options( $post_type = 'post', array $args = [] ): array`\n\n**Bulk Actions:**\n- `change_status( array $post_ids, string $new_status ): array`\n- `change_author( array $post_ids, int $new_author ): array`\n- `trash( array $post_ids ): array`\n- `delete( array $post_ids, bool $force = false ): array`\n\n## Key Features\n\n- **Value/Label Format**: Perfect for forms and selects\n- **Search Functionality**: Built-in post search with formatting\n- **Post Creation**: Create single or multiple posts with validation\n- **Bulk Operations**: Efficient multi-post management\n- **Meta Handling**: Simple and safe meta operations\n- **Flexible Identifiers**: Use IDs, slugs, titles, or objects interchangeably\n- **Content Analysis**: Word counts, reading time, age calculations\n- **Hierarchy Support**: Parent-child relationship management\n- **Type Checking**: Flexible post type validation with single or multiple types\n\n## Requirements\n\n- PHP 7.4+\n- WordPress 5.0+\n\n## Contributing\n\nContributions are welcome! Please feel free to submit a Pull Request.\n\n## License\n\nThis project is licensed under the GPL-2.0-or-later License.\n\n## Support\n\n- [Documentation](https://github.com/arraypress/wp-post-utils)\n- [Issue Tracker](https://github.com/arraypress/wp-post-utils/issues)","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Farraypress%2Fwp-post-utils","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Farraypress%2Fwp-post-utils","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Farraypress%2Fwp-post-utils/lists"}