reggi / shopify

Just a place to track issues and feature requests that I have for shopify
3 stars 1 forks source link

Products global #5

Closed reggi closed 9 years ago

reggi commented 9 years ago

I originally posted this here https://github.com/Shopify/liquid/issues/438. The discussion should be followed there.

We should be able to do this:

{{ products["austin-leaf-map-poster"].url }}

But we (developers) don't have access to a products global.

Instead of having access to the global. I have to hack something together like this:

function.get_product.liquid

{% assign return = false %}
{% if pass_product_handle and collections["all"] %}
  {% for product in collections["all"].products %}
    {% if pass_product_handle == product.handle %}
      {% assign return = product %}
    {% endif %}
  {% endfor %}
{% endif %}

usage.liquid

{% assign pass_product_handle = "austin-leaf-map-poster" %}
{% include "function.get_product" %}
{% assign product = return %}
{{ product.url }}

{% assign pass_product_handle = "boston-leaf-map-poster" %}
{% include "function.get_product" %}
{% assign product = return %}
{{ product.url }}

It can't be good for Shopify to have to interpret this loop every time I need product information, no?

rickydazla commented 9 years ago

@reggi I just modified your snippet to use IDs instead of handles, which are not quite as set-in-stone

{% assign return = false %}
{% if pass_product_id and collections["all"] %}
  {% for product in collections["all"].products %}
    {% if pass_product_id == product.id %}
      {% assign return = product %}
    {% endif %}
  {% endfor %}
{% endif %}
reggi commented 9 years ago

@rickydazla With the way that I write "functions" in liquid we could have this.

function-get-product.liquid


{% comment %}
arguments:
  - pass_product_id
  - pass_product_handle
  - pass_collection_handle
{% endcomment %}

{% assign return = false %}
{% if pass_product_id or pass_product_handle %}
{% if pass_collection_handle %}
{% if collections[pass_collection_handle] %}

{% for product in collections[pass_collection_handle] %}

  {% if pass_product_id %}
    {% if pass_product_id == product.id %}
      {% assign return = product %}
    {% endif %}
  {% elsif pass_product_handle %}
    {% if pass_product_handle == product.handle %}
      {% assign return = product %}
    {% endif %}
  {% endif %}

{% endfor %}

{% endif %}  
{% endif %}
{% endif %}

Then use it both ways:

{% assign pass_collection_handle = "all" %}
{% assign pass_product_handle = "my-product" %}
{% include "function-get-product" %}
{% assign product = return %}

or

{% assign pass_collection_handle = "all" %}
{% assign pass_product_id = 6792163 %}
{% include "function-get-product" %}
{% assign product = return %}

Which just got me thinking to type checking which is possible like this

function-typeof.liquid

{% assign return = false %}
{% assign variable = function-typeof %}
{% assign variable-to-string = variable | strip %}
{% assign variable-to-intiger = variable | plus: 0 %}
{% if variable-to-string == variable %}
  {% assign return = "string" %}
{% elsif variable-to-intiger == variable %}
  {% assign return = "intiger" %}
{% else %}
  {% assign return = "undefined" %}
{% endif %}

function-get-product.liquid

{% assign return = false %}
{% assign the-product = function-get-product %}
{% if the-product %}
  {% include "function-typeof" with the-product %}
  {% assign type = return %}
  {% if type and type == "string" %}
    {% assign pass_product_handle = the-product %}
  {% if type and type == "intiger" %}
    {% assign pass_product_id = the-product %}
  {% endif %}
  {% if pass_product_id or pass_product_handle %}
  {% if pass_collection_handle %}
  {% if collections[pass_collection_handle] %}
  {% for product in collections[pass_collection_handle] %}
    {% if pass_product_id %}
      {% if pass_product_id == product.id %}
        {% assign return = product %}
      {% endif %}
    {% elsif pass_product_handle %}
      {% if pass_product_handle == product.handle %}
        {% assign return = product %}
      {% endif %}
    {% endif %}
  {% endfor %}
  {% endif %}  
  {% endif %}
  {% endif %}  
{% endif %}

Which would allow you to do this:

{% assign pass_collection_handle = "all" %}
{% include "function-get-product" with 1231231 %}
{% assign product = return %}

Ahh, the joys of liquid! sigh

reggi commented 9 years ago

@rickydazla The thing is that the only way we have to access any of the existing globals, like that collection all your using is through the handle.

rickydazla commented 9 years ago

@reggi indeed and that is also nonsensical since the Collection / Page / Whatever handle can change while ID remains...

gavinballard commented 9 years ago

Hey guys, this is awesome work.

Just wondering and wanted to check - will this method be limited by the 50 item pagination restriction?

rickydazla commented 9 years ago

@gavinballard I believe the pagination restriction on Collections is now 1000*, but yes... Which is why global(s) would be oh so nice.

(*) I will try to track down a ref for this

rickydazla commented 9 years ago

@reggi apologies for going somewhat off-topic, but @gavinballard see here re: 1000 limit. It's such a seemingly minor and singular thread that you can be forgiven for missing it. I was gobsmacked when I saw it in the Partners forum in another discussion that remains to be officially confirmed... Working with Shopify is a trip sometimes, albeit generally a good one!

reggi commented 9 years ago

@rickydazla @gavinballard How would you iterate over a global (edit: that is batched into chunks of 50 or 1000, there's no specific endpoint for a global to be paginated with)? It wouldn't be the same as pagination. I'd like to see what happens if I were to create 1001 collections or page then get the .size of that global.

The proposed products["handle"] or products.handle doesn't have to be an object that you can loop or iterate, think of it more as a query. For instance I found out yesterday (the hard way) you can't iterate over blogs, it doesn't error and nothing happens.

This doesn't work.

{% for blog in blogs %}
{% endfor %}

In this respect blogs the global isn't an object with all blogs in it, it's variable that allows you to query for a specific blog via handle blogs["handle"] or blogs.handle.

As for the collections restriction

{{blogs.size}} // EmptyDrop 
{{pages.size}} // EmptyDrop 
{{collections.size}} // 85

This 85 for collections.size may have something do with that 1000 limit @rickydazla is talking about. But the proposed products global would be to expensive to be a full object and would act more like pages and blog.

rickydazla commented 9 years ago

@reggi I assume @gavinballard is asking whether pagination limit would impact on your function-get-product.liquid snippet, which it would, but perhaps not as much as he (may) think..

reggi commented 9 years ago

Oh yes! If you use that snippet, your limited you can only store 50 products per collection. The only way I have of beating that limit is storing collection handles in a linklist like this pullProducts1, pullProducts2, pullProducts3. Then you can loop over collections with 50 products in them.

I did this for blogs the other day. The same idea can be done for the product snippet.

{% for pressBlog in linklists["press-blogs"].links reversed %}
  {% if blogs[pressBlog.title] %}
    {% for local in blogs[pressBlog.title].articles %}
      <div class="col-xs-12 col-sm-4 col-md-2">
        <a href="{{ local.metafields.press.url }}" class="thumbnail" data-original-title="{{ local.metafields.press.vendor }}" target="_blank">
          <img src="{{ local.metafields.press.image | asset_url }}" alt="{{ local.metafields.press.vendor }}">
        </a>
      </div>
    {% endfor %}
  {% endif %}
{% endfor %}

In this case it's helpful to use the API to create your blog or your collection. This is all way off topic, and quite hacky.

This has to also be pretty harsh on Shopify's servers :crying_catface: `¯_(ツ)/¯`

gavinballard commented 9 years ago

@reggi @rickydazla Thanks for the clarification guys. Good to know about the 50 / 1000 limit, although I was sure I'd recently bumped up against a 50 limit (that may have been for blog articles though).

reggi commented 9 years ago

This has been implemented and works.

{{ all_products["product-handle"].title | json }}
rickydazla commented 9 years ago

I am also very grateful, although I wonder if {{ all_products[the-product.id].title }} might be better since N.E. User might decide they want holstee.com/products/manifesto-poster instead of holstee.com/products/holstee-manifesto-poster and that could potentially screw the :poodle:...

reggi commented 9 years ago

@rickydazla None of the global objects in shopify like blogs["blog-handle"] and collections["collection-handle"] work with id's. I agree with you it's better to have something immutable, but I think it's too big of an ask for shopify. What they'd need to do is detect if its a string and if it is it's a handle if it's an int it's the id, that way the lookup would be backward compatible with the existing system.