How does one display a collection list filtered by customer tags in shopify using `assign collection =` - variables

I'm trying to create a section within the 'Simple' Shopify theme that filters collections based on customer tags. For example, the customer has the following tags "Foo", "Bar". In turn display the following collections "Foo", "Bar".
I'm not too familiar with liquid so I'm trying to keep things as simple as possible. As such I have duplicated the collection-list.liquid from the 'Simple Theme' for my base and made the following edits to the file.
My Edits
<!-- the magic -->
<div class="collection-grid">
<div class="grid grid--uniform">
{% for tag in customer.tags %} // check customer tags.
{% capture tag_handle %} // capture tag URL handle e.g. /t-shirts in a variable 'tag_handle'
{{ tag | handle }}
{% endcapture %}
{% unless collection.handle contains tag_handle %} // check collection handles against the current tag handle to find a match. If so then do.
<div class="grid__item {{ grid_item_width }} slide-up-animation animated" {{ block.shopify_attributes }} role="listitem">
{% assign collection = collection.handle %} // Assign correct collection. I suspect this to be my stumbling block. See below for reason why.
{% include 'collection-grid-item' %}
</div>
{% endunless %}
{% endfor %}
</div>
</div>
<!-- / the magic -->
With the current code snippet, I do get results equal to the number of customer tags. The problem is that the results are empty, or at least the assigned collection variable is empty.
Any help with this would be greatly appreciated. Thanks in advance.

If you don't want to have to set up and maintain section blocks, how about something like this?
<!-- the magic -->
<div class="collection-grid">
<div class="grid grid--uniform">
{% for tag in customer.tags %} // for each customer tag do
{% assign tag_handle = tag | handle %} // get the tag handle (I.e. no spaces & lowercase)
{% assign collection = collections[tag_handle] %}
{% if collection == blank %}
{% continue %}
{% endif %}
<div class="grid__item {{ grid_item_width }} slide-up-animation animated" role="listitem">
{% include 'collection-grid-item' %}
</div>
{% endfor %}
</div>
</div>
<!-- / the magic -->
This approach will:
Loop through all the customer tags, as before, then
Takes the handle-ized version of the tag and performs a collection lookup
Skips to the next tag if there was no matching collection found
Displays the collection grid item if there is a matching collection
If you are expecting customers to have a lot of tags but only a few collection-related tags, one possible improvement may be to use a special prefix for the collection-related tags that you can check for and skip to the next tag if the prefix is not present. I also know that Shopify has a limit of 50 uses of the all_products lookup per page, but I don't know if there is a similar limit for collection lookups. Either way, for page performance it is probably best if the number of tags and collection lookups is kept reasonably small.
As an aside, one of the things you may be concerned with is page load time. Having too many expensive Liquid operations can cause Shopify's servers to have a bit of delay when serving your site's pages. While you're trying and testing solutions, one extremely useful tool that you can take advantage of is the "Shopify Theme Inspector" plugin for Chrome. This will give you a visual breakdown of how long it takes for Shopify to put your page together on the back-end. (I have no connection with the plugin at all, it's just a useful tool that I have used a few times when evaluating site performance)

So I was able to resolve my issue by taking a slightly different approach based upon #Bilal Akbar question. Seeing as the existing approach worked in the original Simple Theme why try to alter it? Why not allow the admin to load all collections and simply filter the results during the output.
<!-- the magic -->
<div class="collection-grid">
<div class="grid grid--uniform">
{% for tag in customer.tags %} // for each customer tag do
{% assign tag_handle = tag | handle %} // get the tag handle (I.e. no spaces & lowercase)
{% for block in section.blocks limit: section.blocks.size %} // keeping the original loop from the original file.
{% if block.settings.collection == tag_handle %} // check the collections list within the block to see If it matches the tag handle. if so then do.
<div class="grid__item {{ grid_item_width }} slide-up-animation animated" {{ block.shopify_attributes }} role="listitem">
{% assign collection = collections[block.settings.collection] %}
{% include 'collection-grid-item' %}
</div>
{% endif %}
{% endfor %}
{% endfor %}
</div>
</div>
<!-- / the magic -->
Again I'm not too savvy with liquid, If anyone has any alternate or maybe even a better way to achieve my desired result. I would be interested in hearing about it. Thanks again.

Related

Add Promotional Banner to a Specific Page of a Collection on Shopify?

We are using Shopify and would like to be able to add different promotional banners to specific collection pages. For example, have promo banner #1 only show on page 3 of Collection (A) and promo banner #2 only show on page 5 of Collection (B). Is this possible? If so, how could this be easily managed?
Go to Online store, themes, actions --> edit code. From there search "theme.liquid" and edit in the following code for each collection:
{% if collection.handle == "item1" %}
<div id="item1-banner">
Collection A!
</div>
{% endif %}
If you are wanting to use images, then simply use:
{%if collection.handle == "item1"%}
<img src="item1banner.jpg" />
{% elseif collection.handle =="item2" %}
<img src="item2banner.jpg" />
{% else %}
<img src="item3banner.jpg" />
{% endif %}

Related products based on grouping tags limit memory. Use instead collection with certain handle tag

By starting from this point https://www.shopify.com/partners/blog/related-products
I end up to this custom and working code:
{% comment %}
Get dynamic tag from product and create static collection handle
{% endcomment %}
{% for tag in product.tags %}
{% if tag contains 'IDSeries_' %}
{% assign ser_tag = tag %}
<li>
<a href="/collections/all/{{ ser_tag | handleize }}">
<h2>{{ ser_tag | remove: "IDSeries_" | prepend: "Go to products with Serie ID number:" }}</h2>
</a>
</li>
{% endif %}
{% endfor %}
{% comment %}
Return products by using the extracted tag
{% endcomment %}
{% for product in collections.all.products %}
{% if product.tags contains ser_tag %}
{{ product.title }}
{% endif %}
{% endfor %}
This above does work. But it's not the solution!
because limited memory, it works only for the first bunch of products and not for all.
By considering I have twenty five thousands products all with an incremental "IDSeries_'INT'"
My goal is: to show products with same specific "IDSeries_'INT'".
Since when this already happens at collection tag url like this <a href="/collections/all/{{ ser_tag | handleize }}">
I was wondering if I can return products (into a section products) that are already filtered at the collection handle tag url layer.
So at Themes/Debut/Sections/product-template.liquid
I am trying the following with no luck:
{% assign collection_handle = ser_tag %}
{% for product in collections.all[collection_handle].products %}
{{ product.title }}
{% endfor %}
Question:
Any clue on how to achieve a consistent result for such a big stock of products?
Extra question:
For a store with 30k products each with five tags (so 150k tags in
total).
Does tags have any limitation?
I also created a Shopify community topic https://community.shopify.com/c/Technical-Q-A/Related-products-based-on-grouping-tags-limit-memory-Use-instead/td-p/759705
I can think of two solutions for your problem, that doesn't affect the speed of the site.
Search based + Javascript
The search page on Shopify can search for tags as well.
So if you make a fetch request to the search page like so:
fetch('/search?q=IDSeries_*').then(res => res).then(res => console.log(res))
This will return you the products that include this tag combination and you can append it with javascript.
So you will populate the products with javascript.
GraphQL
You can use the Storefront GraphQL API to make a request and get products by specific tag, like so:
{
products(first: 10, query:"tag:>'IDSeries_'"){
edges {
cursor
node {
title
}
}
}
}
This will require understanding of GraphQL and especially their Store-Front API (not the Admin API).
For a store with 30k products each with five tags (so 150k tags in total).
Does tags have any limitation?
The all_tags object for the collection can return up to 1000 tags, the rest will be skipped. They need to be unique tags, repeatable tags doesn't count.
Other than that there are no limitations to my knowledge.
So at the end of the day to display tag search results in to a Shopify section I used fetch() and script looks like this:
<script>
{% for tag in product.tags %}
{% if tag contains 'IDSeries_' %}
{% assign ser_tag = tag %}
{% endif %}
{% endfor %}
const url = '/search?view=serie_list&q={{ ser_tag }}';
fetch(url)
.then(response => response.text(
))
.then(data => {
$('#serie_list_id').html(data);
});
</script>
Also as the magic #drip said I created a separate search template with {% layout none %} at the top and some other style

Assigning a collection to a custom created collection page in Shopify?

I am using a free Venture theme on Shopify and i am trying to make a custom collection page.
I found a solution in stackoverflow but it was able to help someplace.
How to add collection.liquid to an existing page?
The summery of the solution is:
Copy everything that's in collection.liquid and paste it into a new snippet (let's say you call it collection-copy.liquid).
Then, in the page you want to add the collections page to, just add {% include 'collection-copy' %}
This solution worked well but there is one more issue for me. In the custom created page it says "Sorry, there are no products in this collection" In the customization of the same page there is a "collection" section. But in the "collection" section there is no option to choose a collection. There is only "Enable tag filtering" and "Enable sorting" check boxes.
Webpage: https://mottomfreedom.com/pages/less-is-more
Do you have any idea of assigning a collection with this custom created snippet?
{% paginate collections[settings.frontpage_collection].products by 20 %}
<div class="page-width">
<header class="grid medium-up--grid--table section-header small--text-center">
<div class="grid__item medium-up--one-half section-header__item">
<h1 class="section-header__title">
{{ collection.title }}
{% if current_tags %}
– {% assign title_tags = current_tags | join: ', ' %}
{{ title_tags }}
{% endif %}
</h1>
{% if collection.description != blank %}
<div class="section-header__subtext rte">
{{ collection.description }}
</div>
{% endif %}
</div>
<div class="grid__item medium-up--one-half medium-up--text-right section-header__item">
{% section 'collection-filters' %}
</div>
</header>
<div class="grid grid--no-gutters grid--uniform">
{% for product in collection.products %}
<div class="grid__item small--one- medium-up--one-third">
{% include 'product-card', product: product %}
</div>
{% else %}
{% comment %}
Add default products to help with onboarding for collections/all only.
The onboarding styles and products are only loaded if the
store has no products.
{% endcomment %}
{% if shop.products_count == 0 %}
<div class="grid__item">
<div class="grid grid--no-gutters grid--uniform">
{% assign collection_index = 1 %}
{% for i in (1..10) %}
{% case i %}
{% when 7 %}
{% assign collection_index = 1 %}
{% when 8 %}
{% assign collection_index = 2 %}
{% when 9 %}
{% assign collection_index = 3 %}
{% when 10 %}
{% assign collection_index = 4 %}
{% endcase %}
<div class="grid__item small--one-half medium-up--one-fifth">
<a href="/admin/products" class="product-card">
<div class="product-card__image-container">
<div class="product-card__image-wrapper">
<div class="product-card__image">
{% capture current %}{% cycle 1, 2, 3, 4, 5, 6 %}{% endcapture %}
{{ 'product-' | append: current | placeholder_svg_tag: 'placeholder-svg' }}
</div>
</div>
</div>
<div class="product-card__info">
<div class="product-card__name">{{ 'homepage.onboarding.product_title' | t }}</div>
<div class="product-card__price">
$19.99
</div>
</div>
<div class="product-card__overlay">
{% assign view_string_length = 'products.product.view' | t | size %}
<span class="btn product-card__overlay-btn {% if view_string_length > 8 %} btn--narrow{% endif %}">{{ 'products.product.view' | t }}</span>
</div>
</a>
</div>
{% assign collection_index = collection_index | plus: 1 %}
{% endfor %}
</div>
</div>
{% else %}
{% comment %}
If collection exists but is empty, display message
{% endcomment %}
<div class="grid__item small--text-center">
<p>{{ 'collections.general.no_matches' | t }}</p>
</div>
{% endif %}
{% endfor %}
</div>
{% if paginate.pages > 1 %}
<div class="pagination">
{{ paginate | default_pagination | replace: '« Previous', '←' | replace: 'Next »', '→' }}
</div>
{% endif %}
</div>
{% endpaginate %}
You are right about giving some time before accepting an answer :)) The solution worked but forced me to create 1 page and 4 liquid files per collection. And at the end, i figured out that some sections like "collection.list" doesn't directs to the page which i have created. I think you were talking about this at the beginning of the answer :)
After that, i found a much better solution. Just creating a new section.liquid file and placing it in "collection.liquid" with an "if" statement solved my problem.
{% if collection.handle == 'less-is-more' %}
{% section 'custom-featured-products-LESSisMORE' %}
{% endif %}
But in any way, i'm grateful for your interest. Thank you very much Dave.
It looks like there's nothing defining the collection variable anywhere.
I would suggest changing the beginning of your code snippet from:
{% paginate collections[settings.frontpage_collection].products by 20 %}
To:
{% assign collection = collections[settings.frontpage_collection] %}
{% paginate collection.products by 20 %}
There is an implicit collections variable whenever you're on a page that includes /collections/[something] in the URL, but when you're on a URL that's /page/[something], you have an implicit page variable in Liquid instead.
Note: if the collection set in your theme's value for settings.frontpage_collection isn't the one you want, you can possibly:
a. Change the value using the 'Customize' link beside your theme (most easily found on the /admin/themes page), useful if you're not going to use that setting for anything else;
b. Hard-code a collection handle, eg: collections['i-am-sure-this-will-never-change'], but hard-coded strings are ugly and should generally be avoided;
c. Create your own theme setting by adding an entry to config/settings_schema.json - see https://help.shopify.com/en/themes/development/theme-editor/settings-schema if you're still getting up to speed with custom theme settings; or
d. If all your content is in a section, you can use section settings (similar to theme settings) to make a variable that's tied specifically to just that block of code.
If you need to make these special pages for multiple collections, and each of these pages is largely reusing the same code, you can make your life easier by moving the common code to a snippet and passing variables to it from your page template. To do so:
Create a file in the 'snippets' folder of your theme. (For this example, let's say the file is called collection-in-page.liquid. We will be passing a collection into this snippet, so you can remove the assign statement.
In your page-specific template, figure out what the collection handle is going to be
a. This might be hard-coded, or it might be something you could look up by using metafields or tags on the page. Examples:
{% assign collection_handle = 'hardcoded-handle' %}, {% assign collection_handle = page.metafields.related_items.collection %}
In your page template, include the snippet you created. I find it helps to explicitly pass any variables I want to use, like so:
{% include 'collection-in-page', collection: collections[collection_handle] %}
Hope this helps!

Using metafields and IF statements

I have been using metafields to try and add a list of downloads to my products. I have a few manuals that i want each product to have.
I am using this code:
{% if product.metafields.document.doc_1 =='' %}
<p>There are no downloads for this product</p>
{% else %}
<h4 class="page-subheading productscategory_title">
Downloads
</h4>
<ul>
{% for field in product.metafields.document%}
<li>{{ field | last }}</li>
{% endfor %}
</ul>
</div>
{% endif %}
but it doesn't seem to work.
Also is this the only way i can add links to files in a product without doing it in the description?
Use Boolean logic where ever you can for Shopify liquid. More details and use cases on the Boolean logic - https://help.shopify.com/themes/liquid/basics/true-and-false

Filter out collection name from product tag list (display) shopify

I would love to know how to remove from display tags that are used to automatically create collections (they appear in other product section).
For example:
$125
COLLECTION : BUSINESS
TAGS: AUTUMN BUSINESS BLACK
So that BUSINESS tag (which is used to form a BUSINESS collection ) is filtered out.
Tries the following 'straightforward' solution:
{% for tag in product.tags %}
{% unless tag == 'Business' or tag == 'Vintage' or tag == or tag == 'Boho' %}
display {{ tag }} link
{% endunless %}
{% endfor %}
Which did not see to Work
Thanks
Sometimes multiple conditions in if statements don't work so well in liquid. (See here and here.)
You could try something like this instead:
{% assign excluded_tags = "Business,Vintage,Boho" | split: "," %}
{% for tag in product.tags %}
{% unless excluded_tags contains tag %}
{{ tag }}
{% endunless %}
{% endfor %}
EDIT:
I just tested your code again, and even with the typo or tag == or..., it still works fine for me (just a warning on save).
Another suggestion is perhaps it's got to do with capitalisation. E.g. If you have a tag BUSINESS, then tag == 'Business' is not going to work. The capitalisation needs to be consistent.
EDIT 2:
From comment below:
I was thinking how to automatically populate excluded tags by the names of the collections of a product.
You can use map to get an array of collection titles:
{% assign excluded_tags = product.collections | map: 'title' %}
Your straightforward is incomplete.
{% for tag in product.tags %}
{% unless tag == 'Business' or tag == 'Vintage' or tag == <missing_value_here> or tag == 'Boho' %}
display {{ tag }} link
{% endunless %}
{% endfor %}
The third condition is empty hence could be the reason it is failing.
Also as mentioned in another answer by Steph, multiple conditions may be unstable at times, if that the case try this:
{% for tag in product.tags %}
{% unless tag == collection.title %}
display {{ tag }} link
{% endif %}
{% endfor %}
It will take care of any future collections as well.
P.S. This is assuming that the page where the tags are displaying contains a collection url.