5

I want to run Commerce rule "Create a new account for an anonymous order" from code. I can create user with user_save and etc, but I like this rule and want to reuse it.
After a hour I finished with this code:

// Load reaction rule by name.
$rule = rules_config_load('commerce_checkout_new_account');
// We need $state for 'evaluate' or 'fire'.
$state = new RulesState();
// Add 'commerce_order' parameter to state.
$state->addVariable('commerce_order', $line_item_wrapper->order->value(), array('type' => 'commerce_order', 'label' => 'Commerce order'));
// Execute rule.
$rule->evaluate($state);
// How can I get created user account from $rule?
// Now I have to do user_load_by_mail().

It works but I don't think it is the best way.
I especially don't like to create RulesState manually. I know there are setUpState() and lots of other methods. I want to find the best solution without creating new Rules component.

1
  • just to clarify, You want to run the above code periodically?
    – saadlulu
    Jan 3, 2013 at 23:05

4 Answers 4

5

As a related solution, it's much easier to execute a Rules Component programmatically. They're designed for that.

For example, consider a Rules Component (type "Rule") that accepts a $commerce_order parameter.

In this case, it happens to process a queued order item from a cron queue, pushing the order to a remote system via an API.

function MODULE_queue_iwi_push_instant_sale($item){
  try{
    if($order = commerce_order_load($item['order_id'])){
      // Invoke the rules component programmatically
      // passing the $order parameter
      rules_invoke_component('rules_iwi_push_instant_sale', $order);
    }
  }catch(Exception $e){
    watchdog_exception('my_module', $e);
  }
}

I like to think of Rules Components as similar to functions that can be called, passing parameters and potentially returning a value. They can do other magical things as well. See rules_invoke_component

Reaction rules on the other hand are event driven, triggered by some event such as node_save and passed the relevant parameters. See rules_invoke_event

More info on Rules Components here: https://drupal.org/node/1582182

2

Thanks for the answers. After reviewing the code here, I've created a helper function that works to execute a rule directly (not a component) and is a little simpler/cleaner than the initial code.

You have to look at the rule in the UI and figure out parameters (In conditions, sometimes wrapped and sometimes loaded objects. Experiment.), event machine id (in delete event URL), and rule machine id (in rule edit URL).

Example: send a commerce order email.

    $order = commerce_order_load(285);
    $params = array('commerce_order' => $order);
    $status = mysite_test_fire_rule('my_rule_machine_name', 'commerce_checkout_complete', $params);

/**
 * @param $name string Machine name of rule to fire. From UI edit url.
 * @param $event string Machine name of event. This is a protected property on rule.
 *        dpm($rule->events) or get from the delete button URL on the rule itself.
 * @param $params array of parameters to pass to function, key value paired. Name of param, value of param
 *
 * @source http://drupal.stackexchange.com/questions/45183/execute-reaction-rule-programmatically
 */
function mysite_test_fire_rule($rule_name, $event, $params){
  $rule = rules_config_load($rule_name);
  $state = new RulesState();
  foreach ($params as $param_key => $param) {
    $state->addVariable($param_key, $param, $rule->parameterInfo(TRUE)[$param_key]);
  }
  return $rule->fire($state);
}
0
0

Personally I perferred not to do it with Rules but to do it with the checkout hook. To each his own though.

function hook_commerce_checkout_complete($order) {

        global $user;

            $customer_id = (empty($order->data['self']['CustomerID'])) ? 0 : $order->data['self']['CustomerID'];
        //Create a user if the person is anonymous
        if ($user->uid == 0) {
        //Ignore my customization here, I passed in form data from a custom form to this part, you can just as easily get access to shipping/billing entity data to do the same thing.

            $new_uid = user_creation($order->data['self']['ShippingEmailAddress'], $order->data['self']['ShippingEmailAddress'], $order->data['self']['Password'], $customer_id, $order->data['self']['FirstName'], $order->data['self']['LastName']);
            unset ($order->data['self']['CustomerID']);
            //Set the user id to the newly created user id

            $order->uid = $new_uid;
        } else {
            //Since the user is not anonymous we set the $uid to the value of the currently logged in user
            $order->uid = $user->uid;
        }
2
  • The question was "How to execute reaction rule programmatically", any rule, not just commerce_checkout_new_account.
    – kalabro
    Sep 30, 2012 at 12:27
  • "I want to find the best solution without creating new Rules component." Sorry I couldn't help Sep 30, 2012 at 17:19
0

I don't think there is a better way. As it appears firing event rules from code is not a common thing. the only place I was I found using this is on commerce product pre calculate http://www.rit.edu/drupal/api/drupal/sites%21all%21modules%21commerce%21modules%21product_pricing%21commerce_product_pricing.module/function/commerce_product_pre_calculate_sell_prices/7.41 same RulesState() creation as yours

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.