Twig - Why does it not allow us to set object / array values? - arrays

I'm extremely confused by the decision of Twig to not allow setting values of arrays and object properties via set.
For example, the following code will error out:
{% set entry.depth = 1 %}
Will result in the error:
Unexpected token "punctuation" of value "." ("end of statement block" expected)
Also the following way will also error (which I know twig doesn't prefer to use):
{% set entry['depth'] = 1 %}
So this effectively means we're unable to change properties of objects and arrays. I quite frankly find this bizarre.
Can someone please explain the decision behind this? Maybe if I get a technical reason why it's not possible it might make it less baffling.
Edit: Thanks for the solution, I was more after the reasoning behind the fact you have to use merge rather than just simply being able to override variables.

Twig's a bit weird in this regard. You'll need to use the merge filter for this.
{% set entry = entry|merge({'depth': 1}) %}

Related

How to store value from loop in liquid to display it into the variable

I am solving here issue - If the customer comes via direct url to the product, I don't get the collection name in the form(because it's not contained in the url). How can I solve this please, so that the collection listed by foor loop gets into a variable and can be displayed at the bottom.
I am trying to display the result of the loop into a variable.
the collection.title is still blank in this case because when the for cycle ends it will lost all the data.
Any solution please?
I tried to assign the result of the for loop to a variable, but it failed. I had syntax problems. I thought of assigning a collection with index [1] here. Since the index [0] contains the generic name of the collection, I need to use [1] - technically I couldn't do that.
You can do something like:
{% assign myTitle = '' %}
{% for collection in product.collections reversed offset:1 %}
{% assign myTitle = collection.title %}
{% break %}
{% endfor %}
{{ myTitle }}
Obviously, it would be a good idea to add some if statements around the assignment to make sure the correct title gets assigned and later on if myTitle is not empty but at minimum, this should do the trick. Remember that this code is going to work well if the product has only one collection but this assumption usually is wrong.
EDIT: optionally you can add a break statement like I did to break it after the first iteration so it won't get reassigned. With this code we assume that the correct title is the title of the first collection in the loop (which could not be true)

Iteration of array in Liquid

I'm using an analog of Shopify and I'm stuck with syntax of Liquid.
I need to output in the template the field with an id product[product_field_values_attributes][][value]
So I need to write a loop to get the i value of this array.
I'm confused with this empty element in the brackets.
I've looked through the examples of loop syntax in Liquid but all of those arrays are simple and they are not my case.
For example, the Title field has id product[title] and in Liquid template i call this variable product.title and it works fine.
But all my tries to write a loop for this array failed.
Please, help to write a loop to get the values of the array stated above.
Try outputting the array directly onto the page using {{ product }} or {{ product[product_field_values_attributes] }} somewhere in the HTML. That will do a JSON-like string representation of the array. From there you can figure out what the keys for the array are.
I'm not sure what you're saying about the i value. You don't reference i anywhere else in your question. If you can clarify that then we can see if something can be done about it.

Is the Angular orderBy documentation wrong? Or am I confused?

I'm a bit confused after reading Angular's orderBy documentation:
In HTML Template Binding:
{{ orderBy_expression | orderBy : array : expression : reverse}}`
This shows orderBy being used with 3 additional parameters (reverse is listed as optional), but I cannot find any examples of it being used with more than 2, and when it is 2, it appears to be in the form {{ orderBy_expression | orderBy : expression : reverse}} (ommitting array)
array is defined as "The array to sort.", but what does that make orderBy_expression? Shouldn't that be the array the filter acts upon?
I was actually going to Improve this doc and modify this (what I assume is a documentation error), but it wasn't at all clear to me what exactly was generating the template binding example (the docs are generated with JavaDoc-like comments right in the .js)
So, hopefully this is a valid SO question:
Is the documentation in fact incorrect? Or am I somehow confused
Filters have 2 modes of use. Programmatically, as a function, in which case the first param IS the array to act upon, and inline with | where the array on the left hand side is the array to act upon. So while it may not be immediately clear that is what is going on, the docs are not incorrect. Not saying it shouldn't be cleaned up. It would certainly be nicer if they showed both modes and clearly explained it. But I still stand by it being "correct." And, as Mikke pointed out, the current style of explanation IS consistent through the docs.

Twig iterating though a non nested array ( mysql_fetch_array )

When I use the following code:
$members = array();
$members[] = array([id]=>"1", [name]=>"name") ; // mysql_fetch_array($row)
I can iterate with twig like this:
{% for member in members %}
<tr><td>{member.id}</td><td>{member.name}</td></tr>
{% endfor}
But I have more rows, and I don't want to allocate the entire array in the memory, it seems like a waste of resources to me.
So how do I loop through:
$members = array([id]=>"1", [name]=>"name");
Currently it seems to iterate each array key, in stead of the entire array:
{% for member in members %}
<tr><td>{member.id}</td><td>{member.name}</td></tr>
{% endfor}
outputs :
<tr><td>1</td></tr>
<tr><td>test</td></tr>
And I don't want to use: (unless there is no alternative)
return array( mysql_fetch_array($data) );
Your problem, as I understand it
You wish to iterate through a mysql resultset in twig, without loading the whole thing into php arrays.
What you have tried
Loading the first row into a nested array. This worked, but of course you only got the first record echoed in twig.
You're looking for a way to fetch the next row from twig, using only the array. This is not possible, because the array that you have has no connection to the mysql result pointer (besides having the data of the first record).
Possible solutions
Note that I have not tested any of the code examples.
Load the whole thing into php
If the resultset is reasonably small, like say below a 100 rows, you can just bite the bullet and load it completely into a nested array and pass that to twig. You'll have no problems iterating over it with a simple {% for %} loop like in your question.
Create a generator[docs]
This would require that you upgrade to at least php 5.5, which introduced this feature. Your code would look something like this:
return array( //I'm assuming this returns data to be passed to twig
"data" => function () use ($result) {
//$result is the returned value of mysql_query
yield mysql_fetch_row($result);
},
);
You can then do {% for row in data %} in your template.
Create an Iterator
On older versions, such as your 5.3.3, generators are not available. But that's only a shortcut to writing classes implementing the \Iterator[docs] interface. This involves some code, but you can create a generic class that you can construct using only a result pointer, and you can either foreach (in php) or for (in twig) over it, just like arrays or generators.
Switch to PDO
Depending on your project size this may be the best option (this is the one I recommend). It is very easy to start using PDO, and when you do a query it returns a \PDOStatement object, which implements \Traversable, meaning you can foreach or {% for %} over it - without writing any wrapper code. This was one of the main reasons why I switched to PDO from mysql_* (the other being prepared statements).
Create a twig extension
Just to mention this one. Probably the most involving (both time and code) solution is to create a while tag in twig, and the possibility to call mysql_fetch_* on a result pointer.

How can I do a complex IF statement in visualforce?

I'm totally new to this, the task has been thrown at me as important and I've never done anything like this before. I have been given a template containing roughly this:
<apex:column headervalue="Amount"><c2g:CODAFormatterController number="{!IF([some condition],[something],[something else])}"/></apex:column>
I've replaced the statements with condition/something/something else
is there a way to use a function as you'd do in javascript, so something like
number="getNumber(x);"
or do I have to chain some IF statements together somehow? Is there an IF...ELSE?
I don't know what a CODAFormatterController is as it returned 0 results on google.
Any advice would help, I'm afraid I've been thrown in at the deep end here!
The VForce inlines are functional, they must ultimately return a value, be that a simple value or an invocation point for a piece of server side code. They do not support imperative coding and they are being resolved server-side (long before JS comes into play). in that regard the IF(condition,valuetrue,valuefalse) is an equivalent to IF..THEN..ELSE..ENDIF
You are off course free to chain any number of functions provided there is no type mismatch, meaning your valuetrue could itself be a function, including being an IF function itself.
Usually when people encounter these kind of problems there is always a workaround by using a slightly different approach. It all depends on what you are trying to do here...
I stumbled upon this question while doing some work myself on visualforce pages. Anyway, if anybody else sees this question they should know that there's no need to use the complicated nested IF statements, now there is a CASE function available in Salesforce.
Documentation - https://developer.salesforce.com/docs/atlas.en-us.198.0.pages.meta/pages/pages_variables_functions.htm
{!CASE(Opportunity.StageName,
"Prospecting", "1",
"Needs Analysis", "2",
"Proposal/Price Quote", "3",
"default val")} <!-- the last one returns if none of the previous results matched -->

Resources