The following will be helpful if there is a request to show a product label in product detail page and all product listing sections in the site.
Listing sections being:
- Product detail page - product content area (catalog_product_view.xml)
- Category pages (catalog_category_view.xml)
- Page builder product widget (catalog_widget_product_list.xml)
- Upsell and related products (catalog_product_view.xml)
- Crossell (checkout_cart_index.xml)
- Search (catalogsearch_result_index.xml)
- Advance Search (catalogsearch_advanced_result.xml)
1) Create custom attribute
app/code/Vendor/FeaturedLabel/Setup/Patch/Data/AddFeaturedLabelProductAttribute.php
namespace Vendor\FeaturedLabel\Setup\Patch\Data;
use Magento\Catalog\Model\Product;
use Magento\Eav\Model\Entity\Attribute\Backend\ArrayBackend;
use Magento\Eav\Model\Entity\Attribute\ScopedAttributeInterface;
use Magento\Eav\Setup\EavSetup;
use Magento\Eav\Setup\EavSetupFactory;
use Magento\Framework\Setup\ModuleDataSetupInterface;
use Magento\Framework\Setup\Patch\DataPatchInterface;
use Magento\Framework\Setup\Patch\PatchRevertableInterface;
class AddFeaturedLabelProductAttribute implements DataPatchInterface, PatchRevertableInterface
{
/**
* @var ModuleDataSetupInterface
*/
private $moduleDataSetup;
/**
* @var EavSetupFactory
*/
private $eavSetupFactory;
/**
* Constructor
*
* @param ModuleDataSetupInterface $moduleDataSetup
* @param EavSetupFactory $eavSetupFactory
*/
public function __construct(
ModuleDataSetupInterface $moduleDataSetup,
EavSetupFactory $eavSetupFactory
) {
$this->moduleDataSetup = $moduleDataSetup;
$this->eavSetupFactory = $eavSetupFactory;
}
/**
* {@inheritdoc}
*/
public function apply()
{
$this->moduleDataSetup->getConnection()->startSetup();
/** @var EavSetup $eavSetup */
$eavSetup = $this->eavSetupFactory->create(['setup' => $this->moduleDataSetup]);
$eavSetup->addAttribute(
Product::ENTITY,
'featured_label',
[
'type' => 'int',
'label' => 'Featured Label',
'input' => 'select',
'source' => '',
'required' => false,
'backend' => ArrayBackend::class,
'sort_order' => '30',
'global' => ScopedAttributeInterface::SCOPE_STORE,
'default' => null,
'visible' => true,
'user_defined' => true,
'searchable' => true,
'filterable' => true,
'comparable' => false,
'visible_on_front' => false,
'unique' => false,
'apply_to' => '',
'group' => 'General',
'used_in_product_listing' => true,
'is_used_in_grid' => true,
'is_visible_in_grid' => false,
'is_filterable_in_grid' => false,
'option' => ''
]
);
$this->moduleDataSetup->getConnection()->endSetup();
}
public function revert()
{
$this->moduleDataSetup->getConnection()->startSetup();
/** @var EavSetup $eavSetup */
$eavSetup = $this->eavSetupFactory->create(['setup' => $this->moduleDataSetup]);
$eavSetup->removeAttribute(Product::ENTITY, 'featured_label');
$this->moduleDataSetup->getConnection()->endSetup();
}
/**
* {@inheritdoc}
*/
public function getAliases()
{
return [];
}
/**
* {@inheritdoc}
*/
public static function getDependencies()
{
return [
];
}
}
2) Create one template to show the labels
app/code/Vendor/FeaturedLabel/view/frontend/templates/product/featured_label.phtml
/** @var $block \Magento\Catalog\Block\Product\View */
/* @var $product Vendor\FeaturedLabel\ViewModel\Product */
/* @var $featuredLabel Vendor\FeaturedLabel\ViewModel\FeaturedLabel */
$product = $block->getData('product');
$featuredLabel = $block->getData('featured_label_widget');
?>
<?php $currentProduct = $product->getCurrentProduct() ?: $product; ?>
<?php if ($featuredLabel->getFeaturedLabel($currentProduct)): ?>
<div class="featured-label">
<?= $block->escapeHtml(__($featuredLabel->getFeaturedLabel($currentProduct))) ?>
</div>
<?php endif; ?>
3) Create viewModel for labels
Note: the current product is passed as product inteface
app/code/Vendor/FeaturedLabel/ViewModel/FeaturedLabel.php
namespace Vendor\FeaturedLabel\ViewModel;
use Magento\Catalog\Api\Data\ProductInterface;
use Magento\Framework\View\Element\Block\ArgumentInterface;
class FeaturedLabel implements ArgumentInterface
{
/**
* @param ProductInterface $product
* @return false|\Magento\Framework\Phrase
*/
public function getFeaturedLabel(ProductInterface $product)
{
if ($product->getAttributeText('featured_label')) {
return $product->getAttributeText('featured_label');
}
}
4) View model to get the current product
app/code/Vendor/Catalog/ViewModel/Product.php
namespace Vendor\Catalog\ViewModel;
use Magento\Catalog\Api\Data\ProductInterface;
use Magento\Framework\Registry;
use Magento\Framework\View\Element\Block\ArgumentInterface;
class Product implements ArgumentInterface
{
/**
* @var Registry
*/
private $registry;
/**
* @var ProductInterface
*/
private $product;
public function __construct(
Registry $registry,
array $data = []
) {
$this->registry = $registry;
}
public function getCurrentProduct(): ProductInterface
{
if (is_null($this->product)) {
$this->product = $this->registry->registry('current_product');
}
return $this->product;
}
}
5) Add the label to product detail page - product content area, upsell, related products
app/code/Vendor/FeaturedLabel/view/frontend/layout/catalog_product_view.xml
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
<body>
<referenceContainer name="product.info.media">
<block class="Magento\Catalog\Block\Product\View"
name="product.featured.label"
template="Vendor_FeaturedLabel::product/featured_label.phtml"
before="-">
<arguments>
<argument name="product" xsi:type="object">Vendor\FeaturedLabel\ViewModel\Product</argument>
<argument name="featured_label_widget" xsi:type="object">Vendor\FeaturedLabel\ViewModel\FeaturedLabel</argument>
</arguments>
</block>
</referenceContainer>
<referenceBlock name="catalog.product.related" template="Magento_Catalog::product/list/items.phtml">
<block name="product.related.featured.label"
as="featured.label"
template="Vendor_FeaturedLabel::product/featured_label.phtml">
<arguments>
<argument name="product" xsi:type="object">Vendor\FeaturedLabel\ViewModel\Product</argument>
<argument name="featured_label_widget" xsi:type="object">Vendor\FeaturedLabel\ViewModel\FeaturedLabel</argument>
</arguments>
</block>
</referenceBlock>
<referenceBlock name="product.info.upsell" template="Magento_Catalog::product/list/items.phtml">
<block name="product.upsell.featured.label"
as="featured.label"
template="Vendor_FeaturedLabel::product/featured_label.phtml">
<arguments>
<argument name="product" xsi:type="object">Vendor\FeaturedLabel\ViewModel\Product</argument>
<argument name="featured_label_widget" xsi:type="object">Vendor\FeaturedLabel\ViewModel\FeaturedLabel</argument>
</arguments>
</block>
</referenceBlock>
</body>
</page>
6) Category pages app/code/Vendor/FeaturedLabel/view/frontend/layout/catalog_category_view.xml
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
<body>
<referenceBlock name="category.products.list" template="Vendor_Catalog::product/list.phtml">
<block name="category.product.featured.label"
as="featured.label"
template="Vendor_FeaturedLabel::product/featured_label.phtml">
<arguments>
<argument name="product" xsi:type="object">Vendor\Catalog\ViewModel\Product</argument>
<argument name="featured_label_widget" xsi:type="object">Vendor\FeaturedLabel\ViewModel\FeaturedLabel</argument>
</arguments>
</block>
</referenceBlock>
</body>
</page>
7) Page builder product widget
a) app/code/Vendor/Catalog/Block/Product/ProductsList.php
namespace Vendor\Catalog\Block\Product;
use Magento\Catalog\Api\CategoryRepositoryInterface;
use Magento\Catalog\Api\Data\ProductInterface;
use Magento\Catalog\Block\Product\Context as ProductContext;
use Magento\Catalog\Model\Product\Visibility;
use Magento\Catalog\Model\ResourceModel\Product\CollectionFactory;
use Magento\CatalogWidget\Block\Product\ProductsList as DefaultProductsList;
use Magento\CatalogWidget\Model\Rule;
use Magento\Framework\App\Http\Context;
use Magento\Framework\DataObject\IdentityInterface;
use Magento\Framework\Serialize\Serializer\Json;
use Magento\Framework\Url\EncoderInterface;
use Magento\Framework\View\Element\BlockInterface;
use Magento\Framework\View\LayoutFactory;
use Magento\Rule\Model\Condition\Sql\Builder;
use Magento\Widget\Block\BlockInterface as WidgetBlockInterface;
use Magento\Widget\Helper\Conditions;
class ProductsList extends DefaultProductsList implements WidgetBlockInterface, IdentityInterface
{
/**
* @var LayoutFactory
*/
private $layoutFactory;
/**
* @var Json
*/
private $json;
/**
* @var EncoderInterface
*/
private $urlEncoder;
/**
* @var array
*/
private $layoutBlocks = [];
/**
* ProductsList constructor.
* @param ProductContext $context
* @param CollectionFactory $productCollectionFactory
* @param Visibility $catalogProductVisibility
* @param Context $httpContext
* @param Builder $sqlBuilder
* @param Rule $rule
* @param Conditions $conditionsHelper
* @param CategoryRepositoryInterface $categoryRepository
* @param LayoutFactory $layoutFactory
* @param Json $json
* @param EncoderInterface $urlEncoder
* @param array $data
*/
public function __construct(
ProductContext $context,
CollectionFactory $productCollectionFactory,
Visibility $catalogProductVisibility,
Context $httpContext,
Builder $sqlBuilder,
Rule $rule,
Conditions $conditionsHelper,
CategoryRepositoryInterface $categoryRepository,
LayoutFactory $layoutFactory,
Json $json,
EncoderInterface $urlEncoder,
array $data = []
) {
$this->layoutFactory = $layoutFactory;
$this->json = $json;
$this->urlEncoder = $urlEncoder;
parent::__construct(
$context,
$productCollectionFactory,
$catalogProductVisibility,
$httpContext,
$sqlBuilder,
$rule,
$conditionsHelper,
$categoryRepository,
$data,
$json,
$layoutFactory,
$urlEncoder
);
}
/**
* @param $name
* @return mixed|null
* @throws \Magento\Framework\Exception\LocalizedException
*/
public function getBlockFromLayout($name)
{
$layoutBlock = isset($this->layoutBlocks[$name]) ? $this->layoutBlocks[$name] : null;
if (!$layoutBlock) {
$layout = $this->layoutFactory->create(['cacheable' => false]);
$layout->getUpdate()->addHandle('catalog_widget_product_list')->load();
$layout->generateXml();
$layout->generateElements();
$layoutBlock = $layout->getBlock($name);
$this->layoutBlocks[$name] = $layoutBlock;
}
return $layoutBlock;
}
}
b) app/code/Vendor/FeaturedLabel/etc/widget.xml
<widgets xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Widget:etc/widget.xsd">
<widget id="products_list" class="Vendor\Catalog\Block\Product\ProductsList">
<label translate="true">Catalog Products List</label>
<description translate="true">List of Products</description>
</widget>
</widgets>
c) app/code/Vendor/FeaturedLabel/view/adminhtml/web/js/content-type/products/mass-converter/carousel-widget-directive.js
define([
'Magento_PageBuilder/js/content-type/products/mass-converter/carousel-widget-directive',
'Magento_PageBuilder/js/utils/object'
], function (OriginalCarouselWidgetDirective, _object) {
'use strict';
function CarouselWidgetDirective() {
OriginalCarouselWidgetDirective.apply(this, arguments);
}
CarouselWidgetDirective.prototype = Object.create(OriginalCarouselWidgetDirective.prototype);
/**
* Override the toDom function to pass new 'type' and 'template' parameters.
*
* @param data
* @param config
* @returns {*}
*/
CarouselWidgetDirective.prototype.toDom = function toDom(data, config) {
var attributes = {
type: "Vendor\\Catalog\\Block\\Product\\ProductsList",
template: "Vendor_FeaturedLabel::product/widget/content/carousel.phtml",
anchor_text: "",
id_path: "",
show_pager: 0,
products_count: data.carousel_products_count,
condition_option: data.condition_option,
condition_option_value: "",
type_name: "Catalog Products Carousel",
conditions_encoded: this.encodeWysiwygCharacters(data.conditions_encoded || "")
};
if (data.sort_order) {
attributes.sort_order = data.sort_order;
}
if (typeof data[data.condition_option] === "string") {
attributes.condition_option_value = this.encodeWysiwygCharacters(data[data.condition_option]);
}
if (attributes.conditions_encoded.length === 0) {
return data;
}
_object.set(data, config.html_variable, this.buildDirective(attributes));
return data;
};
return CarouselWidgetDirective;
});
d) app/code/Vendor/FeaturedLabel/view/adminhtml/web/js/content-type/products/mass-converter/widget-directive.js
define([
'Magento_PageBuilder/js/content-type/products/mass-converter/widget-directive',
'Magento_PageBuilder/js/utils/object'
], function (OriginalWidgetDirective, _object) {
'use strict';
function WidgetDirective() {
OriginalWidgetDirective.apply(this, arguments);
}
WidgetDirective.prototype = Object.create(OriginalWidgetDirective.prototype);
/**
* Override the toDom function to pass new 'type' and 'template' parameters.
*
* @param data
* @param config
* @returns {*}
*/
WidgetDirective.prototype.toDom = function toDom(data, config) {
var attributes = {
type: "Vendor\\Catalog\\Block\\Product\\ProductsList",
template: "Vendor_FeaturedLabel::product/widget/content/grid.phtml",
anchor_text: "",
id_path: "",
show_pager: 0,
products_count: data.products_count,
condition_option: data.condition_option,
condition_option_value: "",
type_name: "Catalog Products List",
conditions_encoded: this.encodeWysiwygCharacters(data.conditions_encoded || "")
};
if (data.sort_order) {
attributes.sort_order = data.sort_order;
}
if (typeof data[data.condition_option] === "string") {
attributes.condition_option_value = this.encodeWysiwygCharacters(data[data.condition_option]);
}
if (attributes.conditions_encoded.length === 0) {
return data;
}
_object.set(data, config.html_variable, this.buildDirective(attributes));
return data;
};
return WidgetDirective;
});
e) app/code/Vendor/FeaturedLabel/view/adminhtml/pagebuilder/content_type/products.xml
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_PageBuilder:etc/content_type.xsd">
<type name="products">
<appearances>
<appearance name="grid">
<converters>
<converter component="Vendor_FeaturedLabel/js/content-type/products/mass-converter/widget-directive" name="widget_directive">
<config>
<item name="html_variable" value="html"/>
</config>
</converter>
</converters>
</appearance>
<appearance name="carousel">
<converters>
<converter component="Vendor_FeaturedLabel/js/content-type/products/mass-converter/carousel-widget-directive" name="widget_directive">
<config>
<item name="html_variable" value="html"/>
</config>
</converter>
</converters>
</appearance>
</appearances>
</type>
</config>
f) app/code/Vendor/FeaturedLabel/view/frontend/layout/catalog_widget_product_list.xml
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
<body>
<block name="category.products.list.featured.label"
as="featured.label.top"
template="Vendor_FeaturedLabel::product/featured_label.phtml">
<arguments>
<argument name="product" xsi:type="object">Vendor\Catalog\ViewModel\Product</argument>
<argument name="featured_label_widget" xsi:type="object">Vendor\FeaturedLabel\ViewModel\FeaturedLabel</argument>
</arguments>
</block>
</body>
</page>
8) Template files
- Product detail page - product content area (catalog_product_view.xml)
- Category pages (catalog_category_view.xml)
- Page builder product widget (catalog_widget_product_list.xml)
- Upsell and related products (catalog_product_view.xml)
- Crossell (checkout_cart_index.xml)
- Search (catalogsearch_result_index.xml)
- Advance Search (catalogsearch_advanced_result.xml)
a) Category pages / Advance Search / Search - view/frontend/templates/product/list.phtml
<?php if ($featuredLabel = $block->getChildBlock('featured.label')): ?>
<?= /* @noEscape */ $featuredLabel->setData('product', $product)->toHtml() ?>
<?php endif; ?>
b) Crossell / Upsell and related products lists
view/frontend/templates/product/list/items
<?php if ($featuredLabel = $block->getChildBlock('featured.label')): ?>
<?= /* @noEscape */ $featuredLabel->setData('product', $item)->toHtml() ?>
<?php endif; ?>
b) Page builder product widget (carosal)
<?php if ($featureLabel = $block->getBlockFromLayout('category.products.list.featured.label')): ?>
<?= /* @noEscape */ $featureLabel->setData('product', $_item)->toHtml() ?>
<?php endif; ?>
c) Page builder product widget (grid)
<?php if ($featuredLabel = $block->getBlockFromLayout('category.products.list.featured.label')): ?>
<?= /* @noEscape */ $featuredLabel->setData('product', $_item)->toHtml() ?>
<?php endif; ?>
Top comments (0)