phalcon - how to loop through associative (array) model::find(); to output keys - phalcon

I want to loop through the columns of the model::find results.
What I thought was possible is to cast the returning object to an array to be able to loop throught the columns, but that does not work.
Here is my
Controller code:
<?php
class ManageController extends ControllerBase
{
public function indexAction()
{
$this->view->setVar("pages",(array) Pages::find());
}
}
And view code:
{% for key,value in pages %}
<p>key: {{key}}</p>
{% endfor%}
Any help would be usefull

Use this;
<?php
class ManageController extends ControllerBase
{
public function indexAction()
{
$this->view->setVar("pages", Pages::find());
}
}
And view code:
{% for page in pages %}
{# in this case the key is just "0,1,2,3..." #}
{# so we use the loop index (or loop.index0 for zero based) #}
<p>This is the page #{{ loop.index }}</p>
<p>{{ page.title }}</p>
{% endfor%}
But if you really need to loop through the keys too, use:
{% for key, value in items %}
Key: {{ key }}
Value: {{ value }}
{% endfor%}

Related

'size' on custom product metafield returns '0'

I'm building a component which checks the character count of a custom product metafield; if it's above 24 characters, the data is output as an accordion, otherwise the metafield content is printed in its entirety. The conditional below fails as the metafield size always returns 0, but I can see the content printing via the else statement so I'm certain the path is valid:
{% if product.metafields.custom.product_note.size >= 24 %}
<div class="product-note has--dropdown">
<span class="item__heading item__trigger">Product information</span>
<div class="item__content">
{{ product.metafields.custom.product_note }}
</div>
</div>
{% else %}
<div class="product-note">
<div class="item__content">
{{ product.metafields.custom.product_note }}
</div>
</div>
{% endif %}
I'm not sure it's relevant, but the product_note metafield is a multi-line text field. If anyone could point me in the right direction as to why size is failing to produce a value, I'd appreciate it massively.
You could always try working off the value contained in the metafield. It appears you are short-cutting by referring to just the combo of namespace and key, without actually saying: what is the length of the value stored at that namespace and key. Just a thought. You could at least try that.
Final answer, courtesy of #David Lazar's suggestion:
{% assign data_product_note = product.metafields.custom.product_note.value %}
{% if data_product_note.size >= 24 %}
<div class="product-note has--dropdown">
<span class="item__heading item__trigger">Product information</span>
<div class="item__content">
{{ data_product_note }}
</div>
</div>
{% else %}
<div class="product-note">
<div class="item__content">
{{ data_product_note }}
</div>
</div>
{% endif %}

Shopify: Display discount codes in new order email template

I want to display discount codes in new order email notification template, using this code:
{% for discount in discounts %}
{{ discount.code }} : {{ discount.savings | money_with_currency }}
{% endfor %}
It's displaying the discount amount but not the discount code name/title. It's returning blank even if the discount is manually entered in the back-end when creating an order.
How can I display discount codes no matter where it's entered i.e in admin or checkout?
In notifications you may access to discount_application object as explained here:
https://shopify.dev/docs/themes/liquid/reference/objects/discount-application
So (not tested), something like that might work:
{% for discount_application in discount_applications %}
{{ discount_application.title }} : {{ discount_application.value }}
{% endfor %}
HTH
You can get it from your cookies:
<div id="discount-message"></div>
<script type="text/javascript">
var codeCookieValue;
var getCookie = function(name) {
var value = "; " + document.cookie;
var parts = value.split('; '+name+'=');
if (parts.length == 2) return parts.pop().split(";").shift();
};
codeCookieValue = getCookie('discount_code');
</script>

What is correct way of inserting wagtail streamfield block dependencies into the template?

I rely on template inheritance system to insert extra_css and/or extra_js into my pages:
base.html:
<!DOCTYPE html>
<html lang="en">
<head>
<!-- Core CSS -->
{% block extra_css %}
{# Override this in templates to add extra stylesheets #}
{% endblock %}
</head>
<body>
{% block content %}{% endblock content %}
<!-- Core JS -->
{% block extra_js %}
{# Override this in templates to add extra javascript #}
{% endblock extra_js %}
</body>
</html>
page.html:
{% extends "base.html" %}
{% block extra_css %}
<link href="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.6-rc.0/css/select2.min.css" rel="stylesheet" />
{% endblock %}
{% load wagtailcore_tags wagtailimages_tags %}
{% block content %}
<div class="blog-post">
<!-- Custom HTML -->
{% block content %}
{% include_block page.body %}
{% endblock %}
</div><!-- /.blog-post -->
{% endblock %}
{% block extra_js %}
<script src="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.6-rc.0/js/select2.min.js"></script>
<script type="text/javascript">
// Custom JavaScript
</script>
{% endblock extra_js %}
This works great, thus extra css/js is indeed inserted where it belongs.
The problem arise if I need to use streamfield where one of its block templates need custom css/js. In this case custom resources are inserted along with the block, but not in the specified locations in the base.html.
For instance if extra_js from the page.html in the example above has been added to the block template instead, then select2 would complain about jquery not being present and it would be totally right, because it's inserted with the block, but not as intended after the Core JS libraries.
Loading jquery twice leads to other issues: https://stackoverflow.com/a/25782679/2146346
Other option is to load all block dependencies into the page, but it would fill pages with redundant resources as not every block from the streamfield might be used on the page.
Are there other options?
UPDATE: here’s a much better way that uses JavaScript to only load libraries once: https://github.com/FlipperPA/wagtailcodeblock/blob/main/wagtailcodeblock/templates/wagtailcodeblock/code_block.html#L6
I haven't come up with a way I like of doing this yet. The tools for doing this on the Wagtail editor side are nice. However, here's what I do for WagtailCodeBlock:
{% load static wagtailcodeblock_tags %}
{% spaceless %}
{# This is ugly, as it'll inject this code for each block, but browsers are smart enough to not load each time. #}
<script src="{% static 'wagtailcodeblock/js/prism.min.js' %}" type='text/javascript'></script>
<link href="{% static 'wagtailcodeblock/css/prism.min.css' %}" rel="stylesheet">
{% load_prism_theme %}
{% for key, val in self.items %}
{% if key == "language" %}
<script>
language_class_name = 'language-{{ val }}';
</script>
{% endif %}
{% if key == "code" %}
<pre class="line-numbers">
<code id="target-element-current">{{ val }}</code>
</pre>
<script>
var block_num = (typeof block_num === 'undefined') ? 0 : block_num;
block_num++;
document.getElementById('target-element-current').className = language_class_name;
document.getElementById('target-element-current').id = 'target-element-' + block_num;
</script>
{% endif %}
{% endfor %}
{% endspaceless %}
In this example, I load the JS/CSS assets in each, and let the browser settle it out. It also assumed jQuery is loaded at the parent level. However, it is also possible to use the JavaScript context to ensure things are only loaded once, which is my next step. For now, it isn't pretty but it works.
On the Wagtail editor side, there's the #property media. I wish there was something analogous on the rendered side:
class CodeBlock(StructBlock):
"""
Code Highlighting Block
"""
WCB_LANGUAGES = get_language_choices()
off_languages = ['html', 'mathml', 'svg', 'xml']
language = ChoiceBlock(choices=WCB_LANGUAGES, help_text=_('Coding language'), label=_('Language'))
code = TextBlock(label=_('Code'))
#property
def media(self):
theme = get_theme()
prism_version = get_prism_version()
if theme:
prism_theme = '-{}'.format(theme)
else:
prism_theme = ""
js_list = [
"https://cdnjs.cloudflare.com/ajax/libs/prism/{}/prism.min.js".format(
prism_version,
),
]
for lang_code, lang_name in self.WCB_LANGUAGES:
# Review: https://github.com/PrismJS/prism/blob/gh-pages/prism.js#L602
if lang_code not in self.off_languages:
js_list.append(
"https://cdnjs.cloudflare.com/ajax/libs/prism/{}/components/prism-{}.min.js".format(
prism_version,
lang_code,
)
)
return Media(
js=js_list,
css={
'all': [
"https://cdnjs.cloudflare.com/ajax/libs/prism/{}/themes/prism{}.min.css".format(
prism_version, prism_theme
),
]
}
)
class Meta:
icon = 'code'
template = 'wagtailcodeblock/code_block.html'
form_classname = 'code-block struct-block'
form_template = 'wagtailcodeblock/code_block_form.html'
I hope this gives you some ideas, and I'm all ears if you come up with a better way. Good luck.
Could you create another js block that you only add into the streamfield block template? So in base.html you'll have extra_js block as well as streamblock_js. You can have your jquery in extra_js and the extra dependency for the streamblock in streamblock_js. And if you have multiple custom css/js per streamblock on one page, you could add as many extra blocks in the base.html template to load all the dependencies. I'm not sure if this will work, but that's my idea.

Shopify variant swatches or radio buttons instead of dropdowns in Slate

Shopify's tutorial for color swatches isn't supported yet for Slate,
and the select callback referenced no longer exists in the code base. Is it possible to modify this tutorial to work on Slate themes to create radio buttons or swatches instead of a dropdown for selecting variants on the product template?
Yes. I was able to get this tutorial to work by modifying the code slightly. This workaround is only going to be relevant until the shopify tutorial is updated to correspond to Slate.
Follow the tutorial as per the directions.
When you get to the step "Locate your selectCallback function",
you will notice there is no selectCallback function in Slate. Yikes!
Instead find "_onSelectChange" in theme.js and add the code there.
This is the final function with the swatches code added:
/**
* Event handler for when a variant input changes.
*/
_onSelectChange: function() {
var variant = this._getVariantFromOptions();
this.$container.trigger({
type: 'variantChange',
variant: variant
});
if (!variant) {
return;
}
// BEGIN SWATCHES
var selector = this.originalSelectorId;
if (variant) {
var form = $(selector).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.size()) {
radioButton.get(0).checked = true;
}
}
}
// END SWATCHES
this._updateMasterSelect(variant);
this._updateImages(variant);
this._updatePrice(variant);
this.currentVariant = variant;
if (this.enableHistoryState) {
this._updateHistoryState(variant);
}
},
Then, once you've completed the tutorial, you will notice it's still not working. This is because the code you add to theme.liquid uses a class that is no longer on your variant Selects.
On product.liquid (this is a Section on most Slate themes) Add the class "single-option-selector" to your selects, like so:
{% unless product.has_only_default_variant %}
{% for option in product.options_with_values %}
<div class="selector-wrapper js">
<label for="SingleOptionSelector-{{ forloop.index0 }}">
{{ option.name }}
</label>
<select
id="SingleOptionSelector-{{ forloop.index0 }}"
class="single-option-selector"
data-single-option-selector
data-index="option{{ option.position }}">
{% for value in option.values %}
<option value="{{ value | escape }}"
{% if option.selected_value == value %}selected="selected"{% endif %}>
{{ value }}
</option>
{% endfor %}
</select>
</div>
{% endfor %}
{% endunless %}
Now, the tutorial should work as it's supposed to. I hope this helps someone out!

Wrap Phalcon flash messages in HTML element

Is there a possibility to wrap flash messages in an element? I want to have no html element at all when there are no messages and have an extra div containing all messages if there is any message.
It would be enough if I could at least get information whether there are any flash messages and then code it myself, but it seems to me that neither Phalcon\Flash\Direct nor Phalcon\Flash\Session allow you to access current message count or wrap messages in your own html element.
Just configure your flash service to just output the message:
$this->flash->setAutomaticHtml(false);
Also, when outputting a message, it's automatically echoed.
If you want to just return a string without echoing it to the output buffer use:
$this->flash->setImplicitFlush(false);
These methods aren't in the main documentation page, but you should always look at the class reference too, you might find very usefull information there :)
EDIT
To return only messages you use setAutomaticHtml to false, setImplicitFlush has nothing to do with it. Also to know if a message exists use something like this:
$this->flashSession->has('error');
I have ended with following code. I basically had to generate output myself.
<?php
$messages = $this->flashSession->getMessages();
if ( count($messages) > 0) {
?>
<div class="basic-bg">
<div class="main-column">
<div class="flash-messages">
<?php
foreach ($messages as $messageType => $messageArray) {
foreach ($messageArray as $message) {
echo "<div class=\"flash-$messageType\">$message</div>";
}
}
?>
</div>
</div>
</div>
<?php } ?>
I know this is an old thread, but how about implementing an extended class to make sure your message string is still correctly escaped?
This is the class I've used to implement Bootstrap 3 dismissable messages:
<?php
namespace Ext;
/**
* Extension to Phalcon Framework to implement Bootstrap 3 dismissable messages.
* Pass mappings of phalcon to bootstrap classes to construct
* #link https://docs.phalconphp.com/uk/latest/reference/flash.html Phalcon flash docs
* #author Kevin Andrews <kevin#zvps.uk>
*/
class FlashBootstrap extends \Phalcon\Flash\Session
{
/**
* Correctly escapes the message while building a Bootstrap 3
* compatible dismissable message with surrounding html.
* #param string $type
* #param string $message
* #return void
*/
public function message($type, $message)
{
$bootstrapCssClass = $this->_cssClasses[$type];
$errorType = ucfirst($type);
$bootstrapMessage = "<div class=\"alert alert-{$bootstrapCssClass} alert-dismissible\" role=\"alert\"><button type=\"button\" class=\"close\" data-dismiss=\"alert\" aria-label=\"Close\"><span aria-hidden=\"true\">×</span></button><strong>{$errorType}:</strong> {$this->getEscaperService()->escapeHtml($message)}</div>";
parent::message($type, $bootstrapMessage);
}
}
Also for completeness the initialisation for DI:
<?php
$di->set('flash', function() {
$bootstrapFlash = new Ext\FlashBootstrap(array(
'error' => 'alert alert-danger alert-dismissible',
'success' => 'alert alert-success alert-dismissible',
'notice' => 'alert alert-info alert-dismissible',
'warning' => 'alert alert-dismissible',
));
$bootstrapFlash->setAutoescape(false);
$bootstrapFlash->setAutomaticHtml(false);
return $bootstrapFlash;
});
This also has the advantage that the ->success() ->error() ->notice() and ->warning() helper methods will all go through this code and produce nicely formatted messages wrapped in the desired HTML.
{% if flash.has('notice')==true OR flash.has('success') %}
{% for notif in flash.getMessages('success') %}
<div class="notif_global success">
<div class="notif_global-title">Успешно</div>
<div class="notif_global-content">{{ notif }}</div>
<div class="notif_global-close ico_close"></div>
</div>
{% endfor %}
{% for notif in flash.getMessages('notice') %}
<div class="notif_global success">
<div class="notif_global-title">Сообщение</div>
<div class="notif_global-content">{{ notif }}</div>
<div class="notif_global-close ico_close"></div>
</div>
{% endfor %}
{% endif %}
{% if flash.has('warning')==true OR flash.has('error') %}
{% for notif in flash.getMessages('warning') %}
<div class="notif_global error">
<div class="notif_global-title">Предупреждение</div>
<div class="notif_global-content">{{ notif }}</div>
<div class="notif_global-close ico_close"></div>
</div>
{% endfor %}
{% for notif in flash.getMessages('error') %}
<div class="notif_global error">
<div class="notif_global-title">Ошибка</div>
<div class="notif_global-content">{{ notif }}</div>
<div class="notif_global-close ico_close"></div>
</div>
{% endfor %}
{% endif %}