How can I get all attachments by a user on a WP multisite network?

You could create a cron job that gathers all the data and caches the results in a usermeta value for each user. It’d be slow to generate, but that wouldn’t matter because it’d run in the background. The data would then be fast to retrieve on the page where you display the user’s images.

The basic code would be something like:

add_action( 'init', 'schedule_cron_jobs' );
add_action( 'cache_user_images', 'cache_user_images' );

function schedule_cron_jobs() : void {
    if ( ! wp_next_scheduled( 'cache_user_images' ) ) {
        wp_schedule_event( time(), 'twicedaily', 'cache_user_images' );
    }
};

function cache_user_images() : void {
    $user_images = array();

    $users = get_users(
        array(
            'number' => -1,
            'fields' => 'ID',
        )
    );

    $sites = get_sites(
        array(
            'number' => false,
            'fields' => 'ids',
        )
    );

    foreach ( $sites as $site_id ) {
        switch_to_blog( $site_id );

        foreach ( $users as $user_id ) {
            $attachments = get_posts(
                array(
                    'post_type'      => 'attachment',
                    'post_status'    => 'inherit',
                    'post_mime_type' => 'image',
                    'author'         => (int) $user_id,
                    'posts_per_page' => -1,
                )
            );

            // Prune the cached data to only what's needed, to save disk space.
            array_walk(
                $attachments,
                function( &$attachment ) use ( $site_id ) {
                    $attachment = array(
                        'site_id'    => $site_id,
                        'post_id'    => $attachment->ID,
                        'post_title' => $attachment->post_title,
                        'url'        => wp_get_attachment_url( $attachment->ID ),
                    );
                }
            );

            if ( $attachments ) {
                $user_images[ $user_id ] = array_merge( $user_images[ $user_id ] ?? array(), $attachments );
            }
        }

        restore_current_blog();
    }

    foreach ( $users as $user_id ) {
        if ( ! empty( $user_images[ $user_id ] ) ) {
            update_user_meta( $user_id, 'user_images', $user_images[ $user_id ] );
        }
    }
}

There’s all kinds of optimizations you could make if needed, but it’ll depend on number of sites/users, specific app needs, etc. For example:

  • Run the cron every 10 minutes, but have it only work on 100 users during each execution.
  • Restrict to users/sites queries to a subset, if only some users/sites apply.
  • Set count_total and count to false in the user and site queries, respectively, to avoid the extra query to get the total count.

One downside to this approach is that once a user uploads an image, it won’t show up in the cache until the next cron execution. To solve that, you’ll need to add some more code that will register a save_post callback. That callback would append the attachment being uploaded to the user’s cache, so that it’s available immediately.

Hata!: SQLSTATE[HY000] [1045] Access denied for user 'divattrend_liink'@'localhost' (using password: YES)