My situation is that I have two relevant content types:
parent_content
child_content

these parent is not directly associated with the child, but the child is associated with a single parent via a node reference cck field.

Both content types have their own workflows.

What I am trying to to is to perform an action all child nodes when some particular parent node changes from one workflow state to another.

Currently, the rules/triggers only allow one to load a node by a given id.
The problem with this is that I have no way of knowing what the particular node id is.
The other problem is that there is no way to load an undefined number of nodes based on some boolean (such as content_type == child_content AND child_content->cck_node_reference == updated_content)

Comments

fago’s picture

Status: Active » Postponed

This is planned but postponed after the 1.0 release, see #329500: support for looping and data lists.

thekevinday’s picture

I believe with the custom php action, I was able to do this without changing anything in the rules project.

Here is my example php rule:

<?php
  $view = views_get_view('children_of_parent', TRUE);

  if (is_object($view)) {
    $view->set_display('default');
    $view->set_arguments(array($node->nid));
    $view->render();

    foreach ($view->result as $result) {
      $current_node = node_load($result->nid);
      $actions = array('node_publish_action', 'node_save_action');
      $context['node'] = $current_node;
      actions_do($actions, $current_node, $context, NULL, NULL);
    }
  }
?>
mitchell’s picture

Status: Postponed » Needs review

This code is very similar to #329500: support for looping and data lists, but it uses a completely different methodology, so I marked it as 'needs review' instead of 'duplicate'. It seems amazingly simple and elegant.

@thekevinday: How would you like to proceed with this? Have you seen #329500: support for looping and data lists?

patchak’s picture

Thekevinday : how would I use this code? In the "Load a referenced node" action?

How can I then use this? I can apply an action to "referenced node" and it would apply to all loaded nodes??
Thanks,
Patchak

thekevinday’s picture

mitchell, I have looked at the support for looping and data lists, but the current test code had some issues and I was in need of something working asap.

"How would you like to proceed with this?"
The code can work as is without modifying the Rules source code.
This way you do not have to change the stable series, just supply an example "custom php code" for this situation.

For the development branch, perhaps something like how you have "load content by id" there could be "load view by view name".
Then allow actions be performed on each node found within that view.
This would probably need to extend to the different types of views, such as: users, files, terms, etc..

patchak, There are three places I am able to use the given php code without changing any code of the rules module.
contemplate, the views theming, and the "Execute custom PHP code" action supplied in the latest rules version.

--

Here is a simplified version of what I am using this for:
I have two node types: An Offer and a Bid.

The Offer is made by a drupal user.
The Bid is made by anonymous who sends an id, e-mail, and other contact information.
The drupal user who owns the author is not authorized in any way to edit any of the Bids.

When the drupal user publishes, unpublishes, or even changes workflow of the Offer, all of the Bids need to also be updated.
As the drupal user cannot change the worflow or publishing settings of the Bid, the job gets handed over to the Rules module.

When the bidder makes a Bid, they select the Offer they want, through a node reference CCK field.
The view I created will then load all Bids associated with some Offer by the Offer's nid and that node reference CCK field.
Then the php code above will load the view and process all associated Bids.

--

There was another issue or two that I came across with my original php code I presented above.
1) I didn't check to see if $view->result was empty.
2) The content type "File" as supplied by the File Framework module does not get associated through the node reference but is attached via another means that I have no direct control over.

I had to add another loop to handle attached files:

<?php
  $view = views_get_view('children_of_parent', TRUE);

  if (is_object($view)) {
    $view->set_display('default');
    $view->set_arguments(array($node->nid));
    $view->render();

    if ($view->result){ // bug fix of above example
      foreach ($view->result as $result) {
        $current_node = node_load($result->nid);
        $actions = array('node_publish_action', 'node_save_action');
        $context['node'] = $current_node;
        actions_do($actions, $current_node, $context, NULL, NULL);

        // handle all file attachments, for each child node
        if ($current_node->files){
          foreach($current_node->files as $file_result){
            $file_node = node_load($file_result->nid);
            $actions = array('node_publish_action', 'node_save_action');
            $context['node'] = $file_node;
            actions_do($actions, $file_node, $context, NULL, NULL);
          }
        }
      }
    }
  }
?>

Edit:
The file attachment issue suggests that recursion may need to be supported.
I take that back, there probably won't need to be any recursion as you can get views to grab just about any node you need and add them to the list.

If a node cannot be grabbed, then I suspect it would be a Views issue and not a Rules issue.
My extra code to handle the file attachments for each child node would probably be an issue for me to take up with Views.
This would then allow for the Rules implementation to be very simple.

patchak’s picture

Hey there, thanks for the great example!! I was wondering how could I pass extra arguments to the actions? For example, how to "populate a cck field" or "modify node taxonomy term" using this code? What I mean is how can I integrate it with more complex actions??

Thanks,
Patchak

amitaibu’s picture

Component: Rules Engine » Documentation
Status: Needs review » Active

This is a legitimate use of th ePHP action, however it's not 'Rules' oriented - as it uses Drupal core's actions.
The feature that ultimately is needed (#329500: support for looping and data lists) uses the rules engine mechanism (i.e. arguments and variables), so Rules actions/ conditions can be executed natively on those data sets.

For this reason I mark the issue as documentation.

jrosen’s picture

Is there a way to use the PHP posted in comment #5 and return an array of NodeID's or Nodes to have another action act on them?

crea’s picture

subscribing

thekevinday’s picture

In regards to #6.

I think that making arguments and variables available would need to be created by the Rules team.
I think that is what is being explained in #7.

In regards to #8.

The above php uses the custom php action, which is self-contained. If it were to return something, it would end up in the middle of nowhere.
(of course, if the code referenced by #7 were completed, this could be possible).

Instead of returning the array, try calling a function or triggering a rule.
Is it possible to explicitly call or trigger a rule from within this custom php action?

To get the array of node id's, I believe the <?php $view->result ?> contains what you need.
If you want to have just an array of node id's, then use the foreach function I have above to create the array.

Once you have that array, just do a function call on that array, if you can call your function here.

Here is an example with using a view called 'some_collection_of_nodes', passing no arguments to it.

<?php
  $my_array_of_nids = array();
  $view = views_get_view('some_collection_of_nodes', TRUE);

  if (is_object($view)) {
    $view->set_display('default');
    $view->render();

    if ($view->result){
      foreach ($view->result as $result) {
        array_push($my_array_of_nids, $result->nid);
      }
    }
  }

  my_function_call($my_array_of_nids);
?>

The last action is to call your function or trigger your rule with the generated array of nids.

jrosen’s picture

@thekevinday:
Looping through the NIDs and calling a function to process each Node is a good idea.

But, I much better like the idea of triggering another Rule, or calling another action with the same Rule to keep the functionality as configurable and flexible as possible. Is this possible?

I would like to not have to hard-code a cutom PHP function into my template for the Rule. Obviously, if there is no other viable option, I will do that.

By the way, where can I find documentation for the Rules API so I can figure out if I can call another Action or Rule on my own?

thekevinday’s picture

I have a correction to my example code above.

Where I have something like:

  $file_node = node_load($file_result->nid);
  $actions = array('node_publish_action', 'node_save_action');
  $context['node'] = $file_node;
  actions_do($actions, $file_node, $context, NULL, NULL);

All code calling the node returned by node_load() MUST be wrapped in an if node exists like below:

  $file_node = node_load($file_result->nid);
  if ($file_node){
    $actions = array('node_publish_action', 'node_save_action');
    $context['node'] = $file_node;
    actions_do($actions, $file_node, $context, NULL, NULL);
  }

There were problems with views somehow returning non-existent nodes and actions_do() partially creating them causing some weirdness in the system.
Wrapping in an if exists avoids the issue.

Always check to see if the node exists before performing an actions_do operation because actions_do does not seem to perform this check.

thekevinday’s picture

Here is another Change to my example code.

Apparently calling $view->render(); limits itself to 10 items max.
The more appropriate function call seems to instead be: $view->execute_display();
This function seems to allow one to get ALL the items in the view that are allowed based on the items per page view setting.
If you need to get an unlimited or a limited number, just remember to set the items per page view setting accordingly.

fago’s picture

Just wanted to note that I'm aware of the use-case and rule2 will support this properly.

finex’s picture

subscribing

steven jones’s picture

Note Rules Executor module does this sort of thing.

thekevinday’s picture

I am wondering if #14 is still valid or am I expected to start looking into #16?

I have also noticed considerable changes in views since this thread was last posted to.
Has support for this already been provided?

mayerwin’s picture

subscribing. Waiting for a 6.x release of the looping feature.

itangalo’s picture

I've done some similar things by combining Rules with Views Bulk Operations, and it works well.
I like the solution, since it uses the built-in actions from VBO straight off from Rules with no need to hack or extend Rules functionality (more than the awesomeness it already has).

The concept works like this:

1. Create a rule set performing the actions on the targeted (child) nodes. Due to how VBO works, this rule set must have exactly one argument. (Rules Executor, mentioned by Steven Jones, doesn't have this limitation, but can't yet be called from Rules.)
2. Create a VBO that with help of an argument or two lists all the targeted (child) nodes. Have it call the rule set you just created.
3. Create a triggered rule calling the VBO programatically. Send along any arguments you need in the VBO. A previous bug forced you to use PHP code to call the VBO, instead of the regular UI in Rules. This is now fixed, but so far only in dev.

In all, you get the following workflow: (A) Something happens on your parent node, (B) a triggered rule calls VBO which then (C) performs a rule set on each child node.
A richer description of the process can be found here: http://nodeone.se/blogg/johan-falk/make-rules-dance-with-views-bulk-oper...

In the future I'd be more than happy if Rules could include iteration for multiple-value fields (for references from parent to children), but if the references go from children to parents I suggest sticking to VBO. Finding those children requires DB queries that Views is tailored to do.

Cheers!
//Johan Falk, NodeOne, Sweden

steven jones’s picture

Yeah, the rules executing VBO stuff got added around the same time I started my own solution (rules executor) I've been trying to work out what should happen where, and where to go from.

Probably some code will get folded into VBO and some elsewhere.

fago’s picture

>In the future I'd be more than happy if Rules could include iteration for multiple-value fields (for references from parent to children), but if the references go from children to parents I suggest sticking to VBO.

Well, rules 7.x-2.x is capable of that :) There is already an action "Fetch entities by property value", which gives you a back a list of matching entities you then can iterate over.

mitchell’s picture

Version: 6.x-1.x-dev » 7.x-2.0-alpha1
Category: feature » task

As much as all these examples/workarounds helped in 6.x, now that 7.x covers all of this, this could be classified as documentation to be ported.

New title? >> "Document how to use iteration in 7.x". It's not in the handbook yet..

itangalo’s picture

Assigned: Unassigned » itangalo

I'm on it!

itangalo’s picture

Woaa, this was more difficult than I thought. I need to get some more info on how it is supposed to work before I can do anything reasonable.

@fago: I'll contact you and get some more details.

I'm still on it, though. Cool stuff in Rules 2.

itangalo’s picture

Status: Active » Needs review

Ok, I've done a first version of documentation for loops. There are a number of bugs (or strange behaviour) that makes it difficult to write a full documentation, but this page explains the concepts and the basic steps for adding loops:

http://drupal.org/node/943962

itangalo’s picture

Some thoughts about a different approach for looping through lists can be found here: http://groups.drupal.org/node/99919

fago’s picture

oh, nice - thanks. Indeed I've not tested the loops stuff much yet in d7.

ad "Limitations in current version of Rules 2"
-> please just file issues for that things you noted, so we can use them to track the bugs or discuss it.

I replied to your g.d.o post regarding d6.

itangalo’s picture

@fago: Thanks for comments!

Concerning "Limitations in current version of Rules 2" – I just supposed that the bugs/strange effects I saw was a result of work-in-progress. But you have a point, and I will file issues for stuff I find in the future.

Cheers!

fago’s picture

New fields did not show up as possible lists to create loops from. You could thus not add a multiple-value text field to a node and use it as a loop.
The data selector list-item were only visible when configuring loop actions in the 'data selection' mode (where the data selector is present). It did not show up when using 'direct input' mode (where a number of replacement patterns are listed).
Not all list values were able to load properly, rendering some loops unusable. (Multiple-value body fields could not be called by list-item:value. User roles could be called, though.)
All actions within a loop had to be removed before the loop itself could be deleted.

Let's fix this issue, then update the docs.

1. should be fix already
2. I guess this is a general thing: we don't have token support for custom data structures yet, e.g. just a field item. There should be an issue somewhere.
3. Not sure about that, there shouldn't be any differences.
4. hm, also a general UI problem. We might want to allow users to enforce actions although they lead to a (interim) broken config.

fago’s picture

Status: Needs review » Fixed

>We might want to allow users to enforce actions although they lead to a (interim) broken config.
We've done so now.

I think we can close this old issue now? :)

Status: Fixed » Closed (fixed)

Automatically closed -- issue fixed for 2 weeks with no activity.