Add a while loop like this pseudocode:
while(data-selector) do some-actions-that-hopefully-update-data-selector-and-terminate
Prevent infinite loops with a globally set loop limit.
See: #1525596: [Meta] Rules loops extended
Original description
I see in the issue queue that there was discussion of supporting both foreach() and while() loops (but not for(;;)). I can see how to put together a foreach() loop, that seems really well documented in various places. But I can't find any way to put together a while() loop.
My use case is simple - I fire an action when someone buys a particular kind of product (from Drupal Commerce). I do this by looping over the list of all the line_items in the person's order. But if someone buys multiples of an item, that isn't noted in a separate line_item. It's noted in a decimal "quantity" field. So ideally I'd like to have a while() loop on that quantity field, firing an action and decrementing each time.
I tried doing it with a list, which decrements and adds the same item to the list at the end of the loop. It doesn't work, presumably because variables in loops don't make it out of that scope. I tried doing it by making a rule fire itself at the end, but that causes a recursion problem which Rules (correctly) prevents.
So, what's the proper way to do this? And where should I add documentation on it?
Comments
Comment #1
dasjoafaik that feature isn't available in rules yet
Comment #2
ohthehugemanatee commentedI've been working on this all week... basically Rules thinks that my rules are recursive, so it blocks them. See the discussion at http://groups.drupal.org/node/187774 for detail.
What would it take to add this functionality? Seems like it would be great core Rules functionality - with a foreach and a while loop structure, it would be pretty close to a full-blown GUI based programming language!
Meanwhile I guess I'm going to do this with PHP instead.
Comment #3
dasjoa related request: #1269884: Inline rules support
Comment #4
dasjotagging
Comment #5
fagosomething like that would be best added as separate rules plugin. I'm not sure though it's something we need in rules core.
ad #4:
How would this new feature improve ux?
Comment #6
dasjowell UX might be misleading here, maybe we'd like to name it developer experience, site builder experience. but anyways, USING rules implies that you are building / developing something :)
Comment #7
fagoIndeed, still I think it's just a feature request. It's not making something simpler, it's extending functionality.
Comment #8
ohthehugemanatee commentedIt's extending functionality... but if you say it should be built in a separate plugin, I could throw the question back at you about lots of features in Rules.
Why are for() loops in Rules core? Why does it make sense to include one kind of loop in core, and require a separate module for the other kind? Somewhere there's a thread here where the decision was made to include for() AND do...while(), so it seems like a reversal to me.
In the end it strikes me as not too complicated. The user has to define the parameter that's supposed to change after each loop, and what the exit condition is for that parameter. We can avoid recursion by breaking the loop if the parameter is the same between two consecutive passes. If this is defined as an aspect of the loop itself, we don't have to make the variable available outside of the rule... just make the rule fire back an exit condition of 0 or 1 depending on how that parameter looks when it finishes, and repeat the loop until it returns 1.
This isn't as flexible as a full on loop... the recursion check limits usefulness somewhat. But it could handle basic loops just fine.
Comment #9
fagoA foreach() is necessary to be able to loop over lists, what was an often requested feature in rules 1.x. Up to now a While() was not often requested and I'd consider its usage "advanced". Thus, it should be in a separate module.
However, if many folks come up and state their need for that + it comes with a decent UX, I'd be happy to include the feature. (What doesn't mean I'm going to work on it. Maybe, maybe not.)
Comment #10
mitchell commentedComment #11
mesch commentedI've encountered this use case and my solution was to turn the number into a list by creating what is essentially a casting action:
For Commerce quantity, you would have to change the input to a decimal.
Comment #12
geek-merlin* added shorter description and link to meta issue.
* opened #1525582: Action: generate a list from an integer to get #11 into rules core
Comment #13
mitchell commentedSo, the patch in #1525582: Action: generate a list from an integer plus some docs would cover this feature?
If so, please change this to a docs issue or mark it as a duplicate, or move the other code here and mark that one as a duplicate.
Comment #14
geek-merlinNO.
there are while loops that can NOT be simulated with counter arrays.
for a classification see my meta issue #1525596: [Meta] Rules loops extended
where i elaborated now:
Comparison:
While-loops (which are guarded by some explicit condition) and tail recursion (which are guarded by a rules condition) are equivalent in expression strength.
Counter arrays are weaker, as the number of loops is calculated in advance. So they may cover many, but not all use cases.
I think we still need counter arrays because essentially a for-loop is often more readable than a while loop.
Comment #15
mitchell commentedLet's first do counter array loops in #1525582: Action: generate a list from an integer, then come back to this. And, let's keep tail recursion separate in: #1525588: Looping: add recursion or tail recursion.
@axel.rutz: I think I might be confusing for and foreach, but one of them is what we already have, and the other is what we could do with #1525582: Action: generate a list from an integer and a "count a list" action. The while will be something else to be developed here. Could you please sort this out more clearly?
Comment #16
atlasc1 commentedIs there any way we could just allow the user to specify the recursion limit? I think this would be a quick and entirely valid fix, and it would allow the user to tune rules to the particular application (i.e., a recursive limit of 2 might be a total joke for some people, whereas a recursion limit of 4 may be overkill for others). I feel the lack of such an option is really hurting the usability of rules.
Comment #17
mitchell commented#15: Sorting through these (tagged) issues has helped me understand the different methods, so nm the question.
Foreach is handled in #1525582: Action: generate a list from an integer, and #1525588: Looping: add recursion or tail recursion will require the same change as this issue to extend the recursion handler. I marked the tail recursion issue as a duplicate.
Comment #17.0
mitchell commentedadded short description
Comment #18
tr commentedWe're not going to be adding this feature into D7 Rules this late in the D7 lifecycle. New features should go into the 8.x-3.x branch first, and then be backported if there is interest from the community and participation by the community.
Postponed, because the maintainers won't be working on this. As stated above, the best way to get this into Rules is to implement it in an add-on module, then if there is demonstrated need we can consider this for core Rules.