Create Multi-Level menu in Phalcon - phalcon

I'm trying to create a multi level menu in Phalcon framework from categories table. So far I did this:
MySql table categories:
id parent name count_post
1 0 level_1_name_1 0
2 1 level_2_name_2 0
3 1 level_2_name_3 0
4 2 level_3_name_4 60
5 2 level_3_name_5 30
6 0 level_1_name_6 0
7 6 level_2_name_7 0
8 6 level_2_name_8 0
9 7 level_3_name_9 80
10 7 level_3_name_10 90
I need to create a html like this:
<ul>
<li>
<a>level_1_name_1</a>
<ul>
<li>
<a>level_2_name_2</a>
<ul>
<li><a>level_3_name_4</a></li>
<li><a>level_3_name_5</a></li>
</ul>
</li>
<li>
<a>level_2_name_3</a>
</li>
</ul>
</li>
<li>
<a>level_1_name_6</a>
<ul>
<li>
<a>level_2_name_7</a>
<ul>
<li><a>level_3_name_9</a></li>
<li><a>level_3_name_10</a></li>
</ul>
</li>
<li>
<a>level_2_name_8</a>
</li>
</ul>
</li>
</ul>
In controller I get the data from model:
$menu = Menu::find();
$this->view->setVar('menu', $menu);
But how can I generate HTML in volt template to output like above? Thanks for any help!

Something like this might work for you:
Model
class Menu extends ModelBase
{
public $id;
public $parent;
public $name;
public $count_post;
public function getChilds()
{
$result = false;
if($this->id) {
$childs = self::find("parent = $this->id");
if(count($childs)) {
$result = $childs;
}
}
return $result;
}
}
View
{%- macro print_menu_level(menu_level_items) %}
{%- for menu_item in menu_level_items %}
{% if loop.first %}
<ul>
{% endif %}
<li>
<a>{{ menu_item.name }}</a>
{% set next_menu_level_items = menu_item.getChilds() %}
{% if next_menu_level_items %}
{{ print_menu_level(next_menu_level_items) }}
{% endif %}
</li>
{% if loop.last %}
</ul>
{% endif %}
{%- endfor %}
{%- endmacro %}
{{ print_menu_level(root_menu_items) }}
Controller
$rootMenuItems = Menu::find('parent = 0');
$this->view->setVar('root_menu_items', $rootMenuItems);
DISCLAIMER: That's just an example I didn't tested this_

Related

Out Of Stock Products Showing Incorrect Price - Shopify

I am using an app name "Multi country pricing" through which I am showing different prices by location. The app is using products variants for showing different prices by location. Now the problem is prices are showing incorrect when item is out of stock. I have done what I could do but nothing helped. Please help me sort this issue as it's so frustrating. Thanks in advance
Here is the screenshot of my product
reference screenshot
here's my product-template. liquid code
{% unless settings.breadcrumb_styles == 'none' %}{% include 'breadcrumb' %}{% endunless %}
<div itemscope itemtype="http://schema.org/Product">
<meta itemprop="image" content="https:{{ product.featured_image.src | product_img_url: 'grande' }}">
<span itemprop="name" class="hide">{{ product.title }}</span>
<meta itemprop="mpn" content="925872" />
<meta itemprop="brand" content="{{ product.vendor }}">
<meta itemprop="sku" content="{{ product.variants.first.sku }}">
<div itemprop="aggregateRating"
itemscope itemtype="http://schema.org/AggregateRating" style="display: none;">
Rated <span itemprop="ratingValue">3.5</span>/5
based on <span itemprop="reviewCount">11</span> customer reviews
</div>
<meta itemprop="description" content="{{ product.description | strip_html | truncate: 100 }}">
{% assign _lazyload = settings.use_lazyload %}
{% assign _product_layout = 'normal' %}
{% assign _main_class = 'col-12' %}
<div class="container">
<div id="col-main" class="page-product layout-{{ _product_layout }} slider-normal">
<div itemprop="offers" itemscope itemtype="http://schema.org/Offer">
<meta itemprop="url" content="{{ shop.url }}{{ product.url }}" />
{% if product.available %}
<link itemprop="availability" href="https://schema.org/InStock" />
{% else %}
<link itemprop="availability" href="https://schema.org/OutOfStock" />
{% endif %}
<div class="product">
<div class="product-content-wrapper">
<div class="row">
<div class="col-lg-7 col-md-6 col-sm-12 col-12">
{% include 'product-detail-image' %}
</div>
<div class="col-lg-5 col-md-6 col-sm-12 col-12">
<div id="product-info" class="product-info" data-stock="{{ section.settings.show_product_quantity }}">
<div class="product-info-inner">
{% include 'product-detail-discount-popup' %}
<div class="d-flex brand-label">
{% if section.settings.show_product_vendor and product.vendor != blank %}
<div class="product-vendor">{{ product.vendor | link_to_vendor }}</div>
{% endif %}
{% include 'product-label' %}
</div>
<h1 itemprop="name" content="{{ product.title }}" class="page-heading">{{ product.title }}</h1>
{% include 'product-detail-review' %}
{% include 'product-detail-price' %}
{% include 'product-detail-short-description' %}
{% include 'product-detail-sku-stock' %}
{% include 'product-detail-inventory' %}
{% include 'product-detail-deals' %}
{% if product.available %}
{% assign hide_default_title = false %}
{% if product.variants.size == 1 and product.variants.first.title contains 'Default' %}
{% assign hide_default_title = true %}
{% endif %}
<div class="group-cw clearfix">
{% form 'product', product, class:'product-form product-action variants' %}
{% include 'product-detail-variants' %}
<div class="qty-add-cart">
{% include 'product-detail-qty' %}
{% include 'product-detail-btn' %}
</div>
{% endform %}
{% include 'layout-people-in-cart' %}
{% include 'product-detail-order-shipping' %}
{% include 'product-detail-pre-order-btn' %}
{% include 'product-detail-wishlist-compare' %}
</div>
{% else %}
{% include 'product-detail-unavailable' %}
{% endif %}
{% comment %} {% include 'product-detail-short-description' %} {% endcomment %}
{% include 'product-detail-popup' %}
{% include 'product-detail-pre-order' %}
{% include 'product-secure-image' %}
{% if settings.show_social_sharing_product and settings.share_js != blank %}
{% include 'social-sharing' %}
{% endif %}
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
{% include 'product-toggle' %}
{% if section.settings.product_tab_review == 'below' and settings.product_review_option != 'none' %}
<div class="review-item" id="tab_review_tabbed">
<div id="tab-review" class="container">
{% include 'product-detail-review-content' %}
</div>
</div>
{% endif %}
{% for block in section.blocks %}
{% case block.type %}
{% when 'related-product' %}
{% include 'related-product' %}
{% when 'super-deals-product' %}
{% include 'super-deals-product' %}
{% when 'article' %}
{% include 'category-top-article' %}
{% else %}
{% endcase %}
{% endfor %}
</div>
{% if settings.enable_notify_product_page %}
{% include 'layout-notify' %}
{% endif %}
{% include 'product-template-script' %}
And here's my product-template-script. liquid code
<script type="application/json" id="VariantJson-{{ section.id }}">
[
{% for variant in product.variants %}
{
"id":{{ variant.id | json }},
"incoming": {{ variant.incoming | default: false | json }},
"unformat_incoming_date": {{ variant.next_incoming_date | date:"%d/%m/%Y" | json }},
"next_incoming_date": {{ variant.next_incoming_date | date: format: 'month_day_year' | json }},
"inventory_policy": {{ variant.inventory_policy | json }},
"inventory_quantity": {{ variant.inventory_quantity | json }}
}{% unless forloop.last %},{% endunless %}
{% endfor %}
]
</script>
<script type="text/javascript">
jQuery(document).ready(function($) {
{% unless template.suffix == 'redirect' %}AT_Main.stickAddToCart();{% endunless %}
AT_Main.deadLine_time();
AT_Main.delivery_time();
AT_Main.scrollToReview();
jQuery('.panel-product-accordion .panel-title > a').on('click',function (e) {
$('.panel-product-accordion .panel-collapse').removeClass('show');
$(this).parents('.panel-product-accordion').first().find('.panel-collapse').addClass('show');
})
});
// initialize multi selector for product
$(window).on('load', function () {
setTimeout(function(){
AT_Main.scareWidth();
}, 1000);
$(".qty-inner .qty-up").on("click", function() {
var oldValue = $("#quantity").val(),
newVal = 1;
newVal = parseInt(oldValue) + 1;
$("#quantity").val(newVal);
});
$(".qty-inner .qty-down").on("click", function() {
var oldValue = $("#quantity").val();
if (oldValue > 1) {
newVal = 1;
newVal = parseInt(oldValue) - 1;
$("#quantity").val(newVal);
}
});
var variantProd = JSON.parse(jQuery('#VariantJson-{{ section.id }}').html());
/* selectCallback */
var selectOptionsCallback = function(variant, selector) {
if (variant) {
var form = jQuery('#' + selector.domIdPrefix).closest('form');
for (var i=0,length=variant.options.length; i<length; i++) {
var radioButton = form.find('.swatch[data-option-index="' + i + '"] :radio[value="' + variant.options[i] +'"]');
if (radioButton.length) {
radioButton.get(0).checked = true;
}
}
}
var add_to_cart = '#add-to-cart';
var $price = '#purchase-' + selector.product.id.toString() + ' .detail-price';
if (jQuery('#product-info.product-info').data('stock')) {
if (variant) {
var variant_stock = variantProd.find(item => item.id === variant.id);
if (variant.available) {
$('.inventory-quantity').removeClass('hide');
if(variant_stock.inventory_quantity > 0){
$('.inventory-quantity .text .number').html('<span class="number">' + variant_stock.inventory_quantity + '</span>')
var _total_inventory_quantity = variant_stock.inventory_quantity + 5;
}
else{
$('.inventory-quantity .text .number').html('<span class="number">5</span>')
var _total_inventory_quantity = 10;
}
var _percent = (5/_total_inventory_quantity)*100;
$('.inventory-quantity .line2').css('width', _percent + '%');
}
else{
$('.inventory-quantity').addClass('hide');
}
}
}
if (variant && variant.available) {
jQuery("#stock").html('<span>{{"products.product.available" | t }}:</span><span class="stock"> {{"products.product.in_stock" | t }}</span>');
// Update variant image
if (variant.featured_image) {
var originalImage = jQuery(".slider-for-00 .slick-current .image-zoom");
var newImage = variant.featured_image;
var element = originalImage[0];
var groupImage = String(variant.featured_image.alt);
AT_Main.productPage_variantFilter(groupImage == 'null' ? 'none-group' : groupImage);
Shopify.Image.switchImage(newImage, element, function (newImageSizedSrc, newImage, element) {
jQuery('.slider-thumbs-00 img').each(function() {
var grandSize = jQuery(this).attr('src');
grandSize = grandSize.replace('small','2048x2048');
if (grandSize == newImageSizedSrc) {
jQuery(this).parent().trigger('click');
let w_thumb = jQuery('.slider-thumbs-00').outerWidth()
,w_track = jQuery('.slider-thumbs-00 .slick-track').outerWidth();
if ((w_track <= w_thumb || AT_Main.getWidthBrowser() < 768)) {
jQuery('.slider-thumbs-00 .slick-track').addClass('no-translate3d');
}else{
jQuery('.slider-thumbs-00 .slick-track').removeClass('no-translate3d');
}
return false;
}
});
});
}
// selected a valid variant
$(add_to_cart).removeClass('disabled').removeAttr('disabled').html('<span class="demo-icon icon-electro-add-to-cart-icon"></span>{{ "products.product.add_to_cart" | t }}'); // remove unavailable class from add-to-cart button, and re-enable button
if(variant.compare_at_price == null){
$($price).html('<span class="price">'+Shopify.formatMoney(variant.price, '{{shop.money_format}}')+'</span>');
jQuery(".brand-label .label-sale").addClass('hide');
}
else {
$($price).html('<del class="price-compare">'+Shopify.formatMoney(variant.compare_at_price, '{{shop.money_format}}') + '</del>' + '<span class="price-sale">' + Shopify.formatMoney(variant.price, '{{shop.money_format}}') + '</span>');
jQuery(".brand-label .label-sale").removeClass('hide');
}
{% if section.settings.enable_pre_order_button %}
$('.shopify-payment-button').removeClass('hide');
{% endif %}
/* Update currency */
{% if settings.currency_type == '2' %}
currenciesCallbackSpecial('#product-info span.money');
{% endif %}
}
else {
jQuery("#stock").html('<span>{{"products.product.available" | t }}:</span><span class="stock"> {{"products.product.out_stock" | t }}</span>');
$(add_to_cart).addClass('disabled').attr('disabled', 'disabled'); // set add-to-cart button to unavailable class and disable button
var message = variant ? "{{'products.product.sold_out' | t }}" : "{{'products.product.unavailable' | t }}";
$(add_to_cart).html(message);
{% if section.settings.enable_pre_order_button %}
$('.shopify-payment-button').addClass('hide');
{% endif %}
}
{% if section.settings.show_product_sku %}
if (variant && variant.sku ) { jQuery("#sku").html(variant.sku).parent().show(); }
else{jQuery("#sku").parent().hide(); }
{% endif %}
};
{% if product.variants.size > 0 %}
new Shopify.OptionSelectors("product-select-{{ product.id }}", { product: {{ product | json | remove:'\u003E' | remove:'\u003C' | remove:'\u00a0' }}, onVariantSelected: selectOptionsCallback, enableHistoryState: true });
{% endif %}
// Add label if only one product option
{% if product.options.size == 1 and product.options.first != 'Title' %}
$('.selector-wrapper:eq(0)').prepend('<label>{{ product.options.first | escape }}</label>');
{% endif %}
// Hide selectors if we only have 1 variant and its title contains 'Default'.
{% if product.variants.size == 1 and product.variants.first.title contains 'Default' %}
$('.selector-wrapper').hide();
{% endif %}
});
</script>

how to separate products from loop and put them in to division group by 2 in shopify

I have an products array {1,2,3,4,5,6,7,8}
I want to put them into div group by 2
I have a structure like this the problem is i got all products in one div I want to separate every two products
<div class="images">
images 1 to 8
</div>
I need structure like
<div class="images">
images 1 and 2
images 3 and 4
images 5 and 6
images 7 and 8
{% for media in product.media %}
<div class="image-block ">
<a href="{{ media.preview_image | img_url: product_image_zoom_size, scale: product_image_scale }}"
class=" product-single__thumbnail--{{ section.id }} even count-{{forloop.index}} "
data-thumbnail-id="{{ section.id }}-{{ media.id }}"> <img class="product-single__thumbnail-image" src="{{ media.preview_image | img_url: 'large', scale: 2 }}" alt="{{ thumbnailAlt }}"></a>
</div>
{% endfor %}
use logic this into your loop to open and close and HMTL element according to your need.
{% for media in product.media %}
{% assign data = forloop.index | modulo:2 %}
{% if data != 0 %}<div class="wrapper">{% endif %}
inner content goes here
{% if data == 0 %}</div>{% endif %}
{% endfor %}
Here is the working example of the same code:

How to display Shopify blogs in their own categories?

I'm new to Shopify. I have 5 blog categories (using multiple blogs) and I need to find a way to show each post in its own blog category. My code is currently populating the news articles and displaying the same ones in every category. How can I rewrite my for loop to loop through each category and display the articles only related to that category.
{% for article in blogs.news.articles %}
{% assign content = article.content | split: '[/email]' %}
<li class="widget-article">
<div class="widget-image">
<a href="{{ article.url }}">
<img src="{{ article.image | article_img_url: 'original' }}" alt="">
</a>
</div><!-- /.widget-image -->
<div class="widget-content">
<h3 class="widget-title">{{ article.title }}</h3><!-- /.widget-title -->
<p>
{% if content[1] %}
{{ content[1] | strip_html | truncatewords:15 }}
{% else %}
{{ content[0] | strip_html | truncatewords:15 }}
{% endif %}
</p>
</div><!-- /.widget-content -->
</li><!-- /.widget -->
{% endfor %}
Thanks
When you write "category" do you mean collection? Are you trying to associate a blog with a collection? If that's what you are trying to do then one way is to give the blog and collection the same handle.
then you could add your code to your collection template (I'd suggest as a snippet) and the snippet would start like:
{% assign blog = blogs[collection.handle] %}
{% if blog %}
<div class='blog-wrap'>...
{% for article in blog.articles %}

Block cart icon for certain customer tags on Shopify

I'm trying to block customers tagged with "Wholesale" from the cart icon but it seems to block everybody. I've also tried to add this tag markup but won't let me add in {% include %} without giving me some type of error.
{% unless customer.tag contains 'Wholesale' %}
{% include
<li class="site-nav__item">
<a href="/cart" class="site-nav__link site-nav__link--icon cart-link js-drawer-open-right" aria-controls="CartDrawer">
<span class="icon-fallback-text">
<span class="icon icon-cart" aria-hidden="true"></span>
<span class="fallback-text">{{ 'layout.cart.title' | t }}</span>
</span>
<span class="cart-link__bubble{% if cart.item_count > 0 %} cart-link__bubble--visible{% endif %}"></span>
</a>
</li> %}
{% endunless %}
Your include usage is wrong. It is used to load only snippet files. Just remove {% include and it's corresponding %} and you're good to go.
Also it's customer.tags.
{% unless customer.tags contains 'Wholesale' %}
<li class="site-nav__item">
<a href="/cart" class="site-nav__link site-nav__link--icon cart-link js-drawer-open-right" aria-controls="CartDrawer">
<span class="icon-fallback-text">
<span class="icon icon-cart" aria-hidden="true"></span>
<span class="fallback-text">{{ 'layout.cart.title' | t }}</span>
</span>
<span class="cart-link__bubble {% if cart.item_count > 0 %} cart-link__bubble--visible{% endif %}"></span>
</a>
</li>
{% endunless %}
This should probably be a comment - but I need to include a code example...
As HymnZ has mentioned - customer.tags will only work if the customer is signed in.
If the customer is not signed in, they will be treated as a 'regular' customer.
If you want to hide the cart button until any type of customer is logged in - you'd need to wrap everything in an {% if customer %}
Then, if you wish to check if the customer is wholesale, you'd use
{% unless customer.tags contains 'Wholesale' %}
So to put it all together:
{% if customer %}
{% comment %} Customer is logged in. {% endcomment %}
{% unless customer.tags contains 'Wholesale' %}
<li class="site-nav__item">
<a href="/cart" class="site-nav__link site-nav__link--icon cart-link js-drawer-open-right" aria-controls="CartDrawer">
<span class="icon-fallback-text">
<span class="icon icon-cart" aria-hidden="true"></span>
<span class="fallback-text">{{ 'layout.cart.title' | t }}</span>
</span>
<span class="cart-link__bubble {% if cart.item_count > 0 %} cart-link__bubble--visible{% endif %}"></span>
</a>
</li>
{% endunless %}
{% endif %}
* Edit *
Following on from Gino's comment:
It seems there are some inconsistencies with how Shopify handles tags. My first thought was case sensitivity, however, I couldn't quite remember if tags were case sensitive.
So I logged into my store - and tried to add 'Test' and 'test'.
Once I had added 'Test' - it appeared as 'Test', but I couldn't add 'test' - as Shopify regarded 'Test' and 'test' as the same (case insensitive).
However, in the ui - it showed 'Test' (case sensitive) - which threw me into thinking it is case sensitive (it was nearly 3am when I answered)
A quick google, I found this post from Tobi Lutke - the chap who wrote Shopify https://ecommerce.shopify.com/c/shopify-discussion/t/tag-use-6631 - please see the last comment: "Tags are always downcased automatically." ......
So of course - if the tag is downcased automatically, and you're checking against the string 'Wholesale' it won't work.

Trying to target only frontpage collection to apply a style(Shopify)

OK, so I had asked a few weeks ago on the Shopify forum(slow responses) how to target just one product on a collection page so that there is no hover affect, but now I only want to target the frontpage collection and not my many other collection pages on the site.
Theme is Masonry and here is a snipet of the code I'm working with:
{% if settings.prod_block_display contains 'hover' %}
{% unless forloop.index == 1%}
<div class="hoverinfo{% if forloop.index == 1 %}no-overlay {% endif %}">
<a href="{{ product_url }}">
<div class="info-box">
<div class="title">{{ product-block.title }}</div>
<div class="price">
{% if product-block.compare_at_price_max > product-block.price %}
<span class="previously">{{ product-block.compare_at_price_max | money }}</span>
{% endif %}
{% if product-block.price_varies %}<span class="from">{{ 'products.listing.from' | t }}</span>{% endif %}
<span class="actual">{{ product-block.price | money }}</span>
</div>
</div>
</a>
</div>
{% endunless %}
{% endif %}
{% if settings.prod_block_qv and no_quick_buy == false %}
{% unless forloop.index == 1 %}
<div class="quick-buy-row{% if forloop.index == 1%}no-overlay {% endif %}">
{{ 'products.listing.quick_view' | t }}
</div>
{% endunless %}
{% endif %}
The "{% unless forloop.index == 1%}
" codes are the effects I only want to apply to the frontpage. Any help would be great!
You can add {{ collection.handle }} to classes of product divs where you want to apply the effect. Then in styles.css.liquid add a style for the collection handle (collection.handle) class as required.
What #HymnZ was trying to point to was that you can do things like in styles.liquid.css:
.frontpage .hoverinfo{
display:none;
}
and then in your template:
<div class="{{collection.handle}}">
<div class="hoverinfo">
...
</div>
</div>
This is the easiest way to use this strategy. What I was trying to add on was that you can potentially also use prefixes and suffixes on classes that concatenate with handles to offer more functionality. In styles:
.hide{
display:none;
}
.frontpage-show{
display:block;
}
and then in your template:
<div class="hide {{collection.handle}}-show">
Since most browsers support stacked selectors you probably don't need the -hide, -show variants but they can be helpful depending on how your mind works these things out. The sample above can be used to show/hide things for a variety of circumstances. e.g. you could target elements for a variety of collections:
.frontpage-show,
.collection1-show,
.frontpage.hide, /* these second two are the stacked equivalents to the -show variants */
.collection1.hide{
display:block;
}
Of course you can also do much of this in liquid itself:
{% assign showFor = "frontpage,collection1" |split ','%}
{% if showFor contains collection.handle %}
<div>Something limited</div>
{% endif %}
or
{% assign showFor = "frontpage,collection1" |split ','%}
<div class="{% if showFor contains collection.handle %}conditional{% endif %}">
Something limited
</div>