Smarty - foreach loop 10 times and stop - foreach

Im using the following Smarty code:
{foreach from=$entries key=i item=topic}
{if $topic.topic_style == question}
<li>
{$topic.title}
</li>
{/if}
{/foreach}
How can i do the {foreach} a maximum of 10 times and then stop?

You can use index and break function:
{foreach from=$entries key=i item=topic name=foo}
{if $smarty.foreach.foo.index == 10}
{break}
{/if}
{if $topic.topic_style == question}
<li>
{$topic.title}
</li>
{/if}
{/foreach}
Break function is described here:
Break in Smarty's / Dwoo's foreach

You could just use array_slice:
{foreach $entries|#array_slice:0:10 as $topic}
...
{/foreach}

Use index:
{foreach from=$entries key=i item=topic name=foo}
{if $smarty.foreach.foo.index < 10}
{if $topic.topic_style == question}
<li>
{$topic.title}
</li>
{/if}
{/if}
{/foreach}

This example uses index, you will get 11 results. See my notes
{foreach from=$entries key=i item=topic name=foo} // note (1)
{if $smarty.foreach.foo.index == 10} // notes (2.1, 2.2 and 2.3)
{php}break;{/php} // note (2.4)
{/if}
{if $topic.topic_style == question} // note (3)
<li>
{$topic.title}
</li>
{/if}
{/foreach}
Notes
(1) If you are not using the key, there is no need to define it
(2.1) If you use index the start of the loop is 0, using iteration instead the counter starts at 1, for simple incremental counting use iteration not index.
(2.2) While its fine you use ==, !=, and so on in smarty code, for readability its better to use eq, neq, is, is not, and, or. the list goes on, see the smarty documentation for a full list.
(2.3) I noted above about the 11 results, using index. This would occur in the example above simply because the number is 10, to have a 10 result the print the break, you would have needed to use 9 as the value.
(2.4) Use the smarty break instead, you do not necessarily need to write a smarty plugin, there are plenty of plugins available to install.
(3) Just as in PHP, you do not need to use quotation on variables, integers, constants or boolean values, a string is none of these and should be enclosed in quotations.
The old Revision
{foreach from=$entries item=topic name=foo}
{if $smarty.foreach.foo.iteration eq 10}
{break}
{/if}
{if $topic.topic_style eq "question"}
<li>
{$topic.title}
</li>
{/if}
{/foreach}
I have been rethinking this, and as a result I have figured out a way to skip out the need to break altogether, the loop will simply end at the last iteration. why i didnt think of this earlier i dont know but anyway here is the best way you can end a loop without breaking. lte and le both mean less than or equal too, just the same as PHP <=
You could also use neq (not equal to) and make the number 11 but if you have more results in the array it would simply skip iteration 11 and continue to the end of the array. If you only have 10 items in the array you can use any of the three ways, but for simplicity in this instance I would stick with the less than equal operators.
You will note now that this foreach loop is immensely cleaner to look at and understand
The Better Revision
{foreach from=$entries item=topic name=foo}
{if $smarty.foreach.foo.iteration lte 10 AND $topic.topic_style eq "question"}
<li>
{$topic.title}
</li>
{/if}
{/foreach}
Summary
In short, both ways will work, I noted an option above using the less than operator with index which would be the preferred way to evaluate.
However by switching over to iteration you allow for a more straight forward loop, you dont need to think about is that the right value, its simple, i want 10 results, so iteration eq 10.
Slight side track here: Another issue with index over iteration is index wont display a table correctly if you are using modulus, This is equally true in PHP programs. Using iteration with modulus will make your life easier. its the equivalent of setting a counter because the row index wont do what you need.
By using smarty operators rather than the PHP counterparts you can write a more readable template file. Remember that the templates are not meant for php logic, they are meant for the front end designers. To a PHP programmer operators become second nature but to a designer maybe be alien to their normal work.

If you don't want to write smarty plugin, you can do this too:
{foreach from=$entries key=i item=topic name=foo}
{if $smarty.foreach.foo.index == 10}
{php}break;{/php}
{/if}
{if $topic.topic_style == question}
<li>
{$topic.title}
</li>
{/if}
{/foreach}

All the above worked to a certain degree, but not exactly what I wanted.
Here's what worked for me.
I basically used the index property of foreach
{foreach $products as $product}
{if $product#index eq 3}
{break}
{/if}
<img src="..products/{$product.product_image}" alt="">
{/foreach}

Small extend in smarty to limit foreach.
On file : sysplugins/smarty_internal_compile_foreach.php
Add limit to original attributes :
public $optional_attributes = array('name', 'key','limit');
Add after $output = "<?php "; this >
if (isset($_attr['limit'])) {
$limit = $_attr['limit'];
$output .= "\n \$_limitCnt = 0; \n \$_limit = $limit; \n";
}
Add before $output .= "?>"; this >
if (isset($_attr['limit'])) {
$output .= "\n if (\$_limitCnt == \$_limit) { break; }";
$output .= "\n \$_limitCnt++;";
}
Use as usuall for each and add limit=# to limit your results.
Hope i helped.

Smarty 3 has another option, if at all possible then upgrading would be advisable. If you can't then speaking to the developer of your application.
{foreach $result_set as $result}
{if $result#iteration lte 10}
// just as in php both forms of access are available.
{$result.assoc_key}
{$result.0}
{/if}
{/foreach}
Its also worth noting that Smarty 3 has {break} built in too. However, if you are breaking from the foreach loop before the end and essentially discarding the remaining data then you might want to consider if its possible to LIMIT your sql queries.

Related

Thymeleaf check if variable equals a number

I have the following Thymeleaf code, I want "--" to be printed if taxHistory.landValue equals to zero, and the actual value to be printed if taxHistory.landValue is greater than 0
<td th:if"${taxHistory.landValue==0}" th:text="--"></td>
<td th:unless="${taxHistory.landValue>0}" th:text="|${taxHistory.landValue}|"></td>
However I'm getting the following error
org.thymeleaf.exceptions.TemplateProcessingException: Could not parse as expression: "0}"
What is the correct way of doing this in Thymeleaf?
There are multiple ways to do this, but you can use a ternary operator and print your string if your desired condition does not match:
<td th:text="${taxHistory.landValue > 0 ? taxHistory.landValue : '--' }">[taxHistory.landValue]</td>
You can use the > notation to keep the HTML formed properly. This assumes that a negative value would also print your string (you didn't mention behavior in that unusual case).

Porting POSIX regex to Lua pattern - unexpected results

I have hard time porting POSIX regex to Lua string patterns.
I'm dealing with html response from which I would like to filter checkboxes
that are checked. Particularly I'm interested in value and name fields of
each checked checkbox:
Here are examples of checkboxes I'm interested in:
<input class="rid-2 form-checkbox" id="edit-2-access-comments" name="2[access comments]" value="access comments" checked="checked" type="checkbox">
<input class="rid-3 form-checkbox real-checkbox" id="edit-3-administer-comments" name="3[administer comments]" value="administer comments" checked="checked" type="checkbox">
as opposed I'm not interested in this (unchecked checkbox):
<input class="rid-2 form-checkbox" id="edit-2-access-printer-friendly-version" name="2[access printer-friendly version]" value="access printer-friendly version" type="checkbox">
Using POSIX regex I've used following pattern in Python: pattern=r'name="(.*)" value="(.*)" checked="checked"' and it just worked.
My first approach in Lua was simply to use this: pattern ='name="(.-)"
value="(.-)" checked="checked"' but it gave strange results (first capture
was as expected but the second one returned lots of unneeded html).
I've also tried following pattern:
pattern = 'name="(%d?%[.-%])" value="(.-)"%s?(c?).-="?c.-"%s?type="checkbox"'
This time, in second capture content of value was returned but all
checkboxes where matched (not only those with checked="checked" field)
For completeness, here's the Lua code (snippet from my Nmap NSE script) that
attempts to do this pattern matching:
pattern = 'name="(.-)" value="(.-)" checked="checked"'
data = {}
for name, value in string.gmatch(res.body, pattern) do
stdnse.debug(1, string.format("%s %s", name, value))
end
I've used following pattern in Python: pattern=r'name="(.*)" value="(.*)" checked="checked"' and it just worked.
Python re is not POSIX compliant and . matches any char but a newline char there (in POSIX and Lua, . matches any char including a newline).
If you want to match a string that has 3 attributes above one after another, you should use something like
local pattern = 'name="([^"]*)"%s+value="([^"]*)"%s+checked="checked"'
Why not [^\r\n]-? Because in case there are two tags on one line with the first having the first and/or second attribute and the second having the second and third or just second (and even if there is a third tag with the third attribute while the first one contains the first two attributes), there will be match, as [^\r\n] matches < and > and can "overfire" across the tags.
Note that [^"]*, a negated bracket expression, will only match 0+ chars other than " thus restricting the matches within one tag.
See Lua demo:
local rx = 'name="([^"]*)"%s+value="([^"]*)"%s+checked="checked"'
local s = '<li name="n1"\nvalue="v1"><li name="n2"\nvalue="v1" checked="checked"><li name="n3"\nvalue="v3" checked="checked">'
for name, value in string.gmatch(s, rx) do
print(name, value)
end
Output:
n2 v1
n3 v3
(Updated based on comments) The pattern doesn't work when a line that doesn't have checked="checked" is before a line with checked="checked" in the input as .- expression captures unnecessary parts. There are several ways to avoid this; one suggested by #EgorSkriptunoff is to use ([^"]*) as the pattern; another is to exclude new lines ([^\r\n]-). The following example prints what you expect:
local s = [[
<input class="rid-2 form-checkbox" id="edit-2-access-comments" name="2[access comments]" value="access comments" checked="checked" type="checkbox">
<input class="rid-2 form-checkbox" id="edit-2-access-printer-friendly-version" name="2[access printer-friendly version]" value="access printer-friendly version" type="checkbox">
<input class="rid-3 form-checkbox real-checkbox" id="edit-3-administer-comments" name="3[administer comments]" value="administer comments" checked="checked" type="checkbox">
]]
local pattern = 'name="([^\r\n]-)" value="([^\r\n]-)" checked="checked"'
for name, value in string.gmatch(s, pattern) do
print(name, value)
end
The output:
2[access comments] access comments
3[administer comments] administer comments

Rails/Sphinx: search excerpts are also showing search conditions

This gives the results I was expecting:
result = Content.search("minerva", :conditions => {:publication_code => "12345678"})
result.first.element_type #=> "chapter"
result.first.excerpts.text #=> "outdated practice, The Owl of <span class=\"match\">Minerva</span> talks about the “unrealistic ‘Cartesian … major premise The Owl of <span class=\"match\">Minerva</span> details the innumerable combinations possible … concepts?” See The Owl of <span class=\"match\">Minerva</span>, p. 319. “Of course, ideally"
However: if I'm including search conditions that are literally present in the text, for instance the word "section" (which is a content element type) this is what I'm getting:
result = Content.search("minerva", :conditions => {:publication_code => "12345678", :element_type => "section"})
result.first.element_type #=> "section"
result.first.excerpts.text #=> "November 2001. The Owl of <span class=\"match\">Minerva</span>, p. 107. provides as follows: … foreign diplomatic or consular property, <span class=\"match\">section</span> 177 would place the United … source of leverage. In addition, <span class=\"match\">section</span> 177 could seriously affect our"
"Section", literally, is now also considered a match. I'm not getting what's the cause of this response.
Update to illustrate the problem some more:
Here's a query that finds a search term ("certification") near the term I'm using in the search conditions ("section", to limit my search to element_types that are sections).
result = Content.search("certification", :conditions => {:publication_code => "12345678", :element_type => "section"})
The text that gets returned is this (shortened to match following excerpts, and bold text mine):
result.first.text
[…] and operation of section 10 and the section 10 certification process. He noted […]
[…] object of the certification procedure introduced by section 10(1)(b) was not to […]
[…] domestic court. The certification procedure provided for by section 10 is similarly […]
Calling result.first.excerpts.text gives me the following. As you can see, everywhere in the text where either the term 'classification' or 'section' is found, it's set as a match.
" … and operation of <span class=\"match\">section</span> 10 and the <span class=\"match\">section</span> 10 <span class=\"match\">certification</span> process. He noted: … object of the <span class=\"match\">certification</span> procedure introduced by <span class=\"match\">section</span> 10(1)(b) was not to … domestic court. The <span class=\"match\">certification</span> procedure provided for by <span class=\"match\">section</span> 10 is similarly … "
The excerpts pane uses all query terms when generating output - which includes supplied conditions (as they end up being part of the Sphinx query - e.g. your second example, from Sphinx's perspective, is "minerva #publication_code 12345678 #element_type section").
An alternative is to have your own excerpter with just the query you want:
excerpter = ThinkingSphinx::Excerpter.new 'content_core', 'minerva', {}
excerpter.excerpt! results.first.text
The first argument when building the excerpter is the index name, the second is the search query to match against, and the third is options.
I think this is just a coincidence.
Try with a dataset that doesn't have section in the text to see if this also happens.

concatenate link_to with pipe

I want to concatenate a few links with a pipe. But all links are surrounded by an if-statement.
Example:
- if condition1
= link_to link1
- if condition2
= link_to link2
- if condition3
= link_to link3
If condition 1 and 2 are true, the result should be
link1 | link2
Any hints how to do this?
I would use smth like that for that purpose:
= [[l1, c1], [l2, c2], [l3, c3]].map{ |l, c| link_to(l) if c }.compact.join('|')
or
= [(link_to(l1) if c1),(link_to(l2) if c2),(link_to(l3) if c3)].compact.join('|')
The last one is a bit clumsy but it's a matter of taste. Both works perfectly for filtering out unnecessary links and joining the rest of them with |.
Though, if your conditions are non-trivial and you have quite a few of them it'd be better to move that logic outside of view into controller or some helper (depending on the situation).
And if you have some common method for testing whether you should display link or not, let's say show?(link) helper, then things become a bit nicer and you can do it like that:
= [l1, l2, l3, l4].map{ |l| link_to(l) if show?(l) }.compact.join('|')
or like that:
= [l1, l2, l3, l4].select{ |l| show?(l) }.map{ |l| link_to(l) }.join('|')

counting line numbers of a poem with nokogiri / ruby

I've struggling to try to do this with a simple regex but it's never been very accurate. It doesn't have to be perfect.
Source has a combination of and tags. I don't want to count blank lines.
Old way:
self.words = rendered.gsub(/<p> <\/p>/,'').gsub(/<p><br\s?\/?>|(?:<br\s?\/?>){2,}/,'<br>').scan(/<br>|<br \/>|<p/).size+1
New way (not working:
Tries to turn all the + into paragraphs, then throw it into nokogiri to count paragraph tags with more than 3 chars in them (I have no idea how? Counting 1 letter lines would be nice too, but this worked ok in javascript)
h = rendered
h.gsub!(/<br>\s*<br>/gi,"<p>")
h.gsub!(/<br>/gi,"<p>") if h =~ /<br>\s*<br>/
h.prepend "<p>" if !h =~ /^\s*<p[^>]*>/i
h.replace(/<p>\s*<p>/g,"<p> </p><p>")
Nokogiri::HTML(rendered)
# find+count p tags with at least 1-3 chars?
# this is javascript not ruby, but you get the idea
$('p', c).each(function(i) { // had to trim it to remove whitespaces from start/end.
if ($(this).children('img').length) return; // skip if it's just an image.
if ($.trim($(this).text()).length > 3)
$(this).append("<div class='num'>"+ (n += 1) +"</div>");
})
Other methods are welcome!
Example poem ( http://allpoetry.com/poem/7429983-the_many_endings-by-Kevin )
<p>
from the other side of silence<br>
you met me with change and a pocket<br>
of unhappy apples.</p>
<p>
</p>
<p>
<br>
we bled together to black<br>
and chose the path carefully to<br>
france.<br><br>
sometimes when you smile<br>
your radiant footsteps fall<br>
and all around us is silence:<br>
each dream step is<br>
false but full of such glory</p>
<p>
</p>
<p>
<br>
unhappiness never made a student of you:<br>
just two by two by two. now three<br>
this great we that overflows our<br>
heart-cave<br><br>
each jewel-like addition to the delicate<br>
crown. but flowers fall and dreams,<br>
all dreams, come to and end with death.</p>
Thank you!
For posterity, here's what I'm using now and it seems to be quite accurate. Non latin chars cause some problems sometimes from ckeditor, so I'm stripping them out for now.
html = Nokogiri::HTML(rendered)
text = html.at('body').inner_text rescue nil
return self.words = rendered.gsub(/<p> <\/p>/,'').gsub(/<p><br\s?\/?>|(?:<br\s?\/?>){2,}/,'<br>').scan(/<br>|<br \/>|<p/).size+1 if !text
#bonus points to strip lines entirely non-letter. idk
#d "text is", text.gsub!(/([\x09|\x0D|\t])|(\xc2\xa0){1,}|[^A-z]/u,'')
text.gsub!(/[^A-z\n]/u,'')
#d "text is", text
self.words = text.strip.scan(/(\s*\n\s*)+/).size+1

Resources