One of the most useful parts of Rules is the "list loop" functionality. But I'm having a hard time figuring out how to do other kinds of common loops, like while() and for() loops.
My use case is simple. People can buy e-cards at the (Commerce-powered) web-store. When you buy an e-card, Rules handles sending the actual card. When you buy 3 of an e-card, it should send out 3 emails. But quantity is just a field on the line-item. So I'd like something like this:
<?pseudo-code
$quantity=[commerce-line-item:quantity];
do while ($quantity > 1) {
send_email([commerce-line-item:email]);
$quantity=( $quantity - 1 );
}
?>
I tried creating a list loop something like this, with a rule to add the same item back to the list variable at the end of the loop. Unfortunately variables inside the loop aren't passed back outside of the loop, so it doesn't work.
I also tried creating a rule that calls itself again at the end. No dice there, Rules stops that to avoid recursion.
So what's the proper way to achieve this behavior?
Comments
_
Might be a naive suggestion, but if the action is prevented because of recursion, instead of adding that action, why couldn't you turn that action into it's own rule? That way you have a whole new rule that will call trigger the rule you actually want but a new way to trigger the second rule. As long as the first rule doesn't trigger the second rule, you should be okay, I would think.
To clarify:I want to send an
To clarify:
I want to send an email for each Widget a user buys. The user can add an unlimited quantity of Widgets to their shopping cart.
So here's my rules structure:
Check for widgets in the cart
This rule loops over the list of line items in the cart. If it finds a Widget, it fires:
Send an email
This action sends an email to the user confirming the sale of the widget.
The problem is, I WANT to recurse over Send an Email, X times, where X = quantity purchased. The recursion problem happens when I do this:
Check for widgets in the cart
This rule loops over the list of line items in the cart. If it finds a Widget, it fires:
Check quantity
Copies Quantity from the cart into a $variable.
fires:
Send email
if $variable > 0
send an email
decrement $variable by 1
call Send email again.
If I understand you correctly, you think I should move the decrementing and re-firig of Send Email into it's own sub-rule? I guess I don't understand what that would help.
_
In the example you posted it seems you're skipping the first step. You have
STEP 1: if var > 0
STEP 2: send email
STEP 3: decrement
STEP 4: repeat STEP 2
So the loop would be recursive because you aren't setting it back to STEP 1. STEP 1 gives you the terminate because if the var is 0 it won't continue. So your last step needs to loop back to one.
Now, if Rules is preventing you from looping back to one to protect you from making a recursive rule, then you would need to break it into two different rules.
Rule #1:
STEP1: if var > 0
STEP 2: send email
STEP 3: call Rule #2
Rule #2:
STEP 1: if var > 0
STEP 2: decrement
STEP 3: call Rule #1
In that way you shouldn't have a recursive because both rules can terminate on STEP 1 and Rule#1 has the send email. So even if Rule #2 decrements to 0, it will still call Rule #1, but Rule #1 will terminate as soon as it gets called.
Thanks for your advice RKS. I
Thanks for your advice RKS. I tried structuring it as you suggested, but I still get the same problem. "Not evaluating rule Send Email to prevent recursion." I guess Rules is smart enough to see the recursion across two rules, even if it isn't smart enough to see that there is a break condition.
any other advice on how to get a do... while loop going in Rules? This seems like a fundamental piece of functionality to be missing!
The same problem
I have the same problem on setting rules.
I have a product like coupon, and when a user buy a product of this type, I would like to create an entity node. I have achieve this part but I have problem to implement the loop for the quantity on product. When some user buy more than one products, how can I set the rule to produce more entities?
Thanks.
Conditional Rules, new developments or custom PHP
Hi guys,
It seems there has been quite a lot of discussions and work on-going on different types of loops with Rules.
I haven't tried anything of this myself, but after doing very superficial investigations, I found the Conditional Rules module seems to provide a plugin for While loops, with a simple condition:
and it also seems discussions/developments are undergoing at:
Another solution I could think of (never tried as well), would be to add a list variable to the actions scope of the Rule (standard add a variable action).
Then add a PHP action to the Rule, to modify the variable that was just created and copy X times the line item in the array, for example with something like:
$added_variable = array_fill (0 , $line_item->quantity, $line_item);Then, using the standard Rules loop on the $added_variable variable list item, which contains quantity times the line item.
Since in any case the quantity should always stay within a reasonable range, it doesn't seem like it would be a problem for the allocation of the
$added_variablearray in memory.Since as I mentioned, I never tried any of that myself, I would greatly appreciate to hear your feedbacks, comments, questions, suggestions, objections, issues or concerns on any of these suggested approaches.
Thanks very much in advance to all for your questions, comments, reporting and testing.
Cheers!
My solution
Thanks DYdave for your post. Your recomedations and links are very helpful for my site.
I try a lot on rules and I think that I just found some good working concept. I'll try to describe it.
On my rule I have ane event "When an order is first paid in full".
On actions I have a loop on every card order line (commerce-order:commerce-line-items:0:order:commerce-line-items) and execute inside the loop a custom php code like the below to execute a custom rule component that produce the appropriate amount of nodes based on the quantity of purchased products.
$totalProducts = $list_line_item->quantity;
for ($i=1; $i<=$totalProducts; $i++)
{
rules_invoke_component('rules_produce_event_from_cart',$list_line_item);
}
As you can see I execute the component with code, and pass to it the list_line_item var.
On my component from the other hand I have a variable with data type Commerce Line item that handle the passed argument from the execution of my previews custom php code. Based on that var I could produce the entity nodes that I want.
Thanks.
My question now is how can I
My question now is how can I dump the variable $list_line_item to see what other element and in which form are available in it.
I have try (with devel module enable)
var_dump($list_line_item);
dsm($list_line_item);
print_r($list_line_item);
krm($list_line_item);
but no table of variable print out to my site to see the inside element. In general how could I print out the construction of a variable?
It should actually work....
Hi tarasiadis,
The code you've got there should actually work.
Have you tried to print something first, like:
dpm("test");and see if this would already display?
Maybe the code is not even getting executed.....
If code is not executed you would have to find out why?
Maybe an if test is failing and not entereing your code, etc....
Once the trace prints out, you would have to make sure that the variable name is correct as well.
I don't think there is anything wrong here.
It's just some really basic debugging to do.
Feel free to let us know if you would have any more questions on any other elements, we would surely be glad to help.
Thanks to all in advance for reporting, testing and your feedback.
Cheers!
When and where the dpm print out the message?
I try the dpm("test"); as test.
My execution php code seem to not have problem and runs successfully as I have already enable the Rules Evaluation Log and I can see that work all my steps.
My thought is that the printout of the dpm runs but does not produced at the right time and I can see at the end. What I mean:
I use the event "When an order is first paid in full" so our actions runs when this event happened end then he system produce the successful page witch inform me that my order has been successfully completed. On the last page I could not see the dpm message. But I'm wondering if the message produce on some previous step (before the last screen of successfully order) and I can't see.
I try to solve my confused thoughts. Thanks for your help.
Loops on conditions
While we're talking about loops in Rules....
It would also be good to have loops that work on conditions, so it would be possible to evaluate all the items in a list. See [#821986 - Evaluate conditions on list items].
As far as I know, there is no good way to do this for the general case, and I'm not a good enough Drupal programmer to know how to put together a patch myself to do this.
use rules components for conditions inside loops
Can't you just create a rules component with the required conditions and actions and have that run in the loop?
Can't you just create a rules
That's not what I'm trying for. I want a single boolean result after evaluating a condition repeatedly against each item in a list.
See [#821986 - Evaluate conditions on list items] for details. In the mean time, let me make up an example to illustrate it for you.
Imagine an e-commerce app for a pet store. I sell many products. Some of them are live animals.
I develop a Rules condition: "Product is alive." It takes a single product object as input.
I have a shipping method: "Box with air holes".
A customer can buy lots of things in each order. So, each shipment object has a list of product objects.
Now, I can attach a Rules condition to the shipping method. I want the condition to get that list of products, and test the "Product is alive" condition on each item in the list. If any one of the products is alive, then I want to use the "Box with air holes" shipping method.
You may now start the goldfish jokes.