Extending CIF(Commerce Integration Framework) Core Component

26 / Aug / 2023 by Arpit Aulak 0 comments

In continuation with our last blog on setting up the Venia store, we will customize AEM CIF core components as per our needs.

Adobe Experience Manager (AEM) is a powerful content management solution that enables businesses to deliver exceptional digital experiences. One of its key strengths lies in its Commerce Integration Framework (CIF) Core Components, which facilitates seamless integration of AEM with various commerce platforms. However, to truly stand out from the competition, businesses must go beyond the out-of-the-box features and embrace the art of customization. This blog will explore the benefits of customizing AEM CIF Core Components and guide you through unlocking their full potential.

So far, we have installed and configured the CIF add-on to our local AEM instance and connected it to the Venia store.

Prerequisite:-

  • AEM instance with CIF add-on
  • Magento Commerce setup with Venia store
  • Clone AEM CIF Venia Project: The Venia project can be cloned from Adobe’s official GitHub repo.

Use case

In this blog, we will be focusing on the Product Carousel component to show an “on-sale” icon based on a custom attribute in Magento products. Currently, the Product Carousel component looks like this, and we will add an on-sale icon next to the carousel heading.

After the changes to the core component, the Product Carousel component will show an on-sale icon if any of the products contain the on-sale attribute; this is how it would look.

Why Customize AEM CIF Core Components?

  1. Tailored User Experience: Customization allows you to create unique and personalized user experiences that align with your brand identity and customer preferences. Tailoring the components to specific target audiences can significantly improve engagement and conversion rates.
  2. Enhanced Functionality: The out-of-the-box CIF Core Components provide a solid foundation, but every business has unique requirements. Customization empowers you to add new features, modify existing functionalities, or integrate third-party tools seamlessly.
  3. Performance Optimization: By stripping away unnecessary code and functionalities, you can optimize the performance of CIF Core Components to deliver faster-loading pages, ensuring a smooth user experience.
  4. Scalability and Future-Proofing: Customizing CIF Core Components allows you to future-proof your digital ecosystem. As your business evolves, you can quickly adapt the components to match new requirements without starting from scratch.

Steps to Customize AEM CIF Core Components

1. First, we will add a custom attribute to the products in Adobe Commerce. The following are the steps to add a custom attribute “on_sale” to a product.

a. Navigate to Magento Admin Console > Catalog > Products.
b. Search for the product and edit it to add the custom attribute e.g., product code: VA24
c. Check the Graphql response using a Graphql IDE. eg.

query Products($filter: ProductAttributeFilterInput) {
products(filter: { sku: { eq: "VA24" } ) {
items {
name
sku
on_sale
}
}
}

The response should be like this:-

{
"data": {
"products": {
"items": [
{
"name": "Night Out Collection",
"sku": "VA24",
"on_sale": 1
}
]
}
}
}

2. Create a Custom Component: To avoid modifying the core code directly, create a custom AEM component that inherits from the CIF Core Component you wish to customize. This approach ensures that your changes are isolated and maintainable.

3. Now, we will extend the current sling model of the core component Product Carousel by following the delegation pattern for Sling Models. First, we will create a ProviderType interface that extends the ConsumerType interface of the core component with the additional functionality we want to add in our case, it would be the onSale method.

CustomProductCarousel
@ProviderType
public interface CustomProductCarousel extends ProductCarousel {
    public boolean onSale();
}
CustomProductCarouselImpl
@Model(adaptables = SlingHttpServletRequest.class, adapters = CustomProductCarousel.class, resourceType = CustomProductCarouselImpl.RESOURCE_TYPE)
public class CustomProductCarouselImpl implements CustomProductCarousel {

    private static final Logger LOGGER = LoggerFactory.getLogger(CustomProductCarouselImpl.class);
    private static final String RESOURCE_TYPE = "venia/components/commerce/productcarousel";
    private static final String ON_SALE_ATTRIBUTE = "on_sale";
    @Self
    @Via(type = ResourceSuperType.class)
    private ProductCarousel productCarousel;
    private AbstractProductsRetriever productsRetriever;
    @PostConstruct
    public void initModel() {
        productsRetriever = productCarousel.getProductsRetriever();
        if (productsRetriever != null) {
            // Pass your custom partial query to the ProductRetriever. This class will
            // automatically take care of executing your query as soon
            // as you try to access any product property.
            productsRetriever.extendProductQueryWith(productInterfaceQuery -> productInterfaceQuery.addCustomSimpleField(ON_SALE_ATTRIBUTE));
        }
    }
    @Override
    public boolean onSale() {
        return productsRetriever.fetchProducts().stream().anyMatch(productInterface -> {
            try {
                return productInterface.getAsInteger(ON_SALE_ATTRIBUTE) == 1;
            } catch (SchemaViolationError e) {
                LOGGER.error("Error retrieving on sale attribute");
            }
            return false;
        });
    }
    @Override
    public List<ProductListItem> getProducts() {
        return productCarousel.getProducts();
    }
    @Override
    public List<ProductListItem> getProductIdentifiers() {
        return productCarousel.getProductIdentifiers();
    }
    @Override
    public AbstractProductsRetriever getProductsRetriever() {
        return productsRetriever;
    }
    @Override
    public boolean isConfigured() {
        return productCarousel.isConfigured();
    }
    @Override
    public String getTitleType() {
        return productCarousel.getTitleType();
    }
}

In the above code, the delegation pattern in Sling Models enables MyProductTeaserImpl to use the ProductTeaser model by utilizing the sling:resourceSuperType attribute. For functions you wish to keep as-is without modifications, you can mirror the output of the corresponding ProductCarousel method. In the PostConstruct method, we get the AbstractProductsRetriever object in our model and then extend the Products query with the custom attribute “on_sale.” This way, when the query is executed, it will fetch the on_sale attribute of those products with the other data from graphQL. We have also created a method called “onSale,” which will retrieve attribute values from the products and check if any products have enabled it.

4. Next, we will customize the markup and styles of the component to show the on-sale icon. First, we will modify the HTML to show the on-sale icon next to the Product Carousel heading.
As per the current structure, we will modify two HTML files as follows:-

a. apps/venia/components/commerce/productcarousel/productcarousel.html

productcarousel
<sly data-sly-use.carousel="${'com.venia.core.models.commerce.CustomProductCarouselImpl' @product=properties.product}"
     data-sly-use.templates="core/wcm/components/commons/v1/templates.html"
     data-sly-use.carouselTpl="carousel.html" />


<sly data-sly-test.hasProducts="${carousel.isConfigured && carousel.products}"
     data-sly-call="${carouselTpl.carousel @ carousel = carousel, items = carousel.products, componentType='productcarousel'}"></sly>


<sly data-sly-call="${templates.placeholder @ isEmpty = !carousel.isConfigured}" />
<sly data-sly-call="${templates.placeholder @ isEmpty = carousel.isConfigured
&& !hasProducts, emptyTextAppend = 'Configured, but no products to display'}" />

b. apps/venia/components/commerce/productcarousel/carousel.html

carousel
<template data-sly-template.carousel="${@ carousel, items, componentType}" data-sly-use.cardTpl="card.html">
    <sly data-sly-test.type="${componentType ? componentType : 'carousel'}" />
    <div id="${carousel.id}" data-comp-is="${type}" class="${type}__container" data-cmp-data-layer="${carousel.data.json}">
        <div class="${type}__titlecontainer">
            <h2 data-sly-element="${carousel.titleType}" data-sly-test="${properties.jcr:title}" class="${type}__title">${properties.jcr:title}</h2>
            <!-- This is the span tag added for the on-sale icon.-->
            <span data-sly-test="${carousel.onSale}" class="${type}__sale">sale</span>
        </div>
        <button data-carousel-action='prev' class="${type}__btn ${type}__btn--prev" type="button" title="${'Show previous' @ i18n}" aria-label="${'Show previous' @ i18n}" style="display: none;"></button>
        <button data-carousel-action='next' class="${type}__btn ${type}__btn--next" type="button" title="${'Show next' @ i18n}" aria-label="${'Show next' @ i18n}" style="display: none;"></button>
        <div class="${type == 'carousel' ? '{0}__cardsroot' : '{0}__root' @ format=[type]}">
            <div class="${type}__parent">
                <div class="${type}__cardscontainer" data-sly-list.item="${items ? items : carousel.items}">
                    <sly data-sly-call="${cardTpl.card @ item=item, carousel=carousel}"/>
                </div>
            </div>
        </div>
    </div>
</template>

Now, Accordingly, make the changes in the scss file of the component to show the on-sale icon next to the heading of the carousel. The changes can be made to the following file in the ui.frontend module src/main/styles/commerce/_productcarousel.scss

Finally, we can see the on-sale icon in the product carousel component.

Conclusion

Customizing AEM CIF Core Components gives businesses a competitive edge in delivering personalized and exceptional digital experiences. You can optimize performance, enhance functionality, and future-proof your digital ecosystem by tailoring components to match your unique requirements. Remember to follow best practices, test thoroughly, and document your customizations to ensure a smooth development process. Embrace the power of personalization and elevate your digital presence with AEM CIF Core Components customization.

Stay tuned for our blogs on various other topics.

References:

https://experienceleague.adobe.com/docs/experience-manager-cloud-service/content/content-and-commerce/storefront/developing/customize-cif-components.html

FOUND THIS USEFUL? SHARE IT

Leave a Reply

Your email address will not be published. Required fields are marked *