Skip to content
This repository has been archived by the owner on Feb 23, 2024. It is now read-only.

ProductImage Block: the lifecycle of the hook woocommerce_product_get_image is not backward compatible #12078

Closed
speshiou opened this issue Dec 7, 2023 · 1 comment · Fixed by #12098

Comments

@speshiou
Copy link

speshiou commented Dec 7, 2023

Describe the bug

The hook woocommerce_product_get_image has inconsistent behavior when using a ProductImage Block compared to using the traditional render method (calling $product->get_image() directly) if a product has no images.

The image elements of products on my store are all generated programmatically, we relied on woocommerce_product_get_image to handle the product images. After migrating to ProductImage Block, they are showing placeholder images instead.

To reproduce

Steps to reproduce the behavior:

  1. Create a product without uploading product images
  2. Add a filter to hook woocommerce_product_get_image, and return a custom image element.
  3. Navigate to a list of products composed of ProductImage Blocks that includes the product we created in the first step
  4. The product image shows a placeholder image instead of the image from the hook woocommerce_product_get_image

Expected behavior

The product image should show the image element from the hook woocommerce_product_get_image.

Environment

WordPress (please complete the following information):

  • WordPress version: 6.4.0
  • WooCommerce version: 8.3.1
  • Theme: Twenty Twenty-Four
  • Site language: English

Desktop (please complete the following information):

  • OS: macOS
  • Browser: chrome
  • Version: 119

Additional context

When using a ProductImage block, woocommerce_product_get_image won't be called if the product doesn't have any images.

The code snippet from src/BlockTypes/ProductImage.php.

	private function render_image( $product, $attributes ) {
		...

		// extra placeholder image handling
		if ( ! $product->get_image_id() ) {
			// The alt text is left empty on purpose, as it's considered a decorative image.
			// More can be found here: https://www.w3.org/WAI/tutorials/images/decorative/.
			// Github discussion for a context: https://github.com/woocommerce/woocommerce-blocks/pull/7651#discussion_r1019560494.
			return wc_placeholder_img(
				$image_size,
				array(
					'alt'   => '',
					'style' => $image_style,
				)
			);
		}

		// original placeholder image handling in get_image function
		return $product->get_image(
			$image_size,
			array(
				'alt'         => $product->get_title(),
				'data-testid' => 'product-image',
				'style'       => $image_style,
			)
		);
	}

When using the traditional way (calling $product->get_image() directly) to render a product list, woocommerce_product_get_image is always being called here, even if the product has not images.

The code snippet from templates/content-widget-product.php in WooCommerce.

<li>
	<?php do_action( 'woocommerce_widget_product_item_start', $args ); ?>

	<a href="<?php echo esc_url( $product->get_permalink() ); ?>">
		<?php echo $product->get_image(); // PHPCS:Ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
		<span class="product-title"><?php echo wp_kses_post( $product->get_name() ); ?></span>
	</a>

	<?php if ( ! empty( $show_rating ) ) : ?>
		<?php echo wc_get_rating_html( $product->get_average_rating() ); // PHPCS:Ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
	<?php endif; ?>

	<?php echo $product->get_price_html(); // PHPCS:Ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>

	<?php do_action( 'woocommerce_widget_product_item_end', $args ); ?>
</li>

The code snippet from includes/abstracts/abstract-wc-product.php.

	public function get_image( $size = 'woocommerce_thumbnail', $attr = array(), $placeholder = true ) {
		$image = '';
		if ( $this->get_image_id() ) {
			$image = wp_get_attachment_image( $this->get_image_id(), $size, false, $attr );
		} elseif ( $this->get_parent_id() ) {
			$parent_product = wc_get_product( $this->get_parent_id() );
			if ( $parent_product ) {
				$image = $parent_product->get_image( $size, $attr, $placeholder );
			}
		}

		if ( ! $image && $placeholder ) {
			$image = wc_placeholder_img( $size, $attr );
		}

		return apply_filters( 'woocommerce_product_get_image', $image, $this, $size, $attr, $placeholder, $image );
	}
@kmanijak
Copy link
Contributor

kmanijak commented Dec 8, 2023

Hi @speshiou! Thanks for raising this and thank you for the detailed analysis and for pointing out the code references, really helpful.

I prepared a fix for this issue: #12098, please follow the PR for further updates regarding the fix 🙌

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
3 participants