Multiple Collections in Shopify - collections

I have 3 collections displayed on my page right now.
I want to have one pagination at the very bottom that will load the next 50 products for all 3 collections if there are any.
How can I do that?
this is my code:
{% paginate collections.mycollection1.products by settings.pagination_limit %}
<div style="clear:both;">
<h1>Title</h1>
{% assign products_per_row = "4" %}
{% assign limit = 50 %}
{% assign products = collections.mycollection1.products %}
{% include 'product-loop' with settings.collection_sidebar %}
{% include 'pagination' with settings.collection_sidebar %}
</div>
{% endpaginate %}
{% paginate collections.mycollection2.products by settings.pagination_limit %}
<div style="clear:both;">
<h1>Title</h1>
{% assign products_per_row = "4" %}
{% assign limit = 50 %}
{% assign products = collections.mycollection2.products %}
{% include 'product-loop' with settings.collection_sidebar %}
{% include 'pagination' with settings.collection_sidebar %}
</div>
{% endpaginate %}
{% paginate collections.mycollection3.products by settings.pagination_limit %}
<div style="clear:both;">
<h1>Title</h1>
{% assign products_per_row = "4" %}
{% assign limit = 50 %}
{% assign products = collections.mycollection3.products %}
{% include 'product-loop' with settings.collection_sidebar %}
{% include 'pagination' with settings.collection_sidebar %}
</div>
{% endpaginate %}

This is what you can do by using pagination. I'm assuming all the three collections are of the same size.
First, let us call the pagination for all the products in your store
{% assign total_products = collections.all.products_count %}
Since you are shuffling 4 products at a time, the number of pages to be paginated will be total products count / 4
{% assign loop_value = total_products | divided_by: 4 %}
Now start the pagination
{% paginate collections by loop_value %}
Now we need to get a display window to work for the 4 product displays
{% assign window = paginate.current_page %}
{% assign window_start = window | minus: 1 | times: 4 %} //4 is the number of products being displayed
{% assign window_end = window | times: 4 | plus: 1 %}
Now this is done, we start product display. This is the same syntax for all the 3 collections, just replace the collection handle as required.
{% for product in collections.mycollection1.products %} // mycollection1 is your collection handle
{% if forloop.index > window_start and forloop.index < window_end %}
{% include 'product-block' %}
{% endif %}{% endfor %}
And we end the pagination
{% if paginate.pages > 1 %}<div class="pagination align-right">
{{ paginate | default_pagination }}
</div>{% endif %}{% endpaginate %}
Some important things to note very carefully:
Unless you create "mycollection1/2/3" arrays dynamically from the code, this code works only for the first 50 products in those collections.
You can use paginate function only once in an entire page.

Related

Shopify Liquid show custom div after 5th product in collection

I would like to show a custom div after the 5th product in my collection in shopify. Does anyone know how I can do this in liquid?
End the end of the for loop for the products, put a conditional like
{% if forloop.index == 5 %}
<div></div>
{% endif %}
liquid for loop iteration
use limit/ offset
{% for item in array limit: 4 %}
item less than or equal to 4
{% endfor %}
{% for item in array offset: 4 %}
item greater than 4 (5)
{% endfor %}
or use forloop.index
{% for item in array %}
{% if forloop.index < 5 %}
do something for less than 5
{% else %}
do something greater than or equal to 5
{% endif %}
{% endfor %}

Using a counter in a Twig node template

Setting up a Drupal 8 custom view for Top 4 items, I want a different layout for item 1 than the remaining. I have override files for the custom view, but for this example I'm using base files to keep it simple.
In views-view.html.twig base file we have:
<div class="view-content">
{{ rows }}
</div>
In node.html.twig base file we have:
<div {{ content_attributes.addClass('content') }}>
{{ content }}
</div>
In node.html.twig I am aiming for something like:
{% if row_counter = 1 %}
output this markup / fields
{% else %}
do something boring with the other 3 items.
{% endif %}
I was able to set row_counter in the views-view.twig.html file:
{% for row in rows %}
{% set row_counter = loop.index %}
<div{{ row.attributes }}>
{{ row_counter }}
</div>
{% endfor %}
But I need to check against the value of {{ row_counter }} in the node.html.twig file....
What other properties are available in node.html.twig to check against its position in the list?
From the documentation
The loop variable
Inside of a for loop block you can access some special variables:
Variable Description
-----------------------------------------------------------------
loop.index The current iteration of the loop. (1 indexed)
loop.index0 The current iteration of the loop. (0 indexed)
loop.revindex The number of iterations from the end of the loop (1 indexed)
loop.revindex0 The number of iterations from the end of the loop (0 indexed)
loop.first True if first iteration
loop.last True if last iteration
loop.length The number of items in the sequence
loop.parent The parent context
edit: every variable know in the parent template is also known inside an include as context is passed by default. Only macro's don't know the parent's context
controller
<?php
require __DIR__ . '/../requires/propel_standalone.php';
echo $twig->render('tests/items.html', [
'items' => [
'Abc',
'def',
'ghi',
'jkl',
'mno',
'pqr',
'stu',
'vwx',
'z',
],
]);
items.twig
<!doctype>
<html>
<head><title>Test</title></head>
<body>
{% for item in items %}
{% include "tests/item.html" %}
{% endfor %}
</body>
</html>
item.twig
{% set order = 'Nothing to report' %}
{% if loop.first %}
{% set order = 'I\'m first' %}
{% endif %}
{% if loop.last %}
{% set order = 'I\'m last' %}
{% endif %}
{% if loop.index is even %}
{% set order = 'I\'m even' %}
{% endif %}
{% if loop.index is divisible by(5) %}
{% set order = 'I can be dived by 5' %}
{% endif %}
{% if loop.index is divisible by(3) %}
{% set order = 'I can be dived by 3' %}
{% endif %}
<div>
<b>{{ loop.index }}:</b>{{ order }} - {{ item }}
</div>
You could do something like this if a class is enough:
Your views template:
{% for row in rows %}
<div class="{% if loop.index == 1 %}first_element{% endif %}">
{{ row.content }}
</div>
{% endfor %}
And then just style it appropriately.

Shopify: how to add a class depending on collection

On a clients homepage they want 'all the products' listed with a load more button at the bottom.
There are four categories and each has a different color associated. I want to pull in all the products by date listed order and apply a class to the product depending on the category.
Currently I have
{% assign collection = collections.all %}
{% for product in collection.products %}
{% if collection.handle == 'clothing' %}
<div class="theme-red overview-item">
{% elsif collection.handle == 'pictures' %}
<div class="theme-green overview-item">
{% elsif collection.handle == 'posters' %}
<div class="theme-blue overview-item">
{% elsif collection.handle == 'other' %}
<div class="theme-beige overview-item">
{% else %}
<div class="theme-none overview-item">
{% endif %}
...
</div>
{% endfor %}
This is currently picking up as theme-none and that's because I'm calling collections.all?
How do I get all the products shown regardless of a collection and then apply do something as per the if/else statements like above.
Any ideas, I've tried to see if product.collections, collection.all_types with no happiness.
The reason you're getting theme-none is because your if statement checks if the collection's handle is 'clothing', 'pictures', etc. but you've set the collection to collections.all so it's never going to match any of those conditions.
What you want to do instead is check if the current product is within one of those collections. For example:
{% assign collection = collections.all %}
{% for product in collection.products %}
{% for c in product.collections %}
{% if c.handle == 'clothing' %}
{% assign theme = "red" %}
{% elsif c.handle == 'pictures' %}
{% assign theme = "green" %}
{% elsif c.handle == 'posters' %}
{% assign theme = "blue" %}
{% elsif c.handle == 'other' %}
{% assign theme = "beige" %}
{% else %}
{% assign theme = "none" %}
{% endif %}
{% endfor %}
<div class="theme-{{ theme }} overview-item">
...
</div>
{% endfor %}

Twig variable from one loop in another doesn't work

I have two loops in Twig. One is to check the stock level of a product, the other is to display some product options. I'm trying to create a variable from one loop which I can use inside the other to add a class name to a list(s).
I can't get that to work. Any help is more then welcome...
What I have is this:
{% set stockLevel = '' %}
{% for variant in product.variants %}
{% set stockLevel = variant.stock.level %}
{{ stockLevel }} // gives 1 0 1 1 (So all sizes except the second one are available)
{% endfor %}
{% for option in product.options %}
<ul class="optionslist">
{% if option.values %}
{% for value in option.values %}
<li class=" {% if stockLevel == 0 %}not_on_stock {% endif %}" >
<a class="item">etc...</a>
</li>
{% endfor %}
{% endif %}
</ul>
{% endfor %}
I believe there is something wrong the way you are handling your data. Right now, event if your variable was accessible in your second for loop, the value of it would be the one set lastly and your script would've failed anyway.
I can suggest a bit of a hacky way, feel free to improvise or use the idea behind it.
Say, we assign an empty array:
{% set variantsArray = [] %}
Then, we will use the filter merge to fill it with some dummy data(that's where your first loop comes into play)
{% for number in 1..5 %}
{% set variantsArray = variantsArray | merge([number]) %}
{% endfor %}
Then to be sure, we can access all the values there, we will just dump it out(that's where your second loop comes)
{% for index in 1..3 %}
{{ dump(variantsArray) }}
{% endfor %}
The following dump generated:
array:5 [▼
0 => 1
1 => 2
2 => 3
3 => 4
4 => 5
]
Hope you got the idea. Any suggestions are kindly welcome.
I am not sure what you are trying to do, how do you get your stock level from the variants? i think it is better to have a service function which help you get your stock level by product, the code would be cleaner :
controller action
$stock_helper = $this->container->get('stockhelper_service');
$stocklevel = $stock_helper->getStockLevelByProduct($product); //getstocklevelbyproduct handle your logic and return the stock level integer.
pass this variable to the twig.
One variable in your twig, no loops there, clean view, understandable code in your view and action.
Ok, I found the solution. The trick is to "connect" both values. Like so:
{% for option in product.options %}
{% if option.values %}
<ul id="product_configure_option2_{{ option.id }}" data-id="{{ option.id }}">
{% for value in option.values %}
{% set stock = null %}
{% for variant in product.variants **if variant.title == (option.title ~ ': ' ~ value.title)** %}
{% set stock = variant.stock %}
{% endfor %}
<li value="{{ value.id }}"{% if not stock.available %} class="not_on_stock"{% endif %}>{{ value.title }}</li>
{% endfor %}
</ul>
{% endif %}
{% endfor %}

Twig compare two values in different arrays

First of all I'm learning Twig.
I was wondering if it is possible wit Twig to compare two different values from different arrays/lists?!
I have two list of items I call them like so:
{% if page.cart %}
{% for product in page.cart.products %}
{{ product.id }}
{% endfor %}
{% endif %}
And:
{% if products %}
{% for product in products %}
{{ product.id }}
{% endfor %}
{% endif %}
I want to compare both product.id's so I can create a new statement. Is there any way to compare both values? The idea is to check if an id is present in page.cart.products and if so then do something.
I want to create a new statement to display some info. Something like so:
{% if page.cart %}
{% for product in page.cart.products %}
{% set cartId %}{{ product.id }}{% endset %}
{% endfor %}
{% endif %}
{% if products %}
{% for product in products %}
{% set listId %}{{ product.id }}{% endset %}
{% endfor %}
{% endif %}
{% if cartId == listId %}
.... do this ....
{% endif %}
Any help greatly appreciated!
You can loop over one array and check if the id is present in the second one. If it's there, you can do something.
{# In case you want to store them, you can do so in an array #}
{% set repeatedIds = [] %}
{% for productCart in page.cart.products if page.cart %}
{% for product in products if products %}
{% if productCart.id == product.id %}
<p>This id -> {{ product.id }} is already in page.cart.products</p>
{% set repeatedIds = repeatedIds|merge([product.id]) %}
{% endif %}
{% endfor %}
{% endfor %}
{{ dump(repeatedIds) }}
It's a very basic search algorithm and the cost is quadratic. Obviously, there are more efficient ways to look for an element in an array (though more complicated to implement).
If the amount of products you have to deal with is not very big, you could use this solution. However, if you have, let's say, more than one hundred products in each array (or you feel that the algorithm is slowing down your loading time), you could do this process in the controller using more sophisticated methods and PHP and just pass the result to the template.
Hope it helps.

Resources