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

dasjo’s picture

Version: 7.x-2.0 » 7.x-2.x-dev
Category: support » feature

afaik that feature isn't available in rules yet

ohthehugemanatee’s picture

I'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.

dasjo’s picture

dasjo’s picture

Issue tags: +#d7ux

tagging

fago’s picture

something 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?

dasjo’s picture

well 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 :)

fago’s picture

Issue tags: -#d7ux

Indeed, still I think it's just a feature request. It's not making something simpler, it's extending functionality.

ohthehugemanatee’s picture

It'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.

fago’s picture

A 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.)

mitchell’s picture

Component: Documentation » User interface
Status: Active » Postponed
mesch’s picture

I've encountered this use case and my solution was to turn the number into a list by creating what is essentially a casting action:

/**
 * Implements hook_rules_action_info()
 */
function moduleName_rules_action_info() {
  return array(
    'integer_to_list' => array(
      'label' => t('Turn integer into list'),
      'parameter' => array(
        'integer' => array('type' => 'integer', 'label' => t('Integer')),
      ),
      'provides' => array(
      	'converted_list' => array('type' => 'list<integer>', 'label' => t('Converted list')),
    	),
      'group' => t('moduleName'),      
    ),
  );
}

function integer_to_list($integer) {
	$new_list = array();
	for ($counter = 1; $counter <= $integer; $counter++) {
		$new_list[] = $counter;
	}
	
	return array('converted_list' => $new_list);
}

For Commerce quantity, you would have to change the input to a decimal.

geek-merlin’s picture

* added shorter description and link to meta issue.
* opened #1525582: Action: generate a list from an integer to get #11 into rules core

mitchell’s picture

Component: User interface » Rules Engine

my solution was... essentially a casting action

So, 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.

geek-merlin’s picture

NO.
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.

mitchell’s picture

Title: While() loops » Loops: while and foreach

Let'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?

atlasc1’s picture

Is 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.

mitchell’s picture

Title: Loops: while and foreach » Loops: while and tail recursion support
Priority: Normal » Major
Status: Postponed » Active
Issue tags: +lists and loops

#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.

mitchell’s picture

Issue summary: View changes

added short description

tr’s picture

Version: 7.x-2.x-dev » 8.x-3.x-dev
Component: Rules Engine » Rules Core
Priority: Major » Normal
Issue summary: View changes
Status: Active » Postponed

We'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.