Product Image Helper – How to Resolve Issues with Getting Product Images

helperimageproduct

Consider the example code found in my own block file:

public function getImages()
{
    $images = array();

    $products = Mage::getModel('catalog/product')
        ->getCollection()
        ->addAttributeToSelect('*')
        ->addAttributeToFilter(
            'status',
            array('eq' => Mage_Catalog_Model_Product_Status::STATUS_ENABLED)
            );
    $products->getSelect()->order('rand()');
    $products->getSelect()->limit(3);

    foreach ($products as $product) {
        $imageUrl = $product->getImageUrl();
        $image['imageUrl'] = $imageUrl;
        $image['title'] = $product->getName();
        $images[] = $image;
    }
    return $images;
}

In my template file, I have some expected corresponding code:

<div class="images">
    <?php foreach ($this->getImages() as $image): ?>
        <img src="<?php echo $image['imageUrl'] ?>" title="<?php echo $image['title'] ?>">
    <?php endforeach ?>
</div>

This completely works as expected. Three random products are chosen and an image is displayed for each (with the product name as title):

  • Product 1 – Image1 – Title1
  • Product 2 – Image2 – Title2
  • Product 3 – Image3 – Title3

When I attempt to call the image resize helper, however, things get weird. Focus on the foreach ($products as $product) section from the working piece of code above, and see the difference below:

    foreach ($products as $product) {
        // Use the resizer instead of directly calling $product->getImageUrl()
        $imageUrl = $this->helper('catalog/image')->init($product, 'image')->resize(200)->keepFrame(false);
        $image['imageUrl'] = $imageUrl;
        $image['title'] = $product->getName();
        $images[] = $image;
    }

I would expect this to work properly, but instead each product is using the image for the last product listed, but the title remains correct:

  • Product 1 – Image3 – Title1
  • Product 2 – Image3 – Title2
  • Product 3 – Image3 – Title3

It's clear that $image['imageUrl'] is getting assigned correctly in each iteration of the loop, because echo $image['imageUrl'] inside the loop displays the correct URL. Ultimately $image['imageUrl'] is getting reassigned for all iterations to the final value. But why does this occur when using the helper and it doesn't when calling the attribute value directly?

Best Answer

So as you have discovered the helper init function actually just populates lots of attributes and then returns itself.

If you look at file /app/design/frontend/base/default/template/catalog/product/view/media.phtml line 40 you will see the following:

$_img = '<img src="'.$this->helper('catalog/image')->init($_product, 'image')->resize(265).'" alt="'.$this->htmlEscape($this->getImageLabel()).'" title="'.$this->htmlEscape($this->getImageLabel()).'" />';

I would assume that looking at this it is safe to cast the helper as a string and use the return string as the image path. The helper has a __toString function that will be used when you perform the cast to string.

Edit

When assigning the imageUrl variable to the array it will only assign a reference so you will always get the last one when you come back to this array in the template. If you really want to have the object in the images then try the following.

$image['imageUrl'] = clone $imageUrl;

This will create a separate copy of the object each time. Personally I think that casting it would be the best solution though.

Related Topic