How can I show Shopify variants depending on option value - shopify

How could you display only certain variants on the product and collection pages in Shopify depending on the value of an option?
Assuming the option name is 'Box Options' and the values would either be 'Trade' or 'Retail' I would expect something similar to this code to return the price for the Trade variant:
{%- assign get_variant = product.options_with_values -%}
{% assign trade_variant = get_variant | where: "Trade" %}
<p>{{ trade_variant.price }}</p>
For context I'm trying to get trade prices to be used where the customer tag is "Trade" using this if statement in the liquid file:
{% if customer.tags contains 'Trade' %}
Feel like this should be really easy but for the life of me I cannot get this to work.

If your variants contain only one option, then something like that should work (not tested):
{% if customer.tags contains 'Trade' %}
{% assign p_variants = product.variants | where:'title','Trade' %}
{% for variant in p_variants %}
{{ variant.title }}
{% endfor %}
{% endif %}
Then, second case, your variants go with multiple options, then it is a little bit more complex (not tested either):
{% if customer.tags contains 'Trade' %}
{% for variant in product.variants %}
{% if variant.options contains 'Box Options' and variant.title contains 'Trade' %}
{{ variant.title }}
{% endif %}
{% endfor %}
{% endif %}

Related

Go through all available variants in Shopify liquid

I am trying to show all the available sizes from a product in the collection page.
I managed to write this snippet that will show available sizes, but only for one of the color combinations.
{% for option in product.options_with_values %}
{% assign downcased_option = option.name | downcase %}
{% if downcased_option contains 'size' %}
{% assign is_size = true %}
{% for value in option.values %}
{% assign variant_available = true %}
{% if product.options.size >= 1 %}
{% unless product.variants[forloop.index0].available %}
{% assign variant_available = false %}
{% endunless %}
{% endif %}
<span class="{% unless variant_available %}soldout{% endunless %}">{{ value | escape }}</span>
{% endfor %}
{% endif %}
{% endfor %}
But, let's say we have the next variants:
Green/32, Green/34
Blue/34
Black/34, Black/36
With the snippet above, it will not show available size 36 from black color as it's not available in green.
What I want is to go through every single size and show if availability, no matter the color.
Does someone know how to achieve this?
Thanks
Forgive me if I'm missing something but making the assumption that all variants have a size and there are only two options you just need to loop through the available variants and just display the variant name split it and display first or last where ever the size is in the options list. if there are loads of duplicates then put them in an array and run 'uniq'.
{% for variant in product.variants %}
{% if variant.available %}
{% assign sizesList = variant.title | split: '/' | first | append: ', ' }}
{% endif %}
{% endfor %}
{% assign sizes = sizesList | split: ', ' %}
{{ sizes | uniq | join: ", " }}

paginating variant collection in liquid

In my collection template I'm showing product variants if applicable and products if not. This works very well, safe for the fact that it seems impossible to paginate. I've tried creating a custom array of variants/products, but paginate wants nothing to do with it.
Here's the code I'm currently using which works for outputting variants/products in a grid:
{% for product in collection.products %}
{% if product.variants.size == 0 %}
{% include 'product-card-grid2', max_height: max_height %}
{% else %}
{% for variant in product.variants %}
{% include 'product-card-grid2', max_height: max_height %}
{% endfor %}
{% endif %}
{% endfor %}
How do you paginate something like this? Essentially I'd like something like the excellent pimoroni.com.
Liquid seems exceptionally rigid in terms of array construction/manipulation.
I guess the alternative would be to output the list as json and then manually handling it in JS, but it would be nice if that's avoidable.
One page may show 200 and another may show 50. I'd like to normalize that to 20 per page - also to avoid hitting shopify's hard cap on collections.
You can't achieve this only with liquid.
You can paginate the products but the variants will throw the logic of.
So there is not a lot of you can do.
One hacky ways is to overwrite the paginate limit to something crazy to show all the products and create a JS paginate instead of a liquid one.
Example:
{% paginate collection.products by 9999 %}
{% for product in collection.products %}
{% if product.variants.size == 0 %}
{% include 'product-card-grid2', max_height: max_height %}
{% else %}
{% for variant in product.variants %}
{% include 'product-card-grid2', max_height: max_height %}
{% endfor %}
{% endif %}
{% endfor %}
{% endpaginate %}
But if you have a lot of products the load time will be crazy slow for the back-end to process the request.
Another option is to create a GraphQL query with the Storefront API and populate the page via JS. Bu this may be an overkill for such a thing.
Another option is to use some kind of an App that will pull the variants and create the pagination for you. ( via JS once again )
Last option is to describe this to your client and that you don't have an easy/optimised way to handle variants pagination because of Shopify limitations.
Concat only works on arrays. Luckily there are ways to handle that. Array doesn't have the slice method (only strings) so we need to construct that too. Modulo takes care of under/overflow.
At last, success:
{% comment %}
------------------------------------------------------------------------------
Consolidating the product list for later use.
NOTE: It will probably cap over 50 products!!!!!!!!!!!!!!!!!!!!!!!!!!!
Needs to be rewritten at some point to use page number to load only the needed
products.
{% endcomment %}
{% assign productList = "" | split: "" %}
{% assign counter = 0 %}
{% assign variantLimit = 30 %}
{% for product in collection.products %}
{% assign productList = productList | concat: product.variants %}
{% assign counter = counter | plus: product.variants.size %}
{% endfor %}
{% assign maxSize = productList.size %}
{% assign start = current_page | minus: 1 | times: variantLimit %}
{% assign end = current_page | times: variantLimit | minus: 1 %}
{% if end > maxSize %}
{% assign end = productList.size | modulo: variantLimit %}
{% endif %}
{% assign slice = "" | split: "" %}
{% for i in (start..end) %}
{% assign temp = productList[i] | where: "available", true %}
{% assign slice = slice | concat: temp %}
{% endfor %}
{% assign productList = slice %}
And this as product loop
{% for variant in productList %}
{% include 'product-card-grid2', max_height: max_height %}
{% endfor %}
Also remember the next button
{% unless end > maxSize %}
Next Page
{% endunless %}

Shopify How to output a product that doesn't have a collection

I am trying to output all the products in a menu up to a certain number than output the collection types when that number is reached. So far that works fine, however I don't know how to output a product if the user does not put the product in a collection, I would like the products not entered into a collection to be in a template that I can link to in the menu that outputs the products not in a collection.
Here is my code so far.
{% for collection in collections %}
{% if shop.products_count <= 5 %}
{% for product in collections.[collection.title].products %}
{% capture productLink %}{{ product.url }}{% endcapture %}
{{product.title}}
{% endfor %}
{% elsif shop.products_count > 5 %}
{% capture collectionLink %}{{ collection.url }}{% endcapture %}
{{collection.title}}
{% else %}
You have no Products
{% endif %}
{% endfor %}
I would probably start by making a collection that contains all products in your shop (a smart collection where Product price > 0).
Then on the page where you want to display the products that are not in any collection (except the one we just created), try something like this:
{% for product in collections.all.products %}
{% if product.collections.size <= 1 %}
This product is not in any collections other than collections.all
{% endif %}
{% endfor %}

SHOPIFY: How can I check if ANY variant is out of stock

I sell t-shirts with multiple variants.
I would like to check if ANY of those variants is "Out of Stock". Then if ANY of the variants is "Out of Stock" display a message. (the same message, not matter what variant is out of stock)
I know this works to check stock of the FIRST variant:
{% if product.variants.first.inventory_quantity <= 0 %}
<p style="color:#ff0000">This item is currently out of stock.
Order today, and your item will be shipped in 4-6 weeks.</p>
{% case product.variants.first.inventory_quantity %}
{% when '0' %}
{% else %}
{% endcase %}
{% endif %}
But this only checks the first variant. Any help here would be greatly appreciated. I am open to handling this in javascript or liquid, but am fairly new to both.
Here is one surefire way... and you can twist this into whatever pretzels you desire.
{% for variant in product.variants %}
{% if variant.inventory_quantity == 0 %}
<h1>Damn Dawg, this variant be out of stock</h1>
{% else %}
... whatever....
{% endif %}
{% endfor %}
It sounds like if you only want this message printed once, then it applies to the product and not the variants themselves. In this case, just set a variable to true if anything is out of stock and you can thus print your message just once. The limit:2 suggestion is nonsensical.
eg:
{% assign out_of_stock = false %}
{% for variant in product.variants %}
{% if variant.inventory_quantity == 0 %}
{% assign out_of_stock = true %}
{% endif %}
{% endfor %}
{% if out_of_stock == true %}
<h1>Damn Dog, we're out of beers</h1>
{% endif %}
In 2021 there is an HasOutOfStockVariants attribute for GraphQL.

Shopify If in collection then display this

I am trying to write a simple if statement, but always struggle with shopify's system.
Essentially I want it to do this:
{% if collection.product == 'discontinued' %}
This Product is Discontinued.
{% endif %}
If it's in this collection, then display this text/html. Otherwise it wouldn't display anything. This would be in the product.liquid template.
Any ideas?
This is what ended up working:
{% for c in product.collections %}
{% if c.handle == "discontinued" %}
This product is Discontinued
{% endif %}
{% endfor %}
You can create an array of the collections for a product using map on product.collections. This which will create a new array with your specified property, i.e. the handles of each collection.
You can then check if this new array contains the handle you want to work with.
{% assign productCollections = product.collections | map: "handle" %}
{% if productCollections contains 'your-collection-handle' %}
{% comment %} DoSomething {% endcomment %}
{% endif %}
So for your example:
{% assign productCollections = product.collections | map: "handle" %}
{% if productCollections contains 'discontinued' %}
This product is Discontinued
{% endif %}
You can map other fields if your case is different, such as the title.
I guess this will help any one, I have used in the sidebar of shopify website.
The current collection page will get checked by this below code.
<div class="row-fluid not-animated" data-animate="fadeInUp">
<div class="title">By Collections</div>
<form class="coll">
{% assign col_tags = collection.title %}
{% for collection in collections %}
<input type="radio" value="{{ collection.url }}" name="collections" {% if col_tags contains collection.title %} checked {% endif %} >{{ collection.title | escape }} <br/>
{% endfor %}
</form>
If I understand how liquid collections work in Shopify, you will need to iterate over all of your products.
You'd need to do something similar to this if you are working with collections directly:
{% for product in collection.product %}
{% if product.tags contains 'discontinued' %}
This product has been discontinued :(
{% endif %}
{% endfor %}
If you are just working with a single product you can probably just use the inner if liquid tag part.
References:
Collection.liquid
Product.liquid
You can indeed add discontinued products to a collection called discontinued.
When rendering a product, you could do as csaunders suggests, simply loop through all the products in the discontinued collection, and check if the id of the current product matches any of the products in that collection. If so, do what you must do. No need to use tags.