, family: array} */ public static function of_product( int $product_id ): array { clean_post_cache( $product_id ); $product = wc_get_product( $product_id ); if ( ! $product instanceof \WC_Product ) { return array( 'images' => array(), 'family' => array(), ); } $family = array( $product_id ); $images = array_map( 'intval', $product->get_gallery_image_ids( 'edit' ) ); $images[] = (int) $product->get_image_id( 'edit' ); foreach ( $product->get_children() as $child ) { $family[] = (int) $child; $variation = wc_get_product( (int) $child ); if ( $variation instanceof \WC_Product ) { $images[] = (int) $variation->get_image_id( 'edit' ); } } return array( 'images' => array_values( array_filter( array_unique( $images ) ) ), 'family' => $family, ); } /** * Whether $attachment_id is one of the images in $set (from of_product()). * * @param array{images: array, family: array} $set The product's images. */ public static function contains( array $set, int $attachment_id ): bool { if ( $attachment_id <= 0 || 'attachment' !== get_post_type( $attachment_id ) ) { return false; } return in_array( $attachment_id, $set['images'], true ) || in_array( (int) get_post_field( 'post_parent', $attachment_id ), $set['family'], true ); } /** * Whether $attachment_id is an image of any product that counts, cheapest check first: its post_parent (a primary * key read), then products naming it as their main image, then galleries (each narrowed by the meta_key index). * Nothing is cached: the answer guards a single write and the relations change under the editor's feet. */ public static function of_any_product( int $attachment_id ): bool { if ( $attachment_id <= 0 || 'attachment' !== get_post_type( $attachment_id ) ) { return false; } $parent = (int) get_post_field( 'post_parent', $attachment_id ); if ( $parent > 0 && self::counts( $parent ) ) { return true; } global $wpdb; $id = (string) $attachment_id; // phpcs:ignore WordPress.DB.DirectDatabaseQuery -- Existence check on one meta key, uncached on purpose; see the docblock. $thumbnail_of = $wpdb->get_col( $wpdb->prepare( "SELECT post_id FROM {$wpdb->postmeta} WHERE meta_key = '_thumbnail_id' AND meta_value = %s", $id ) ); // A whole list entry only: FIND_IN_SET never matches an id whose digits sit inside another id, unlike LIKE alone. // phpcs:ignore WordPress.DB.DirectDatabaseQuery -- As above. $gallery_of = $wpdb->get_col( $wpdb->prepare( "SELECT post_id FROM {$wpdb->postmeta} WHERE meta_key = '_product_image_gallery' AND meta_value LIKE %s AND FIND_IN_SET( %s, meta_value )", '%' . $wpdb->esc_like( $id ) . '%', $id ) ); foreach ( array_merge( (array) $thumbnail_of, (array) $gallery_of ) as $owner ) { if ( self::counts( (int) $owner ) ) { return true; } } return false; } /** * Whether $post_id is a product or variation that is neither trashed nor an auto-draft. */ private static function counts( int $post_id ): bool { $post = get_post( $post_id ); return $post instanceof \WP_Post && in_array( $post->post_type, array( 'product', 'product_variation' ), true ) && ! in_array( $post->post_status, self::DEAD_STATUSES, true ); } }