Neste tutorial, adicionaremos 2 espaços para produtos relacionados (alternativos e complementares) exibidos na parte inferior da página de detalhes do produto.
Os tipos de produtos relacionados que serão exibidos são:
- Produtos da mesma categoria do produto principal (este é o comportamento padrão)
- Se os produtos forem vinculados manualmente a partir do formulário de produtos no administrador, serão exibidos:
- Produtos alternativos: devem ser produtos semelhantes ao produto principal
- Produtos complementares: devem ser os produtos que complementam o produto principal
HTML
1. Vamos criar um novo snipplet com o nome product-related.tpl dentro da pasta snipplets/product. O código para isso é o seguinte:
{# Default related products visibility conditions #} {% set related_products = [] %} {% set related_products_ids_from_app = product.metafields.related_products.related_products_ids %} {% set has_related_products_from_app = related_products_ids_from_app | get_products | length > 0 %} {% if has_related_products_from_app %} {% set related_products = related_products_ids_from_app | get_products %} {% endif %} {% if related_products is empty %} {% set max_related_products_length = 8 %} {% set max_related_products_achieved = false %} {% set related_products_without_stock = [] %} {% set max_related_products_without_achieved = false %} {% if related_tag %} {% set products_from_category = related_products_from_controller %} {% else %} {% set products_from_category = category.products | shuffle %} {% endif %} {% for product_from_category in products_from_category if not max_related_products_achieved and product_from_category.id != product.id %} {% if product_from_category.stock is null or product_from_category.stock > 0 %} {% set related_products = related_products | merge([product_from_category]) %} {% elseif (related_products_without_stock | length < max_related_products_length) %} {% set related_products_without_stock = related_products_without_stock | merge([product_from_category]) %} {% endif %} {% if (related_products | length == max_related_products_length) %} {% set max_related_products_achieved = true %} {% endif %} {% endfor %} {% if (related_products | length < max_related_products_length) %} {% set number_of_related_products_for_refill = max_related_products_length - (related_products | length) %} {% set related_products_for_refill = related_products_without_stock | take(number_of_related_products_for_refill) %} {% set related_products = related_products | merge(related_products_for_refill) %} {% endif %} {% endif %} {% set complementary_products = complementary_product_list | length > 0 %} {# Show alternative products when there are default category alternatives with no complementaries or manually selected alternatives #} {% set alternative_products = related_products | length > 0 and not (complementary_products and source_alternative == 'default') %} {# Set related products classes #} {% set section_class = 'section-products-related my-3' %} {% set container_class = 'container' %} {% set title_class = 'h3 text-center' %} {% set products_container_class = 'position-relative swiper-container-horizontal' %} {% set slider_container_class = 'swiper-container' %} {% set swiper_wrapper_class = 'swiper-wrapper' %} {% set slider_control_pagination_class = 'swiper-pagination' %} {% set slider_control_class = 'icon-inline icon-w-8 icon-2x svg-icon-text' %} {% set slider_control_prev_class = 'swiper-button-prev' %} {% set slider_control_next_class = 'swiper-button-next' %} {% set control_prev = include ('snipplets/svg/chevron-left.tpl', {svg_custom_class: slider_control_class}) %} {% set control_next = include ('snipplets/svg/chevron-right.tpl', {svg_custom_class: slider_control_class}) %} {# Alternative products #} {% set alternative_data_component = source_alternative == 'default' ? 'related-products' : 'alternative-products' %} {% if alternative_products %} {{ component( 'products-section',{ title: settings.products_related_title, id: 'related-products', data_component: alternative_data_component, products_amount: related_products | length, products_array: related_products, product_template_path: 'snipplets/grid/item.tpl', product_template_params: {'slide_item': true}, slider_controls_position: 'bottom', slider_pagination: true, svg_sprites: false, section_classes: { section: 'js-related-products ' ~ section_class, container: container_class, title: title_class, products_container: products_container_class, slider_container: 'js-swiper-related ' ~ slider_container_class, slider_wrapper: swiper_wrapper_class, slider_control_pagination: 'js-swiper-related-pagination ' ~ slider_control_pagination_class, slider_control_prev_container: 'js-swiper-related-prev ' ~ slider_control_prev_class, slider_control_prev: 'icon-flip-horizontal', slider_control_next_container: 'js-swiper-related-next ' ~ slider_control_next_class, }, custom_control_prev: control_prev, custom_control_next: control_next, }) }} {% endif %} {# Complementary products #} {% set complementary_section_id = 'complementary-products' %} {% if complementary_products %} {{ component( 'products-section',{ title: 'Para comprar con este producto' | translate, id: complementary_section_id, data_component: complementary_section_id, products_amount: complementary_product_list | length, products_array: complementary_product_list, product_template_path: 'snipplets/grid/item.tpl', product_template_params: {'slide_item': true}, slider_controls_position: 'bottom', slider_pagination: true, svg_sprites: false, section_classes: { section: 'js-complementary-products ' ~ section_class, container: container_class, title: title_class, products_container: products_container_class, slider_container: 'js-swiper-complementary ' ~ slider_container_class, slider_wrapper: swiper_wrapper_class, slider_control_pagination: 'js-swiper-complementary-pagination ' ~ slider_control_pagination_class, slider_control_prev_container: 'js-swiper-complementary-prev ' ~ slider_control_prev_class, slider_control_prev: 'icon-flip-horizontal', slider_control_next_container: 'js-swiper-complementary-next ' ~ slider_control_next_class, }, custom_control_prev: control_prev, custom_control_next: control_next, }) }} {% endif %}
Pode ver que estamos incluindo o snipplet item.tpl da pasta snipplets/grid (com base no layout Base), pode ser que você precise incluir o snipplet single_product.tpl. O importante é usar o mesmo snipplet que usamos para o item nas listagens de produtos, como aqueles nos templates category.tpl ou search.tpl.
Por outro lado, é importante mencionar que estamos utilizando o componente privado seção de produtos. Para mais informações sobre as opções deste componente, recomendamos este artigo.
2. Dentro de item.tpl ou single_product.tpl iremos adicionar na div onde temos as classes para as colunas do Bootstrap o condicional {% if slide_item %}js-item-slide swiper-slide{% endif %}.. Abaixo está um exemplo de como aplicá-lo:
<div class="{% if slide_item %}js-item-slide swiper-slide{% else %}col-12 col-sm-4{% endif %} item item-product"> ... </div>
3. Incluímos o snipplet de produtos relacionados no template product.tpl abaixo de tudo, como abaixo:
{% include 'snipplets/product/product-related.tpl' %}
4. Finalmente, para a parte do HTML, precisamos adicionar uma pasta SVG dentro da pasta snipplets. Aqui vamos adicionar os SVGs que usamos para os ícones no carrinho.
chevron-left.tpl
<svg class="{{ svg_custom_class }}" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 512"><path d="M231.293 473.899l19.799-19.799c4.686-4.686 4.686-12.284 0-16.971L70.393 256 251.092 74.87c4.686-4.686 4.686-12.284 0-16.971L231.293 38.1c-4.686-4.686-12.284-4.686-16.971 0L4.908 247.515c-4.686 4.686-4.686 12.284 0 16.971L214.322 473.9c4.687 4.686 12.285 4.686 16.971-.001z"/></svg>
chevron-right.tpl
<svg class="{{ svg_custom_class }}" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 512"><path d="M24.707 38.101L4.908 57.899c-4.686 4.686-4.686 12.284 0 16.971L185.607 256 4.908 437.13c-4.686 4.686-4.686 12.284 0 16.971L24.707 473.9c4.686 4.686 12.284 4.686 16.971 0l209.414-209.414c4.686-4.686 4.686-12.284 0-16.971L41.678 38.101c-4.687-4.687-12.285-4.687-16.971 0z"/></svg>
CSS
Requisito:
Ter adicionado helper classes em seu layout. Você pode seguir este pequeno tutorial para fazer isso (é só copiar e colar algumas classes, não leva mais que 1 minuto).
Como neste exemplo usamos um slider com o Swiper, precisamos adicionar o plugin. Para ver como fazer isso, você pode ler este pequeno artigo e continuar com este tutorial.
Se você preferir mostrar os produtos em um grid clássico sem slider ou se já tiver incluído o Swiper, pode pular esta etapa.
JS
⚠️ A partir do dia 30 de janeiro de 2023, a biblioteca jQuery será removida do código de nossas lojas, portanto, a função "$" não poderá ser utilizada.
Precisamos aplicar as funções no arquivo store.js.tpl (ou onde suas funções JS estiverem) com o seguinte código:
{% if template == 'product' %} {# /* // Product Related */ #} // Set loop for related products products sliders {% set columns = settings.grid_columns %} const desktopColumns = {% if columns == 1 %}3{% else %}4{% endif %}; function calculateRelatedLoopVal(sectionSelector) { let productsAmount = jQueryNuvem(sectionSelector).attr("data-related-amount"); let loopVal = false; const applyLoop = (window.innerWidth < 768 && productsAmount > {{ columns }}) || (window.innerWidth > 768 && productsAmount > desktopColumns); if (applyLoop) { loopVal = true; } return loopVal; } let alternativeLoopVal = calculateRelatedLoopVal(".js-related-products"); let complementaryLoopVal = calculateRelatedLoopVal(".js-complementary-products"); {# Alternative products #} createSwiper('.js-swiper-related', { lazy: true, watchOverflow: true, loop: alternativeLoopVal, centerInsufficientSlides: true, spaceBetween: 30, slidesPerView: {{ columns }}, pagination: { el: '.js-swiper-related-pagination', clickable: true, }, navigation: { nextEl: '.js-swiper-related-next', prevEl: '.js-swiper-related-prev', }, breakpoints: { 767: { slidesPerView: desktopColumns, } } }); {# Complementary products #} createSwiper('.js-swiper-complementary', { lazy: true, watchOverflow: true, loop: complementaryLoopVal, centerInsufficientSlides: true, spaceBetween: 30, slidesPerView: {{ columns }}, pagination: { el: '.js-swiper-complementary-pagination', clickable: true, }, navigation: { nextEl: '.js-swiper-complementary-next', prevEl: '.js-swiper-complementary-prev', }, breakpoints: { 767: { slidesPerView: desktopColumns, } } }); {% endif %}
Configurações
No arquivo config/settings.txt vamos adicionar a opção de alterar o título da seção dps produtos alternativos (você pode fazer isso também para as complementares se precisar) dentro da seção “Detalhes do produto”.
title title = Productos relacionados i18n_input description = Título para los productos alternativos name = products_related_title
Traduções
Nesta etapa adicionamos os textos para as traduções no arquivo config/translations.txt
es "Productos relacionados" pt "Produtos relacionados" en "Related products" es_mx "Productos relacionados" es "Título para los productos alternativos" pt "Título para os produtos alternativos" es_mx "Título para los productos alternativos"
Dentro de config/defaults.txt vamos adicionar os textos das mensagens padrão.
products_related_title_es = Productos similares products_related_title_en = Similar products products_related_title_pt = Produtos similares
Ativação
Depois que todas as alterações forem aplicadas ao seu código, precisamos que você entre em contato com a Nuvemshop para concluir a ativação em parceiros@nuvemshop.com.br
Pronto, uma vez relacionados os produtos a partir do formulário de produtos no administrador, serão visualizados os relacionados criados manualmente, caso contrário serão mostrados os padrão mencionados no início deste artigo. Excelente!