Magento自定义.phtml文件中带自定义与配置选项的产品表单使用方法及显示异常排查
问题描述
我在Magento主题中尝试通过SKU循环展示2个关联产品,集成代码后产品能正常显示,加入购物车功能也没问题,但产品的自定义选项和配置选项完全不显示。想请教各位我遗漏了哪些关键步骤?同时也想了解如何在自定义.phtml文件中正确渲染带有自定义选项和配置选项的产品表单。
我的实现步骤如下:
1. 修改form.phtml文件
在主题目录theme/Magento_Catalog/templates/view/下的form.phtml中添加了以下代码:
<?php $_complete_set_attributeValue = $_product->getResource()->getAttribute('complete_set_sku')->getFrontend()->getValue($_product); // print_r($_complete_set_attributeValue); if ($_complete_set_attributeValue) { echo $this->getLayout()->createBlock("Magento\Framework\View\Element\Template") ->setTemplate("Magento_Catalog::product/popup_comeplete_set_product.phtml") ->toHtml(); } ?>
2. 创建自定义模板popup_comeplete_set_product.phtml
在主题目录theme/Magento_Catalog/templates/product/下创建了该文件,代码如下:
<?php $objectManager = \Magento\Framework\App\ObjectManager::getInstance(); $helperImport = $objectManager->get('\Magento\Catalog\Helper\Image'); $cartHelper = $objectManager->get('\Magento\Catalog\Block\Product\ListProduct'); $_helper = $this->helper('Magento\Catalog\Helper\Output'); $postDataHelper = $this->helper('Magento\Framework\Data\Helper\PostHelper'); $productTypeInstance = $objectManager->get('Magento\ConfigurableProduct\Model\Product\Type\Configurable'); $typeInstance = $objectManager->get('Magento\GroupedProduct\Model\Product\Type\Grouped'); // $typeInstance = $objectManager->get('Magento\Bundle\Model\Product\Type'); $collection = $objectManager->get('Magento\Catalog\Model\Product') ->getCollection() ->addAttributeToSelect('*') ->addAttributeToFilter('sku',array('BAB','PRI')); $total_product_count = $collection->count(); ?> <div class="popup-complete-set-block product-add-form <?= $block->escapeHtml($product->getSku()) ?>"> <form style="width: 100%;display: flex;" data-product-sku="<?= $block->escapeHtml($product->getSku()) ?>" action="<?= $addToCartUrl; ?>" method="post" id="product_addtocart_form"<?php if ($product->getOptions()): ?> enctype="multipart/form-data"<?php endif; ?>> <input type="hidden" name="product" value="<?= /* @escapeNotVerified */ $product->getId() ?>" /> <input type="hidden" name="selected_configurable_option" value="" /> <input type="hidden" name="related_product" id="related-products-field" value="" /> <?= $block->getBlockHtml('formkey') ?> <?= $block->getChildHtml('form_top') ?> <?php if (!$block->hasOptions()):?> <?= $block->getChildHtml('product_info_form_content') ?> <?php else:?> <?php if ($product->isSaleable() && $block->getOptionsContainer() == 'container1'):?> <?= $block->getChildChildHtml('options_container') ?> <?php endif;?> <?php endif; ?> <?php if ($product->isSaleable() && $block->hasOptions() && $block->getOptionsContainer() == 'container2'):?> <?= $block->getChildChildHtml('options_container') ?> <?php endif;?> <?= $block->getChildHtml('form_bottom') ?> <div class="popup-complete-set-image"> <img src="<?php echo $resizedImageUrl; ?>" alt="<?php echo $product->getName() ?>" /> </div> <div class="popup-complete-set-detail"> <p class="slide-number"><?php echo $counter++;?> / <?php echo $total_product_count;?></p> <h3 class="slide-title"><a href="<?php echo $product->getProductUrl() ?>"><?php echo $product->getName() ?></a></h3> <p class="slide-price"> <?php echo $block->getProductPrice($product);?> </p> <?php if ($product->isSaleable()): ?> <div class="box-tocart"> <div class="fieldset"> <?php //if ($block->shouldRenderQuantity()): ?> <div class="field qty"> <label class="label" for="qty"><span><?= /* @escapeNotVerified */ __('Qty') ?></span></label> <div class="control"> <div class="sp-quantity"> <div class="sp-minus fff"> <a class="ddd" href="#">-</a></div> <div class="sp-input"> <input type="number" name="qty" id="qty" value="<?= /* @escapeNotVerified */ $block->getProductDefaultQty() * 1 ?>" title="<?= /* @escapeNotVerified */ __('Qty') ?>" class="input-text qty quntity-input" style="border: 0px solid #dfdfdf !important;border-radius: 0 !important;height: 43px;" data-validate="<?= $block->escapeHtml(json_encode($block->getQuantityValidators())) ?>" /> </div> <div class="sp-plus fff"> <a class="ddd" href="#">+</a></div> </div> </div> </div> <?php //endif; ?> <div class="actions"> <button type="submit" title="<?= /* @escapeNotVerified */ $buttonTitle ?>" class="action primary tocart product-form-cart" id="product-addtocart-button"> <span><?= /* @escapeNotVerified */ $buttonTitle ?></span> <svg fill="#fff" height="20" viewBox="0 0 1792 1792" xmlns="http://www.w3.org/2000/svg"><path d="M1171 960q0 13-10 23l-466 466q-10 10-23 10t-23-10l-50-50q-10-10-10-23t10-23l393-393-393-393q-10-10-10-23t10-23l50-50q10-10 23-10t23 10l466 466q10 10 10 23z"></path></svg> </button> <?= $block->getChildHtml('', true) ?> </div> </div> </div> <?php else : ?> <?php if ($product->getIsSalable()) : ?> <div class="stock available"><span><?php /* @escapeNotVerified */ echo __('In stock') ?></span></div> <?php else : ?> <div class="stock unavailable"><span><?php /* @escapeNotVerified */ echo __('Out of stock') ?></span></div> <?php endif; ?> <?php endif; ?> </div> </form> </div>
问题原因分析
你的代码里有几个核心问题导致选项不显示:
- 未正确传递产品对象到自定义块:你在
form.phtml里创建的是通用Template块,没有把查询到的关联产品传递给这个块,导致模板里的$product变量上下文不对,无法正确加载产品选项。 - 缺少选项渲染的子块:Magento的产品自定义选项、配置选项是通过专门的子块(比如
Magento_Catalog::product/view/options.phtml、Magento_ConfigurableProduct::product/view/type/options/configurable.phtml)来渲染的,你的自定义块没有加载这些子块,所以getChildChildHtml('options_container')根本找不到内容。 - 直接使用ObjectManager不规范:虽然能拿到产品集合,但没有按照Magento的标准流程初始化产品块的依赖,导致产品的选项数据没有被正确加载和准备。
- 变量未定义:模板里的
$addToCartUrl、$resizedImageUrl、$buttonTitle、$counter这些变量都没有定义,不仅会导致功能异常,也会影响选项渲染的逻辑。
解决方案
下面给你两种可行的修改方案,推荐第一种更符合Magento规范的方法:
方案一:使用Magento自带的产品表单块渲染
Magento自带的Magento\Catalog\Block\Product\View\Form块已经封装了所有选项渲染的逻辑,我们可以直接复用它:
1. 修改form.phtml中的代码
<?php $_complete_set_attributeValue = $_product->getResource()->getAttribute('complete_set_sku')->getFrontend()->getValue($_product); if ($_complete_set_attributeValue) { // 先获取关联产品SKU列表,这里假设你的属性值是逗号分隔的SKU $relatedSkus = explode(',', $_complete_set_attributeValue); // 获取产品集合 $productCollection = $this->getLayout()->createBlock('Magento\Catalog\Block\Product\ListProduct') ->getProductCollection() ->addAttributeToSelect('*') ->addAttributeToFilter('sku', ['in' => $relatedSkus]); // 循环每个产品,创建表单块 $counter = 1; $total_product_count = $productCollection->count(); foreach ($productCollection as $relatedProduct) { echo $this->getLayout()->createBlock('Magento\Catalog\Block\Product\View\Form') ->setProduct($relatedProduct) ->setData('counter', $counter++) ->setData('total_count', $total_product_count) ->setTemplate('Magento_Catalog::product/popup_complete_set_product.phtml') ->toHtml(); } } ?>
2. 修改popup_complete_set_product.phtml模板
现在模板可以直接使用$block(即Product\View\Form块)的所有方法,包括选项渲染:
<?php $_product = $block->getProduct(); $buttonTitle = __('Add to Cart'); // 处理图片 $resizedImageUrl = $block->getImage($_product, 'product_page_image_small')->getUrl(); $counter = $block->getData('counter'); $total_product_count = $block->getData('total_count'); ?> <div class="popup-complete-set-block product-add-form <?= $block->escapeHtml($_product->getSku()) ?>"> <form data-product-sku="<?= $block->escapeHtml($_product->getSku()) ?>" action="<?= $block->getSubmitUrl($_product) ?>" method="post" id="product_addtocart_form_<?= $_product->getId() ?>" <?php if ($_product->getOptions()): ?> enctype="multipart/form-data"<?php endif; ?>> <input type="hidden" name="product" value="<?= /* @escapeNotVerified */ $_product->getId() ?>" /> <input type="hidden" name="selected_configurable_option" value="" /> <input type="hidden" name="related_product" id="related-products-field-<?= $_product->getId() ?>" value="" /> <?= $block->getBlockHtml('formkey') ?> <?= $block->getChildHtml('form_top') ?> <?php if (!$block->hasOptions()):?> <?= $block->getChildHtml('product_info_form_content') ?> <?php else:?> <?php if ($_product->isSaleable() && $block->getOptionsContainer() == 'container1'):?> <?= $block->getChildChildHtml('options_container') ?> <?php endif;?> <?php endif; ?> <?php if ($_product->isSaleable() && $block->hasOptions() && $block->getOptionsContainer() == 'container2'):?> <?= $block->getChildChildHtml('options_container') ?> <?php endif;?> <?= $block->getChildHtml('form_bottom') ?> <div class="popup-complete-set-image"> <img src="<?= $resizedImageUrl; ?>" alt="<?= $block->escapeHtml($_product->getName()) ?>" /> </div> <div class="popup-complete-set-detail"> <p class="slide-number"><?= $counter;?> / <?= $total_product_count;?></p> <h3 class="slide-title"><a href="<?= $_product->getProductUrl() ?>"><?= $block->escapeHtml($_product->getName()) ?></a></h3> <p class="slide-price"> <?= $block->getProductPrice($_product);?> </p> <?php if ($_product->isSaleable()): ?> <div class="box-tocart"> <div class="fieldset"> <div class="field qty"> <label class="label" for="qty_<?= $_product->getId() ?>"><span><?= /* @escapeNotVerified */ __('Qty') ?></span></label> <div class="control"> <div class="sp-quantity"> <div class="sp-minus fff"> <a class="ddd" href="#">-</a></div> <div class="sp-input"> <input type="number" name="qty" id="qty_<?= $_product->getId() ?>" value="<?= /* @escapeNotVerified */ $block->getProductDefaultQty() * 1 ?>" title="<?= /* @escapeNotVerified */ __('Qty') ?>" class="input-text qty quntity-input" style="border: 0px solid #dfdfdf !important;border-radius: 0 !important;height: 43px;" data-validate="<?= $block->escapeHtml(json_encode($block->getQuantityValidators())) ?>" /> </div> <div class="sp-plus fff"> <a class="ddd" href="#">+</a></div> </div> </div> </div> <div class="actions"> <button type="submit" title="<?= /* @escapeNotVerified */ $buttonTitle ?>" class="action primary tocart product-form-cart" id="product-addtocart-button-<?= $_product->getId() ?>"> <span><?= /* @escapeNotVerified */ $buttonTitle ?></span> <svg fill="#fff" height="20" viewBox="0 0 1792 1792" xmlns="http://www.w3.org/2000/svg"><path d="M1171 960q0 13-10 23l-466 466q-10 10-23 10t-23-10l-50-50q-10-10-10-23t10-23l393-393-393-393q-10-10-10-23t10-23l50-50q10-10 23-10t23 10l466 466q10 10 10 23z"></path></svg> </button> <?= $block->getChildHtml('', true) ?> </div> </div> </div> <?php else : ?> <?php if ($_product->getIsSalable()) : ?> <div class="stock available"><span><?php /* @escapeNotVerified */ echo __('In stock') ?></span></div> <?php else : ?> <div class="stock unavailable"><span><?php /* @escapeNotVerified */ echo __('Out of stock') ?></span></div> <?php endif; ?> <?php endif; ?> </div> </form> </div>
方案二:手动加载选项子块(适合自定义程度高的场景)
如果你需要完全自定义表单结构,可以手动在自定义块中添加选项子块:
1. 在form.phtml中传递产品并添加子块
<?php $_complete_set_attributeValue = $_product->getResource()->getAttribute('complete_set_sku')->getFrontend()->getValue($_product); if ($_complete_set_attributeValue) { $relatedSkus = explode(',', $_complete_set_attributeValue); $productCollection = $this->getLayout()->createBlock('Magento\Catalog\Block\Product\ListProduct') ->getProductCollection() ->addAttributeToSelect('*') ->addAttributeToFilter('sku', ['in' => $relatedSkus]); $counter = 1; $total_product_count = $productCollection->count(); foreach ($productCollection as $relatedProduct) { // 创建自定义块 $customBlock = $this->getLayout()->createBlock("Magento\Framework\View\Element\Template") ->setTemplate("Magento_Catalog::product/popup_complete_set_product.phtml") ->setProduct($relatedProduct) ->setData('counter', $counter++) ->setData('total_count', $total_product_count); // 添加自定义选项子块 $customBlock->setChild('options', $this->getLayout()->createBlock('Magento\Catalog\Block\Product\View\Options') ->setProduct($relatedProduct) ->set
相关产品推荐
相关产品推荐

