Interesting developments with 2.9! However, the new conditional parsing does break a little something I was using quite a bit, specifically with my add-on Low Search. Take this bit of template, for example:
{exp:low_search:form query="{segment_2}"}
...
<select name="category[]" multiple>
{exp:channel:categories style="linear"}
<option value="{category_id}"{if category_id IN ({low_search_category})} selected{/if}>
{category_name}
</option>
{/exp:channel:categories}
</select>
...
{/exp:low_search:form}The main issue here is the special IN conditional. This is a way of working around EE’s parse order, specifically the way it handles nested tags. Here the low_search:form tag is parsed first, then the channel:categories tag is parsed. The {low_search_category} variable contains a pipe-separated list of category IDs, and is set by the outer tag.
For example, say {low_search_category} equates to 1|2|3. Then the conditional will look like this when the outer tag parses its variables:
{if category_id IN (1|2|3)} ... {/if}Low Search then rewrites that conditional to:
{if category_id == 1 OR category_id == 2 OR category_id == 3} ... {/if}…making it a valid (advanced) conditional, which got parsed after tags were processed.
This allowed me to have conditionals for multi-valued variables that were known to an outer tag, but needed to be passed on to an inner tag, and worked a treat. I could even write a NOT IN conditional and rewrite that to {if var != 1 AND var != 2 …}.
Needless to say, the new conditional parser breaks this. I’m hoping you will help me making this work in 2.9 in some way or another.
Oh my.
Low: If we came up with a solution that required you to replace the IN, is that even an option?
Robson: Have to be careful what path that puts us on; not everyone writing templates is a programmer. Some struggle with what most programmers would consider relatively simple conditionals.
Robson: Not quite. All the other operators are standard PHP operators or math equations. In PHP this is doesn’t work:
if ('some-string' IN ('foo', 'bar', 'lorem', 'ipsum'))Basically, the IN conditional mimics MySQL’s “WHERE foo IN (1,2,3)” syntax or is similar to PHP’s “if (in_array($foo, [1, 2, 3]))”. IN inherently is not a valid operator in PHP, so some rewriting must be done.
Of course, I’d appreciate if the IN conditionals could stay intact. I know that Mark Croxton also uses this syntax. FWIW, this is the code I use to rewrite the conditionals:
function low_prep_in_conditionals($tagdata = '')
{
if (preg_match_all('#'.LD.'if (([\w\-_]+)|((\'|")(.+)\\4)) (NOT)?\s?IN \((.*?)\)'.RD.'#', $tagdata, $matches))
{
foreach ($matches[0] AS $key => $match)
{
$left = $matches[1][$key];
$operand = $matches[6][$key] ? '!=' : '==';
$andor = $matches[6][$key] ? ' AND ' : ' OR ';
$items = preg_replace('/(&(amp;)?)+/', '|', $matches[7][$key]);
$cond = array();
foreach (explode('|', $items) AS $right)
{
$tmpl = preg_match('#^(\'|").+\\1$#', $right) ? '%s %s %s' : '%s %s "%s"';
$cond[] = sprintf($tmpl, $left, $operand, $right);
}
// Replace {if foo IN (a|b|c)} with {if foo == 'a' OR foo == 'b' OR foo == 'c'}
$tagdata = str_replace(
$match,
LD.'if '.implode($andor, $cond).RD,
$tagdata
);
}
}
return $tagdata;
}I know, @Low. I actually use it a lot on SQL queries.
And I know this isn’t an standard. To me, this looks like more when you have consecutive cases on a switch statement. I really don’t know if you should keep only the solutions already available on PHP. The point of a template language is to make the templates easier to code and the IN is easier than several == and OR.
But my ideas are usually not common sense. I have a completely different background then a programmer. I’m a designer first.
If I had to code it, my idea would be to:
category_id IN (1|2|3)
Parse the category_id, split by IN, then add | around both:
And looks for the position of the first item on the second.
But I’m insane! You’ll probably find this more fun than useful.
Not really a productive comment I know, and I can’t even identify fully why, but it sure is weird to me to mimic a SQL comparison operator in a conditional.
Edit: actually I think that’s simply the reason. Conditionals don’t use WHERE, they use programmatic IF, and where would we stop? LEAST(), GREATEST(), COALESCE()?
It’s not really about the syntax of the conditional, but more about the problem it solves, Derek. I have a list of values in an outer tag that needs to be used by a conditional in an inner tag. The multiple values are, like EE parameters, separated by pipes, but this doesn’t work:
{if 'lorem' == 'foo|bar|lorem|ipsum'}Instead, I need to write this:
{if 'lorem' == 'foo' OR 'lorem' == 'bar' OR 'lorem' == 'lorem' OR 'lorem' == 'ipsum'}However, when you write the template, there’s no way to know all the options. Let alone that it just looks obtuse, too. So I set out to solve the problem by thinking up some way that I would like to write such a conditional, and that it would still be picked up by the native template parser. PHP’s equivalent would be, as I said earlier, in_array(). IN array. So, logically…
{if 'lorem' IN ('foo', 'bar', 'lorem', 'ipsum')}…made a lot of sense for me.
Still, the main issue here is allowing multivalued vars to be used in conditionals.
Throwing this out there, what if it was this:
{if 'lorem' find 'foo|bar|lorem|ipsum'}{if find 'lorem' in 'foo|bar|lorem|ipsum'}Also, isn’t there a hidden “feature” in EE right now that lets you do this, but only for logged in member group checking.
{if in_group(1|2|3)}Which could be changed to
{if 'lorem' in_group('foo|bar|lorem|ipsum')}I don’t think it’s a syntax issue. I guess Derek is afraid of what else can come by this door if they open it.
And you shouldn’t mention in_group, Brian. From the Template.php, line 3343:
// Member Group in_group('1') function, Super Secret! Shhhhh!
if (preg_match_all("/in_group\(([^\)]+)\)/", $str, $matches))
{Also, isn’t there a hidden “feature” in EE right now that lets you do this, but only for logged in member group checking.{if in_group(1|2|3)}
Yes, which throws an error in 2.9 now.
Anyhoo, I think it would be very useful to allow some sort of in_group or in_array or in_whatevs conditional checking.
I’ve used a similar custom conditional to what @low has in LS for this same issue.
Especially dealing with form state re-populations for arrays of elements, there’s really no other way to do it (without dipping into php in the template).
The specific syntax I settled on was slightly different, but solved the same issue :
{if '{var_one}' exists_in '{piped_list}'} ..Which is the same conceptually as the example @low provided. Further I extended that to take the next obvious step and allow array to array comparisons, which accounted for reordered elements :
{if '{array_one}' matches '{piped_list}'} ..and variants for the obvious cases that opens up (is subset, superset, exclusive, etc..). All these would render down to the appropriate EE standard conditionals before parsing.
I’m not saying these extended situations should be handled, but the base example @low provided is a very common one, and does deserve a native solution.
No I completely get the reason for wanting it, and even the reasons for settling on the syntax you did. Pascal’s going to be popping in with our proposed solution, and my comment is really only beneficial in the context of a post-mortem, which I’ll not derail this thread with now. I’ll start a new thread after we’ve nailed this one, as I think it’s a very important conversation to have.
Packet Tide owns and develops ExpressionEngine. © Packet Tide, All Rights Reserved.