Skip to content

Magento 2.4.6: Order confirmation email configurable product custom attribute returns parent value instead of child

I’m trying to add a custom product attribute ‘renewal’ to the order confirmation email. I have several configurable products and they are returning the parent product attribute value rather than the correct child product.

The ‘renewal’ product attribute is a text field that may contain some html markup. It will normally be different for the parent product and the child product when the product is a configurable product. Site is running Magento 2.4.6

Here is the line of code I’m using in /Vendor/Theme/Magento_Sales/templates/email/items/order/default.phtml

<?= $_item->getProduct()->getData('renewal'); ?>

I also attempted to extend DefaultItems, but it does not appear to be working properly (/Vendor/Module/)

public function getRenewal($id){
    $product = $this->productFactory->create()->load($id);
    return $product->getRenewal();
}

Then calling the function in the default.phtml template

<?= $_item->getRenewal($_item) ?>

This is the complete /Vendor/Theme/Magento_Sales/templates/email/items/order/default.phtml file (I had attempted to integrate this into the module, but it did not load correctly).

<?php
/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */

// phpcs:disable Magento2.Templates.ThisInTemplate
// phpcs:disable Magento2.Files.LineLength, Generic.Files.LineLength

/** @var $block MagentoSalesBlockOrderEmailItemsDefaultItems */

/** @var $_item MagentoSalesModelOrderItem */
/** @var MagentoFrameworkEscaper $escaper */
$_item = $block->getItem();
$_order = $_item->getOrder();
?>
<tr>
    <td class="item-info<?= ($block->getItemOptions() ? ' has-extra' : '') ?>">
        <p class="product-name"><?= $escaper->escapeHtml($_item->getName()) ?></p>
        <p class="sku"><?= $escaper->escapeHtml(__('SKU')) ?>: <?= $escaper->escapeHtml($block->getSku($_item)) ?></p>
        <?php if ($block->getItemOptions()): ?>
            <dl class="item-options">
            <?php foreach ($block->getItemOptions() as $option): ?>
                <dt><strong><em><?= $escaper->escapeHtml($option['label']) ?></em></strong></dt>
                <dd>
                    <?= /* @noEscape */  nl2br($option['value']) ?>
                </dd>
            <?php endforeach; ?>
            </dl>
        <?php endif; ?>
        <?php $addInfoBlock = $block->getProductAdditionalInformationBlock(); ?>
        <?php if ($addInfoBlock):?>
            <?= $addInfoBlock->setItem($_item)->toHtml() ?>
        <?php endif; ?>
        <p><?= $_item->getProduct()->getData('renewal'); ?></p>
        
        <?= $escaper->escapeHtml($_item->getDescription()) ?>
    </td>
    <td class="item-qty"><?= (float) $_item->getQtyOrdered() ?></td>
    <td class="item-price">
       <?= $block->getOriginalPrice($_item) ?><br>
       <?= $block->getDiscountAmount($_item) ?><br>       
        <?= /* @noEscape */ $block->getItemPrice($_item) ?>
    </td>
</tr>
<?php if ($_item->getGiftMessageId()
    && $_giftMessage = $this->helper(MagentoGiftMessageHelperMessage::class)
        ->getGiftMessage($_item->getGiftMessageId())
): ?>
    <tr>
    <td colspan="3" class="item-extra">
        <table class="message-gift">
            <tr>
                <td>
                    <h3><?= $escaper->escapeHtml(__('Gift Message')) ?></h3>
                    <strong><?= $escaper->escapeHtml(__('From:')) ?></strong> <?= $escaper->escapeHtml($_giftMessage->getSender()) ?>
                    <br /><strong><?= $escaper->escapeHtml(__('To:')) ?></strong> <?= $escaper->escapeHtml($_giftMessage->getRecipient()) ?>
                    <br /><strong><?= $escaper->escapeHtml(__('Message:')) ?></strong>
                    <br /><?= $escaper->escapeHtml($_giftMessage->getMessage()) ?>
                </td>
            </tr>
        </table>
    </td>
</tr>
<?php endif; ?>

I created a custom module to extend the DefaultItems class (I have replaced my custom vendor and module names with Vendor and Module respectively). This does not appear to be working currently.

registration.php

<?php
   MagentoFrameworkComponentComponentRegistrar::register(
    MagentoFrameworkComponentComponentRegistrar::MODULE,
    'Vendor_Module',
    __DIR__
);

etc/module.xml

<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd">
        <module name="Vendor_Module" setup_version="1.0.0" />
    </config>

etc/di.xml

<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
    <preference for="MagentoSalesBlockOrderEmailItemsDefaultItems" type="VendorModuleBlockOrderEmailItemsDefaultItems"/>
</config>

Block/Order/Email/Items/DefaultItems.php

<?php
/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */

namespace VendorModuleBlockOrderEmailItems;

use MagentoCatalogModelProductFactory;

class DefaultItems extends MagentoSalesBlockOrderEmailItemsDefaultItems
{
    public function getProductModel($id)
    {
        $product = $this->productFactory->create()->load($id);
        return $product;
    }
    
    public function getRenewal($id){
           $product = $this->productFactory->create()->load($id);
            return $product->getRenewal();
    }