wordpress-advanced-architecture

安装量: 129
排名: #6689

安装

npx skills add https://github.com/bobmatnyc/claude-mpm-skills --skill wordpress-advanced-architecture

Advanced WordPress Architecture

Master advanced WordPress development patterns including REST API endpoints, WP-CLI commands, performance optimization, and caching strategies for scalable WordPress applications.

  1. REST API Development

The WordPress REST API provides a powerful interface for creating custom endpoints with proper authentication, validation, and response formatting.

Endpoint Registration with Namespacing add_action( 'rest_api_init', 'register_custom_rest_routes' ); function register_custom_rest_routes() { // Namespace: myplugin/v1 (enables versioning) $namespace = 'myplugin/v1';

// GET /wp-json/myplugin/v1/books
register_rest_route( $namespace, '/books', [
    'methods'  => 'GET',
    'callback' => 'get_books_callback',
    'permission_callback' => '__return_true', // Public endpoint
    'args' => [
        'per_page' => [
            'default' => 10,
            'validate_callback' => function( $param ) {
                return is_numeric( $param ) && $param > 0 && $param <= 100;
            },
            'sanitize_callback' => 'absint',
        ],
        'page' => [
            'default' => 1,
            'validate_callback' => function( $param ) {
                return is_numeric( $param ) && $param > 0;
            },
            'sanitize_callback' => 'absint',
        ],
    ],
]);

// GET /wp-json/myplugin/v1/books/(?P<id>\d+)
register_rest_route( $namespace, '/books/(?P<id>\d+)', [
    'methods'  => 'GET',
    'callback' => 'get_book_callback',
    'permission_callback' => '__return_true',
    'args' => [
        'id' => [
            'validate_callback' => function( $param ) {
                return is_numeric( $param );
            },
            'sanitize_callback' => 'absint',
        ],
    ],
]);

// POST /wp-json/myplugin/v1/books (authenticated)
register_rest_route( $namespace, '/books', [
    'methods'  => 'POST',
    'callback' => 'create_book_callback',
    'permission_callback' => function() {
        return current_user_can( 'edit_posts' );
    },
    'args' => [
        'title' => [
            'required' => true,
            'type' => 'string',
            'validate_callback' => function( $param ) {
                return is_string( $param ) && strlen( $param ) > 0;
            },
            'sanitize_callback' => 'sanitize_text_field',
        ],
        'content' => [
            'required' => false,
            'type' => 'string',
            'sanitize_callback' => 'wp_kses_post',
        ],
        'status' => [
            'default' => 'draft',
            'enum' => [ 'draft', 'publish', 'private' ],
        ],
    ],
]);

// PUT /wp-json/myplugin/v1/books/(?P<id>\d+)
register_rest_route( $namespace, '/books/(?P<id>\d+)', [
    'methods'  => 'PUT',
    'callback' => 'update_book_callback',
    'permission_callback' => function( $request ) {
        $book_id = $request->get_param( 'id' );
        return current_user_can( 'edit_post', $book_id );
    },
    'args' => [
        'id' => [
            'validate_callback' => function( $param ) {
                return is_numeric( $param );
            },
            'sanitize_callback' => 'absint',
        ],
        'title' => [
            'type' => 'string',
            'sanitize_callback' => 'sanitize_text_field',
        ],
        'content' => [
            'type' => 'string',
            'sanitize_callback' => 'wp_kses_post',
        ],
    ],
]);

// DELETE /wp-json/myplugin/v1/books/(?P<id>\d+)
register_rest_route( $namespace, '/books/(?P<id>\d+)', [
    'methods'  => 'DELETE',
    'callback' => 'delete_book_callback',
    'permission_callback' => function( $request ) {
        $book_id = $request->get_param( 'id' );
        return current_user_can( 'delete_post', $book_id );
    },
    'args' => [
        'id' => [
            'validate_callback' => function( $param ) {
                return is_numeric( $param );
            },
            'sanitize_callback' => 'absint',
        ],
    ],
]);

}

Complete CRUD Implementation // GET /wp-json/myplugin/v1/books function get_books_callback( $request ) { $per_page = $request->get_param( 'per_page' ); $page = $request->get_param( 'page' ); $offset = ( $page - 1 ) * $per_page;

$args = [
    'post_type' => 'book',
    'posts_per_page' => $per_page,
    'offset' => $offset,
    'post_status' => 'publish',
];

$query = new WP_Query( $args );

if ( ! $query->have_posts() ) {
    return rest_ensure_response([
        'books' => [],
        'total' => 0,
        'page' => $page,
        'per_page' => $per_page,
    ]);
}

$books = [];
while ( $query->have_posts() ) {
    $query->the_post();
    $books[] = [
        'id' => get_the_ID(),
        'title' => get_the_title(),
        'content' => get_the_content(),
        'author' => get_the_author(),
        'date' => get_the_date( 'c' ), // ISO 8601 format
        'link' => get_permalink(),
    ];
}
wp_reset_postdata();

$response = rest_ensure_response([
    'books' => $books,
    'total' => $query->found_posts,
    'page' => $page,
    'per_page' => $per_page,
    'total_pages' => ceil( $query->found_posts / $per_page ),
]);

// Add HATEOAS links
$response->add_link( 'self', rest_url( "myplugin/v1/books?page={$page}&per_page={$per_page}" ) );

if ( $page > 1 ) {
    $prev_page = $page - 1;
    $response->add_link( 'prev', rest_url( "myplugin/v1/books?page={$prev_page}&per_page={$per_page}" ) );
}

if ( $page < ceil( $query->found_posts / $per_page ) ) {
    $next_page = $page + 1;
    $response->add_link( 'next', rest_url( "myplugin/v1/books?page={$next_page}&per_page={$per_page}" ) );
}

return $response;

}

// GET /wp-json/myplugin/v1/books/123 function get_book_callback( $request ) { $book_id = $request->get_param( 'id' ); $book = get_post( $book_id );

if ( ! $book || 'book' !== $book->post_type ) {
    return new WP_Error(
        'book_not_found',
        'Book not found',
        [ 'status' => 404 ]
    );
}

$data = [
    'id' => $book->ID,
    'title' => $book->post_title,
    'content' => apply_filters( 'the_content', $book->post_content ),
    'excerpt' => $book->post_excerpt,
    'author' => get_the_author_meta( 'display_name', $book->post_author ),
    'date' => get_the_date( 'c', $book ),
    'modified' => get_the_modified_date( 'c', $book ),
    'status' => $book->post_status,
    'link' => get_permalink( $book ),
    'featured_image' => get_the_post_thumbnail_url( $book, 'large' ),
    'meta' => [
        'isbn' => get_post_meta( $book->ID, '_isbn', true ),
        'pages' => (int) get_post_meta( $book->ID, '_pages', true ),
    ],
];

return rest_ensure_response( $data );

}

// POST /wp-json/myplugin/v1/books function create_book_callback( $request ) { $title = $request->get_param( 'title' ); $content = $request->get_param( 'content' ); $status = $request->get_param( 'status' );

$post_data = [
    'post_type' => 'book',
    'post_title' => $title,
    'post_content' => $content,
    'post_status' => $status,
    'post_author' => get_current_user_id(),
];

$book_id = wp_insert_post( $post_data, true );

if ( is_wp_error( $book_id ) ) {
    return new WP_Error(
        'book_creation_failed',
        $book_id->get_error_message(),
        [ 'status' => 500 ]
    );
}

// Add custom metadata if provided
if ( $request->has_param( 'isbn' ) ) {
    update_post_meta( $book_id, '_isbn', sanitize_text_field( $request->get_param( 'isbn' ) ) );
}

if ( $request->has_param( 'pages' ) ) {
    update_post_meta( $book_id, '_pages', absint( $request->get_param( 'pages' ) ) );
}

$response = rest_ensure_response([
    'id' => $book_id,
    'title' => $title,
    'message' => 'Book created successfully',
    'link' => get_permalink( $book_id ),
]);

$response->set_status( 201 ); // Created
$response->header( 'Location', rest_url( "myplugin/v1/books/{$book_id}" ) );

return $response;

}

// PUT /wp-json/myplugin/v1/books/123 function update_book_callback( $request ) { $book_id = $request->get_param( 'id' ); $book = get_post( $book_id );

if ( ! $book || 'book' !== $book->post_type ) {
    return new WP_Error(
        'book_not_found',
        'Book not found',
        [ 'status' => 404 ]
    );
}

$post_data = [ 'ID' => $book_id ];

if ( $request->has_param( 'title' ) ) {
    $post_data['post_title'] = $request->get_param( 'title' );
}

if ( $request->has_param( 'content' ) ) {
    $post_data['post_content'] = $request->get_param( 'content' );
}

if ( $request->has_param( 'status' ) ) {
    $post_data['post_status'] = $request->get_param( 'status' );
}

$result = wp_update_post( $post_data, true );

if ( is_wp_error( $result ) ) {
    return new WP_Error(
        'book_update_failed',
        $result->get_error_message(),
        [ 'status' => 500 ]
    );
}

return rest_ensure_response([
    'id' => $book_id,
    'message' => 'Book updated successfully',
    'link' => get_permalink( $book_id ),
]);

}

// DELETE /wp-json/myplugin/v1/books/123 function delete_book_callback( $request ) { $book_id = $request->get_param( 'id' ); $book = get_post( $book_id );

if ( ! $book || 'book' !== $book->post_type ) {
    return new WP_Error(
        'book_not_found',
        'Book not found',
        [ 'status' => 404 ]
    );
}

// Soft delete (trash) or hard delete
$force = $request->get_param( 'force' );
$result = wp_delete_post( $book_id, $force );

if ( ! $result ) {
    return new WP_Error(
        'book_deletion_failed',
        'Failed to delete book',
        [ 'status' => 500 ]
    );
}

return rest_ensure_response([
    'deleted' => true,
    'id' => $book_id,
    'message' => $force ? 'Book permanently deleted' : 'Book moved to trash',
]);

}

Controller Pattern for Complex Endpoints

For complex REST endpoints, use a controller class to organize logic:

namespace, '/' . $this->rest_base, [ [ 'methods' => \WP_REST_Server::READABLE, 'callback' => [ $this, 'get_items' ], 'permission_callback' => [ $this, 'get_items_permissions_check' ], 'args' => $this->get_collection_params(), ], [ 'methods' => \WP_REST_Server::CREATABLE, 'callback' => [ $this, 'create_item' ], 'permission_callback' => [ $this, 'create_item_permissions_check' ], 'args' => $this->get_endpoint_args_for_item_schema( \WP_REST_Server::CREATABLE ), ], 'schema' => [ $this, 'get_public_item_schema' ], ]); register_rest_route( $this->namespace, '/' . $this->rest_base . '/(?P[\d]+)', [ 'args' => [ 'id' => [ 'description' => 'Unique identifier for the book', 'type' => 'integer', ], ], [ 'methods' => \WP_REST_Server::READABLE, 'callback' => [ $this, 'get_item' ], 'permission_callback' => [ $this, 'get_item_permissions_check' ], 'args' => [ 'context' => $this->get_context_param( [ 'default' => 'view' ] ), ], ], [ 'methods' => \WP_REST_Server::EDITABLE, 'callback' => [ $this, 'update_item' ], 'permission_callback' => [ $this, 'update_item_permissions_check' ], 'args' => $this->get_endpoint_args_for_item_schema( \WP_REST_Server::EDITABLE ), ], [ 'methods' => \WP_REST_Server::DELETABLE, 'callback' => [ $this, 'delete_item' ], 'permission_callback' => [ $this, 'delete_item_permissions_check' ], 'args' => [ 'force' => [ 'type' => 'boolean', 'default' => false, 'description' => 'Whether to bypass trash and force deletion', ], ], ], 'schema' => [ $this, 'get_public_item_schema' ], ]); } public function get_items( $request ) { // Implementation similar to get_books_callback above } public function get_item( $request ) { // Implementation similar to get_book_callback above } public function create_item( $request ) { // Implementation similar to create_book_callback above } public function update_item( $request ) { // Implementation similar to update_book_callback above } public function delete_item( $request ) { // Implementation similar to delete_book_callback above } public function get_items_permissions_check( $request ) { return true; // Public endpoint } public function get_item_permissions_check( $request ) { return true; // Public endpoint } public function create_item_permissions_check( $request ) { return current_user_can( 'edit_posts' ); } public function update_item_permissions_check( $request ) { $book_id = $request->get_param( 'id' ); return current_user_can( 'edit_post', $book_id ); } public function delete_item_permissions_check( $request ) { $book_id = $request->get_param( 'id' ); return current_user_can( 'delete_post', $book_id ); } public function get_public_item_schema() { if ( $this->schema ) { return $this->add_additional_fields_schema( $this->schema ); } $schema = [ '$schema' => 'http://json-schema.org/draft-04/schema#', 'title' => 'book', 'type' => 'object', 'properties' => [ 'id' => [ 'description' => 'Unique identifier for the book', 'type' => 'integer', 'context' => [ 'view', 'edit', 'embed' ], 'readonly' => true, ], 'title' => [ 'description' => 'The book title', 'type' => 'string', 'context' => [ 'view', 'edit', 'embed' ], 'required' => true, ], 'content' => [ 'description' => 'The book content', 'type' => 'string', 'context' => [ 'view', 'edit' ], ], 'status' => [ 'description' => 'The book status', 'type' => 'string', 'enum' => [ 'draft', 'publish', 'private' ], 'context' => [ 'view', 'edit' ], ], ], ]; $this->schema = $schema; return $this->add_additional_fields_schema( $this->schema ); } } // Register controller add_action( 'rest_api_init', function() { $controller = new \MyPlugin\API\Books_Controller(); $controller->register_routes(); }); REST API Authentication // Application Passwords (WordPress 5.6+) // Users generate passwords at /wp-admin/profile.php // Example cURL request with authentication: // curl -X POST https://example.com/wp-json/myplugin/v1/books \ // -u username:application_password \ // -H "Content-Type: application/json" \ // -d '{"title":"New Book","content":"Book content"}' // Cookie authentication for logged-in users (requires nonce) add_action( 'rest_api_init', function() { // Add nonce to wp_localize_script wp_localize_script( 'my-ajax-script', 'wpApiSettings', [ 'root' => esc_url_raw( rest_url() ), 'nonce' => wp_create_nonce( 'wp_rest' ), ]); }); // JavaScript REST API call with nonce: /* fetch( wpApiSettings.root + 'myplugin/v1/books', { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-WP-Nonce': wpApiSettings.nonce }, body: JSON.stringify({ title: 'New Book', content: 'Book content' }) }).then( response => response.json() ) .then( data => console.log( data ) ); */ 2. WP-CLI Commands WP-CLI enables automation of WordPress tasks through custom commands. Custom Command Registration ] * : Render output in a particular format. * --- * default: table * options: * - table * - csv * - json * - yaml * --- * * [--status=] * : Filter by post status. * * ## EXAMPLES * * wp books list * wp books list --format=json * wp books list --status=publish * * @when after_wp_load */ public function list( $args, $assoc_args ) { $defaults = [ 'format' => 'table', 'status' => 'any', ]; $assoc_args = wp_parse_args( $assoc_args, $defaults ); $query_args = [ 'post_type' => 'book', 'posts_per_page' => -1, 'post_status' => $assoc_args['status'], ]; $books = get_posts( $query_args ); if ( empty( $books ) ) { \WP_CLI::warning( 'No books found.' ); return; } $items = []; foreach ( $books as $book ) { $items[] = [ 'ID' => $book->ID, 'Title' => $book->post_title, 'Status' => $book->post_status, 'Author' => get_the_author_meta( 'display_name', $book->post_author ), 'Date' => get_the_date( 'Y-m-d H:i:s', $book ), ]; } \WP_CLI\Utils\format_items( $assoc_args['format'], $items, [ 'ID', 'Title', 'Status', 'Author', 'Date' ] ); \WP_CLI::success( sprintf( 'Found %d books.', count( $items ) ) ); } /** * Create a new book. * * ## OPTIONS * * <title> * : The book title. * * [--content=] * : The book content. * * [--status=] * : The book status. * --- * default: draft * options: * - draft * - publish * - private * --- * * [--isbn=] * : The book ISBN. * * [--pages=] * : Number of pages. * * ## EXAMPLES * * wp books create "My Book Title" * wp books create "My Book" --status=publish --isbn=978-3-16-148410-0 --pages=350 * * @when after_wp_load */ public function create( $args, $assoc_args ) { $title = $args[0]; $defaults = [ 'content' => '', 'status' => 'draft', ]; $assoc_args = wp_parse_args( $assoc_args, $defaults ); $post_data = [ 'post_type' => 'book', 'post_title' => $title, 'post_content' => $assoc_args['content'], 'post_status' => $assoc_args['status'], 'post_author' => get_current_user_id(), ]; $book_id = wp_insert_post( $post_data, true ); if ( is_wp_error( $book_id ) ) { \WP_CLI::error( 'Failed to create book: ' . $book_id->get_error_message() ); } // Add custom metadata if ( isset( $assoc_args['isbn'] ) ) { update_post_meta( $book_id, '_isbn', sanitize_text_field( $assoc_args['isbn'] ) ); } if ( isset( $assoc_args['pages'] ) ) { update_post_meta( $book_id, '_pages', absint( $assoc_args['pages'] ) ); } \WP_CLI::success( sprintf( 'Created book #%d: %s', $book_id, $title ) ); } /** * Import books from CSV file. * * ## OPTIONS * * * : Path to CSV file. * * [--dry-run] * : Preview import without creating books. * * ## EXAMPLES * * wp books import books.csv * wp books import books.csv --dry-run * * @when after_wp_load */ public function import( $args, $assoc_args ) { $file = $args[0]; $dry_run = isset( $assoc_args['dry-run'] ); if ( ! file_exists( $file ) ) { \WP_CLI::error( 'File not found: ' . $file ); } $csv = array_map( 'str_getcsv', file( $file ) ); $header = array_shift( $csv ); $total = count( $csv ); $created = 0; \WP_CLI::log( sprintf( 'Processing %d books...', $total ) ); $progress = \WP_CLI\Utils\make_progress_bar( 'Importing books', $total ); foreach ( $csv as $row ) { $book = array_combine( $header, $row ); if ( $dry_run ) { \WP_CLI::log( sprintf( 'Would create: %s', $book['title'] ) ); } else { $post_data = [ 'post_type' => 'book', 'post_title' => $book['title'], 'post_content' => $book['content'] ?? '', 'post_status' => $book['status'] ?? 'draft', ]; $book_id = wp_insert_post( $post_data, true ); if ( ! is_wp_error( $book_id ) ) { if ( isset( $book['isbn'] ) ) { update_post_meta( $book_id, '_isbn', $book['isbn'] ); } $created++; } } $progress->tick(); } $progress->finish(); if ( $dry_run ) { \WP_CLI::success( sprintf( 'Dry run complete. Would create %d books.', $total ) ); } else { \WP_CLI::success( sprintf( 'Imported %d of %d books.', $created, $total ) ); } } /** * Generate sample books. * * ## OPTIONS * * [--count=] * : Number of books to generate. * --- * default: 10 * --- * * [--status=] * : Book status. * --- * default: publish * --- * * ## EXAMPLES * * wp books generate --count=50 * wp books generate --count=100 --status=draft * * @when after_wp_load */ public function generate( $args, $assoc_args ) { $count = isset( $assoc_args['count'] ) ? absint( $assoc_args['count'] ) : 10; $status = isset( $assoc_args['status'] ) ? $assoc_args['status'] : 'publish'; $progress = \WP_CLI\Utils\make_progress_bar( 'Generating books', $count ); for ( $i = 1; $i <= $count; $i++ ) { $post_data = [ 'post_type' => 'book', 'post_title' => sprintf( 'Sample Book %d', $i ), 'post_content' => sprintf( 'This is sample book number %d.', $i ), 'post_status' => $status, ]; $book_id = wp_insert_post( $post_data ); // Add random metadata update_post_meta( $book_id, '_isbn', sprintf( '978-3-16-%06d-0', rand( 100000, 999999 ) ) ); update_post_meta( $book_id, '_pages', rand( 100, 500 ) ); $progress->tick(); } $progress->finish(); \WP_CLI::success( sprintf( 'Generated %d books.', $count ) ); } } // Register WP-CLI command if ( defined( 'WP_CLI' ) && WP_CLI ) { \WP_CLI::add_command( 'books', 'MyPlugin\CLI\Books_Command' ); } Interactive Prompts and Confirmation /** * Delete all books (with confirmation). * * ## OPTIONS * * [--yes] * : Skip confirmation prompt. * * ## EXAMPLES * * wp books delete-all * wp books delete-all --yes * * @when after_wp_load */ public function delete_all( $args, $assoc_args ) { $books = get_posts([ 'post_type' => 'book', 'posts_per_page' => -1, 'fields' => 'ids', ]); $count = count( $books ); if ( 0 === $count ) { \WP_CLI::warning( 'No books to delete.' ); return; } // Prompt for confirmation unless --yes flag is provided \WP_CLI::confirm( sprintf( 'Are you sure you want to delete %d books?', $count ), $assoc_args ); $progress = \WP_CLI\Utils\make_progress_bar( 'Deleting books', $count ); foreach ( $books as $book_id ) { wp_delete_post( $book_id, true ); // Force delete $progress->tick(); } $progress->finish(); \WP_CLI::success( sprintf( 'Deleted %d books.', $count ) ); } Testing WP-CLI Commands # List all WP-CLI commands wp cli command-list # Get help for custom command wp help books wp help books create # Test commands wp books list wp books list --format=json wp books create "Test Book" --status=publish wp books generate --count=50 wp books import books.csv --dry-run wp books delete-all --yes 3. Performance Optimization Transients API (Expiring Cache) // Store data with expiration function get_popular_books() { $transient_key = 'popular_books'; // Try to get cached value $popular_books = get_transient( $transient_key ); if ( false === $popular_books ) { // Cache miss - fetch and store $popular_books = new WP_Query([ 'post_type' => 'book', 'posts_per_page' => 10, 'meta_key' => '_view_count', 'orderby' => 'meta_value_num', 'order' => 'DESC', ]); // Cache for 1 hour (3600 seconds) set_transient( $transient_key, $popular_books, HOUR_IN_SECONDS ); } return $popular_books; } // Invalidate cache on post update add_action( 'save_post_book', 'invalidate_books_cache' ); function invalidate_books_cache( $post_id ) { delete_transient( 'popular_books' ); } // Site-specific transients (multisite) set_site_transient( 'network_data', $data, DAY_IN_SECONDS ); $data = get_site_transient( 'network_data' ); delete_site_transient( 'network_data' ); // Clear all transients (cleanup) global $wpdb; $wpdb->query( "DELETE FROM $wpdb->options WHERE option_name LIKE '%_transient_%'" ); Object Caching (Redis, Memcached) // WordPress Object Cache functions (wp_cache_*) // Works with persistent cache (Redis/Memcached) if configured // Add to cache wp_cache_add( 'my_key', $data, 'my_group', 3600 ); // Get from cache $data = wp_cache_get( 'my_key', 'my_group' ); if ( false === $data ) { // Cache miss - fetch and cache $data = expensive_database_query(); wp_cache_set( 'my_key', $data, 'my_group', 3600 ); } // Delete from cache wp_cache_delete( 'my_key', 'my_group' ); // Flush entire cache wp_cache_flush(); // Example: Cache user data function get_user_books( $user_id ) { $cache_key = "user_{$user_id}_books"; $cache_group = 'user_books'; $books = wp_cache_get( $cache_key, $cache_group ); if ( false === $books ) { $books = get_posts([ 'post_type' => 'book', 'author' => $user_id, 'posts_per_page' => -1, ]); wp_cache_set( $cache_key, $books, $cache_group, HOUR_IN_SECONDS ); } return $books; } // Invalidate user cache on book update add_action( 'save_post_book', 'invalidate_user_books_cache', 10, 2 ); function invalidate_user_books_cache( $post_id, $post ) { wp_cache_delete( "user_{$post->post_author}_books", 'user_books' ); } Redis Configuration (object-cache.php) // Install Redis plugin or drop-in // Recommended: https://wordpress.org/plugins/redis-cache/ // Or manual configuration: // wp-content/object-cache.php '127.0.0.1', 'port' => 6379, 'auth' => '', // Password if required 'database' => 0, // Redis database number ]; // wp-config.php settings define( 'WP_REDIS_HOST', '127.0.0.1' ); define( 'WP_REDIS_PORT', 6379 ); define( 'WP_REDIS_DATABASE', 0 ); define( 'WP_REDIS_TIMEOUT', 1 ); define( 'WP_REDIS_READ_TIMEOUT', 1 ); Database Query Optimization // BAD: Multiple queries in loop $posts = get_posts([ 'post_type' => 'book', 'posts_per_page' => -1 ]); foreach ( $posts as $post ) { $author = get_user_by( 'id', $post->post_author ); // N+1 query problem $meta = get_post_meta( $post->ID, '_isbn', true ); // Another query per iteration } // GOOD: Pre-fetch data $posts = get_posts([ 'post_type' => 'book', 'posts_per_page' => -1 ]); $author_ids = wp_list_pluck( $posts, 'post_author' ); $authors = get_users([ 'include' => $author_ids ]); // Single query $authors_by_id = []; foreach ( $authors as $author ) { $authors_by_id[ $author->ID ] = $author; } // Pre-load all meta with update_meta_cache() update_post_caches( $posts, 'book' ); foreach ( $posts as $post ) { $author = $authors_by_id[ $post->post_author ]; $isbn = get_post_meta( $post->ID, '_isbn', true ); // Uses cache } // Custom queries with JOIN global $wpdb; $results = $wpdb->get_results(" SELECT p.*, pm.meta_value as isbn FROM {$wpdb->posts} p LEFT JOIN {$wpdb->postmeta} pm ON p.ID = pm.post_id AND pm.meta_key = '_isbn' WHERE p.post_type = 'book' AND p.post_status = 'publish' ORDER BY p.post_date DESC LIMIT 10 "); // Add database indexes for frequently queried meta global $wpdb; $wpdb->query(" CREATE INDEX meta_key_value ON {$wpdb->postmeta} (meta_key, meta_value(20)) "); Lazy Loading and Pagination // Paginate large queries function get_books_paginated( $page = 1, $per_page = 20 ) { $args = [ 'post_type' => 'book', 'posts_per_page' => $per_page, 'paged' => $page, ]; return new WP_Query( $args ); } // Infinite scroll with AJAX add_action( 'wp_ajax_load_more_books', 'ajax_load_more_books' ); add_action( 'wp_ajax_nopriv_load_more_books', 'ajax_load_more_books' ); function ajax_load_more_books() { check_ajax_referer( 'load_more_nonce', 'nonce' ); $page = isset( $_POST['page'] ) ? absint( $_POST['page'] ) : 1; $query = get_books_paginated( $page, 10 ); if ( $query->have_posts() ) { ob_start(); while ( $query->have_posts() ) { $query->the_post(); get_template_part( 'template-parts/content', 'book' ); } $html = ob_get_clean(); wp_send_json_success([ 'html' => $html, 'has_more' => $query->max_num_pages > $page, ]); } else { wp_send_json_error( 'No more books' ); } } // Lazy load images (native HTML) Book Cover Profiling with Query Monitor // Install Query Monitor plugin // https://wordpress.org/plugins/query-monitor/ // Add custom timing measurements do_action( 'qm/start', 'my_expensive_operation' ); // ... expensive code ... do_action( 'qm/stop', 'my_expensive_operation' ); // Log custom data do_action( 'qm/debug', 'Custom debug message' ); do_action( 'qm/info', $data_to_inspect ); // Benchmark queries Query Monitor shows: // - SQL queries (count, time, duplicates) // - HTTP requests // - Hooks and actions // - PHP errors and notices // - Template file hierarchy // - Enqueued scripts/styles 4. Caching Strategies Fragment Caching // Cache HTML fragments function render_book_grid() { $cache_key = 'book_grid_html'; $html = get_transient( $cache_key ); if ( false === $html ) { ob_start(); $books = new WP_Query([ 'post_type' => 'book', 'posts_per_page' => 12, ]); if ( $books->have_posts() ) { echo '
'; while ( $books->have_posts() ) { $books->the_post(); ?>
            <div class="book-item">
                <h3><?php the_title(); ?></h3>
                <?php the_post_thumbnail( 'medium' ); ?>
            </div>
            <?php
        }
        echo '</div>';
    }
    wp_reset_postdata();

    $html = ob_get_clean();
    set_transient( $cache_key, $html, HOUR_IN_SECONDS );
}

echo $html;

}

Page Caching vs Object Caching /* * Page Caching: * - Caches entire HTML pages * - Fastest (serves static HTML) * - Plugins: WP Super Cache, W3 Total Cache, WP Rocket * - Bypassed for logged-in users * * Object Caching: * - Caches database queries and computed values * - Works for all users (logged-in and anonymous) * - Requires persistent cache backend (Redis, Memcached) * - Granular cache control /

// Example: Bypass page cache for dynamic content header( 'Cache-Control: no-cache, must-revalidate, max-age=0' );

// Cache-Control headers for static assets function add_cache_headers() { if ( is_admin() || is_user_logged_in() ) { return; }

// Cache static pages for 1 hour
if ( is_page() || is_single() ) {
    header( 'Cache-Control: public, max-age=3600' );
}

// Cache archives for 30 minutes
if ( is_archive() || is_home() ) {
    header( 'Cache-Control: public, max-age=1800' );
}

} add_action( 'send_headers', 'add_cache_headers' );

Cache Invalidation Patterns // Pattern 1: Time-based expiration set_transient( 'data', $value, 12 * HOUR_IN_SECONDS );

// Pattern 2: Event-based invalidation add_action( 'save_post_book', 'clear_book_caches' ); function clear_book_caches( $post_id ) { // Clear specific caches delete_transient( 'popular_books' ); delete_transient( 'recent_books' ); wp_cache_delete( "book_{$post_id}", 'books' );

// Clear author cache
$post = get_post( $post_id );
wp_cache_delete( "user_{$post->post_author}_books", 'user_books' );

}

// Pattern 3: Versioned cache keys function get_cache_version() { $version = wp_cache_get( 'cache_version', 'global' ); if ( false === $version ) { $version = time(); wp_cache_set( 'cache_version', $version, 'global' ); } return $version; }

function get_cached_data( $key ) { $version = get_cache_version(); $versioned_key = "{$key}_v{$version}"; return wp_cache_get( $versioned_key, 'my_group' ); }

function set_cached_data( $key, $data ) { $version = get_cache_version(); $versioned_key = "{$key}_v{$version}"; wp_cache_set( $versioned_key, $data, 'my_group', HOUR_IN_SECONDS ); }

function invalidate_all_caches() { // Increment version to invalidate all caches $new_version = time(); wp_cache_set( 'cache_version', $new_version, 'global' ); }

// Pattern 4: Cache warming add_action( 'save_post_book', 'warm_book_caches', 20 ); function warm_book_caches( $post_id ) { // Rebuild cache immediately after invalidation get_popular_books(); // Rebuilds transient }

CDN Integration // Rewrite asset URLs to CDN add_filter( 'wp_get_attachment_url', 'cdn_rewrite_url' ); add_filter( 'wp_calculate_image_srcset', 'cdn_rewrite_srcset' );

function cdn_rewrite_url( $url ) { $cdn_domain = 'https://cdn.example.com'; $site_url = site_url();

// Only rewrite uploads directory
if ( strpos( $url, '/wp-content/uploads/' ) !== false ) {
    return str_replace( $site_url, $cdn_domain, $url );
}

return $url;

}

function cdn_rewrite_srcset( $sources ) { if ( ! is_array( $sources ) ) { return $sources; }

foreach ( $sources as &$source ) {
    $source['url'] = cdn_rewrite_url( $source['url'] );
}

return $sources;

}

// Purge CDN cache on content update add_action( 'save_post', 'purge_cdn_cache' ); function purge_cdn_cache( $post_id ) { // Example: Cloudflare API $zone_id = 'your_zone_id'; $api_key = 'your_api_key'; $email = 'your@email.com';

$url = get_permalink( $post_id );

wp_remote_post( "https://api.cloudflare.com/client/v4/zones/{$zone_id}/purge_cache", [
    'headers' => [
        'X-Auth-Email' => $email,
        'X-Auth-Key' => $api_key,
        'Content-Type' => 'application/json',
    ],
    'body' => json_encode([
        'files' => [ $url ],
    ]),
]);

}

  1. Advanced Database Patterns WP_Query Optimization // Use 'fields' parameter to limit data $query = new WP_Query([ 'post_type' => 'book', 'fields' => 'ids', // Only return IDs (faster) ]);

// Count queries $count = new WP_Query([ 'post_type' => 'book', 'posts_per_page' => -1, 'fields' => 'ids', 'no_found_rows' => true, // Skip SQL_CALC_FOUND_ROWS 'update_post_meta_cache' => false, 'update_post_term_cache' => false, ]);

// Complex meta queries with performance in mind $query = new WP_Query([ 'post_type' => 'book', 'meta_query' => [ 'relation' => 'AND', [ 'key' => '_pages', 'value' => 200, 'compare' => '>', 'type' => 'NUMERIC', ], [ 'key' => '_rating', 'value' => 4, 'compare' => '>=', 'type' => 'DECIMAL', ], ], 'orderby' => 'meta_value_num', 'order' => 'DESC', ]);

Direct SQL for Complex Queries // When WP_Query is too slow, use direct SQL global $wpdb;

// Get books with JOIN on multiple meta keys $results = $wpdb->get_results(" SELECT p.ID, p.post_title, pm1.meta_value as isbn, pm2.meta_value as pages, pm3.meta_value as rating FROM {$wpdb->posts} p LEFT JOIN {$wpdb->postmeta} pm1 ON p.ID = pm1.post_id AND pm1.meta_key = '_isbn' LEFT JOIN {$wpdb->postmeta} pm2 ON p.ID = pm2.post_id AND pm2.meta_key = '_pages' LEFT JOIN {$wpdb->postmeta} pm3 ON p.ID = pm3.post_id AND pm3.meta_key = '_rating' WHERE p.post_type = 'book' AND p.post_status = 'publish' AND CAST(pm2.meta_value AS UNSIGNED) > 200 ORDER BY CAST(pm3.meta_value AS DECIMAL(3,2)) DESC LIMIT 20 ");

// Use $wpdb->prepare() for dynamic values $min_pages = 200; $results = $wpdb->get_results( $wpdb->prepare(" SELECT p.ID, p.post_title, pm.meta_value as pages FROM {$wpdb->posts} p INNER JOIN {$wpdb->postmeta} pm ON p.ID = pm.post_id WHERE p.post_type = 'book' AND pm.meta_key = '_pages' AND CAST(pm.meta_value AS UNSIGNED) > %d ORDER BY p.post_date DESC ", $min_pages ) );

Database Indexing // Add custom indexes in plugin activation register_activation_hook( FILE, 'create_custom_indexes' ); function create_custom_indexes() { global $wpdb;

// Index on post_type and post_status (common filter)
$wpdb->query("
    CREATE INDEX idx_post_type_status
    ON {$wpdb->posts} (post_type, post_status)
");

// Index on meta_key and meta_value for frequent lookups
$wpdb->query("
    CREATE INDEX idx_meta_key_value
    ON {$wpdb->postmeta} (meta_key, meta_value(191))
");

// Composite index for date-based queries
$wpdb->query("
    CREATE INDEX idx_post_type_date
    ON {$wpdb->posts} (post_type, post_date)
");

}

// Remove indexes on deactivation register_deactivation_hook( FILE, 'remove_custom_indexes' ); function remove_custom_indexes() { global $wpdb;

$wpdb->query( "DROP INDEX idx_post_type_status ON {$wpdb->posts}" );
$wpdb->query( "DROP INDEX idx_meta_key_value ON {$wpdb->postmeta}" );
$wpdb->query( "DROP INDEX idx_post_type_date ON {$wpdb->posts}" );

}

  1. Multisite Development Network-Activated Plugins // Detect multisite if ( is_multisite() ) { // Multisite-specific code }

// Network-wide hooks add_action( 'network_admin_menu', 'add_network_admin_page' ); function add_network_admin_page() { add_menu_page( 'Network Settings', 'My Plugin', 'manage_network_options', 'my-network-settings', 'render_network_settings_page' ); }

// Network options (not site-specific) add_site_option( 'my_network_setting', 'value' ); $value = get_site_option( 'my_network_setting' ); update_site_option( 'my_network_setting', 'new_value' ); delete_site_option( 'my_network_setting' );

Cross-Site Operations // Switch to another site $current_blog_id = get_current_blog_id(); switch_to_blog( 2 ); // Switch to site ID 2

// Perform operations on site 2 $posts = get_posts([ 'post_type' => 'book' ]); update_option( 'my_option', 'value' );

// Always restore restore_current_blog();

// Iterate all sites $sites = get_sites([ 'number' => 999 ]); foreach ( $sites as $site ) { switch_to_blog( $site->blog_id );

// Do something on each site
$count = wp_count_posts( 'book' );
error_log( "Site {$site->blog_id} has {$count->publish} books" );

restore_current_blog();

}

  1. Best Practices Service-Oriented Architecture
cache = $cache; $this->validator = $validator; } public function get_book( $book_id ) { // Check cache first $cache_key = "book_{$book_id}"; $book = $this->cache->get( $cache_key ); if ( false === $book ) { $book = get_post( $book_id ); if ( $book && 'book' === $book->post_type ) { $this->cache->set( $cache_key, $book, HOUR_IN_SECONDS ); } } return $book; } public function create_book( $data ) { // Validate input $errors = $this->validator->validate_book_data( $data ); if ( ! empty( $errors ) ) { return new \WP_Error( 'validation_failed', 'Validation failed', $errors ); } // Create book $book_id = wp_insert_post([ 'post_type' => 'book', 'post_title' => $data['title'], 'post_content' => $data['content'], 'post_status' => $data['status'], ]); if ( is_wp_error( $book_id ) ) { return $book_id; } // Clear related caches $this->cache->invalidate_group( 'books' ); return $book_id; } } // Dependency injection container class Container { private $services = []; public function register( $name, $service ) { $this->services[ $name ] = $service; } public function get( $name ) { if ( ! isset( $this->services[ $name ] ) ) { throw new \Exception( "Service not found: {$name}" ); } return $this->services[ $name ]; } } // Bootstrap $container = new Container(); $container->register( 'cache', new CacheService() ); $container->register( 'validator', new ValidationService() ); $container->register( 'books', new BookService( $container->get( 'cache' ), $container->get( 'validator' ) ) ); Event-Driven Design // Fire custom events do_action( 'myplugin_book_created', $book_id, $book_data ); do_action( 'myplugin_book_updated', $book_id, $old_data, $new_data ); do_action( 'myplugin_book_deleted', $book_id ); // Other plugins can listen add_action( 'myplugin_book_created', function( $book_id, $book_data ) { // Send email notification // Update analytics // Sync to external service }, 10, 2 ); // Use filters for modifiable data $book_data = apply_filters( 'myplugin_before_book_save', $book_data, $book_id ); $notification_recipients = apply_filters( 'myplugin_notification_recipients', [ 'admin@example.com' ], $book_id ); Scalability Considerations /** * Performance Checklist: * * 1. Database: * - Use indexes on frequently queried columns * - Avoid SELECT * queries (use specific fields) * - Batch operations instead of loops * - Use LIMIT for large datasets * * 2. Caching: * - Implement object caching (Redis/Memcached) * - Use transients for expensive operations * - Fragment caching for HTML blocks * - CDN for static assets * * 3. Queries: * - Use 'fields' => 'ids' when possible * - Set 'no_found_rows' => true if not paginating * - Disable update_post_meta_cache and update_post_term_cache when not needed * * 4. Assets: * - Minify and concatenate CSS/JS * - Lazy load images * - Use responsive images (srcset) * - Defer non-critical JavaScript * * 5. Monitoring: * - Use Query Monitor for development * - New Relic or Application Insights for production * - Monitor slow queries * - Track cache hit rates */ // Example: Batch processing with WP-CLI function process_books_batch() { $offset = 0; $batch_size = 100; do { $books = get_posts([ 'post_type' => 'book', 'posts_per_page' => $batch_size, 'offset' => $offset, 'fields' => 'ids', ]); foreach ( $books as $book_id ) { // Process each book update_post_meta( $book_id, '_processed', time() ); } $offset += $batch_size; // Prevent memory leaks wp_cache_flush(); } while ( count( $books ) === $batch_size ); } Related Skills When building advanced WordPress applications, consider these complementary skills (available in the skill library): WordPress Plugin Fundamentals: Core plugin architecture and hooks - essential foundation for custom REST endpoints and WP-CLI commands WordPress Security & Data Validation: Security best practices - critical for securing REST API endpoints and validating user input WordPress Testing & QA: Testing REST endpoints and WP-CLI - comprehensive testing strategies for advanced WordPress features GraphQL: Alternative to REST API - consider GraphQL as a modern alternative to WordPress REST API for complex data queries Docker: Development environment setup - containerize WordPress development for consistent and reproducible environments References REST API Handbook - Official REST API documentation WP-CLI - Command-line interface for WordPress Object Cache - WordPress object caching Transients API - WordPress transients documentation Query Monitor - Performance profiling plugin Redis Object Cache - Redis integration plugin ?>
返回排行榜