Drupal custom Rules hoe schrijft u uw eigen events condities, acties en custom object (+custom tokens)

Posted by: 
Dominique De Cooman

De rules module is een erg krachtige module wat u in staat stelt om dingen te laten reageren op uw site wanneer bepaalde events plaats vinden (zoals een opmerking die gemaak is) onder bepaalde voorwaarden (bijvoorbeeld wanneer de gebruiker rol x heeft).

Een hoop events, condities en acties zijn reeds out of the box of gelevered door enige contrib modules die rules integration hebben.

Maar wat doe je wanneer je een module hebt en je wil rules support toevoegen? Allereerst, waarom zou je rules support toevoegen? Waarom niet alles in code doen?

Voorbeeld
Laat me dit uitleggen aan de hand van een voorbeeld. We hebben een custom module dat administators in staat stelt packages te bepalen die gebruikers kunnen bestellen (de packages zijn geen nodes, waarom niet is niet aan de orde). Dus wanneer een package wordt gekocht is een event ontslagen. We hebben ook een custom conditie wat het mogelijk maakt om het type package te controleren. Uiteindelijk hebben we een custom action wat de status van de gemaakte bestelling verandert. (We hebben een database tabel met packages en een met bestellingen van packages)

Nu kan een site administrator een set mails bepalen door gebruik te maken van deze regels. Bijvoorbeeld een mail kan verstuurd worden naar de gebruiker wanneer hij een subscription package koopt of een system bericht kan verstuurd worden wanneer de gebruiker een package koopt dat zijn aantal credits vergroot. Voor elk type package dat gekocht wordt kan een andere mail gestuurd worden. U begrijpt het, bedenk dat u in code de mail op deze actie moet veranderen, of de system messages elke keer dat de eigenaar "Beste Klant" in plaats van "Geachte Klant" boven aan zijn mails/berichten wil hebben.

Laten we nu naar de code kijken die u nodig heeft voor uw custom rules.
Er zijn 3 hooks die u moet implementeren, dus creëer een custom module en creëer een extra bestand genaamd your_module.rules.inc, al onze code rules gerelateerde code gaan hier in.

Events
De eerste hook is hook_rules_event_info() en zal ons custom event bepalen.

<?php
/**
* Implementation of hook_rules_event_info().
* @ingroup rules
*/
function your_module_rules_event_info() {
  return array(
    
'your_module_package_bought' => array(
      
'label' => t('A package was bought'),
      
'module' => 'your_module',
      
'arguments' => array(
        
'acting_user' => array('type' => 'user''label' => t('The user who bought the package.')),
        
'package' => array('type' => 'package''label' => t('The purchased package.')),
      ),
    ),
  );
}
?>

Wat we gedaan hebben is een actie bepaald die de acting user en de package object bepaalt. Laten we die actie triggeren wanneer we een package kopen. (Deze functie zal in your_module.module zitten)

<?php
function your_module_buy_package() {
  
//here the code for buying a package will be located
  //when that code returns that a package was bought trigger the rule
  
$order order_load($oid);//$oid will be the id of the order made
  
$package package_load($pid);//pid will be the id of the bought package
  
global $user;
  
rules_invoke_event('your_module_package_bought'$user$package$order);
  
watchdog('your_module',t('A Member !uid has ordered (order:!oid) package !pid', array('!uid' => $user->uid'pid' => $package->pid'oid' => $order->oid)));
}
?>

In de rules interface op de site zult u uw triggered rule bepalen door dit event te selecteren.
Merk op hoe we het event loggen in de watchdog. Dit is een goede oefening omdat je wilt controleren wat er op uw site gebeurd. U kunt de watchdog ook configured laten loggen in de triggered rules interface wanneer u acties bepaalt voor uw event, maar ik denk watchdog logging is meer iets dat in code behoort en het is iets dat de site admin niet zou moeten kunnen configureren (in tegenstelling tot mails en gebruikers berichten).

Condities
Nu bepalen we de condities zodat u uw reactie kunt baseren op het type package dat gekocht was. Voor deze moet u het volgende implementeren hook_condition_info()

<?php
/**
 * implementation of hook_rules_condition_info()
 */
function your_module_rules_condition_info() {
  return array(
    
'your_module_condition_package_type' => array(
      
'label' => t('Type of the package'),
      
'arguments' => array(
        
'package_id' => array('type' => 'value''label' => t('The type of the purchased package.')),
        
'package_type' => array('type' => 'string''label' => t('The type of the purchased package is')),
      ),
      
'module' => 'your_module',
    ),
  );
}

/**
 * Condition packages_condition_packagetype
 */
function your_module_condition_package_type($pid$type) {
  
$package package_load($pid);
  return (
$package->type == $type) ? true false;
}
?>

Wanneer de conditie is beoordeeld wordt de functie your_module_condition_package_type uitgevoerd door middel van de argument pid (package id) geleverd door het event en de argument type bepaald in de interface.

De functie zal true terugsturen als de gekochte package hetzelfde is als de package type bepaalt in de interface.

Acties
Een hoop andere acties zijn reeds bepaald. Voor onze acties hebben we reeds de mail en berichten actie tot onze beschikking. We configureren de "stuur een bericht aan een gebruiker" en om dit te doen hebben we tokens nodig.

Vervanging tokens
Zoals u gemerkt heeft zijn er enkele handige vervanging tokens die we out of the box kunnen gebruiken, zoals de acting users bijvoorbeeld. We kunnen dan een vervanging token toevoegen voor de gebruikersnaam([acting_user:user]). In de mail zal deze token vervangen worden door de gebruikersnaam van de acting user.
In ons voorbeeld konden we een custom vervanging token bouwen voor de naam van de package. Met gebruik van het package id conden we de naam van de package terugvinden.

Om custom tokens in durpal te krijgen gebruiken we de token api functies hook_token_values () en hook_toke_list() (u stopt dit stuk code in your_module.module)

De hook_token_list zal data leveren aan de interface en de hook_token_values zullen de token vervangen
[package:package_name] u voert uw mails in.

<?php
/**
 * Implementation of hook_token_list()
 */
function your_module_token_list($type 'all') {
  if (
$type == 'all' || $type == 'package') {
    
$tokens['your_module'] = array(
      
'package_name' => t('The name of the package'),   
    );
  }

  return 
$tokens;
}

/**
 * Implementation of hook token api
 */
function your_module_token_values($type$object NULL$options = array()) {
  
$tokens = array();
  if (
$object) {
    if (
$type == 'package') {
      
$tokens = array(
        
'package_name' => $object->name,
      );
    }
  }

  return 
$tokens;
}
?>

Slechts een functie ontbreekt om de rules ons package te laten laden de package_load functie. Dit zou er zo uit kunnen zien:

<?php
function package_load($pid) {
  
$package db_fetch_object("SELECT pid, name, type FROM {packages} WHERE pid = %d"$pid);
    
  return 
$package;
}
?>

In onze invocation gave we de package als een argument, daardoor gaf rules ons pachage aan de your_module_token_values() functie in de $object argument zodat het de package_name token kon vervangen met de echte naam van onze token.

Custom acties
De laatste hook is hook_rules_action_info() welke uw custom action bepaalt.

<?php
/**
 * Implementation of hook_rules_action_info().
 */
function packages_rules_action_info() {
  return array(
    
'your_module_action_change_order_status' => array(
      
'label' => t('Change the order status'),
      
'arguments' => array(
        
'order' => array('type' => 'value''label' => t('The order object.')),
      ),
      
'module' => 'your_module',
    ),
  );
}
?>

De functie die de actie uitvoert gebruikt de settings variabele. U kunt de waarde van de status door de interface op deze manier configureren. Als u dat niet wil kunt u de waarde gewoon hardcode, dit demonstreert dat u een form kunt hebben in de interface die custom values vastlegt.

<?php
function your_module_action_change_order_status($oid) {
  
$q "UPDATE {orders} SET status = '%s' WHERE id = %d";
  
db_query($q$settings['status'], $oid);
  
//better would be to create an order_save function but you get the idea
}

/**
* Action drupal message configuration form.
*/
function your_module_action_change_order_status_form($settings, &$form) {
  
$settings += array('status' => '');

  
$form['settings']['status'] = array(
    
'#type' => 'textfield',
    
'#title' => t('Status'),
    
'#default_value' => $settings['status'],
    
'#description' => t('The order status will change to the one supplied here.'),
  );
}

?>

De bestelling load zou zoiets kunnen zijn:

<?php
function order_load($oid) {
  
$order db_fetch_object("SELECT oid, status, uid FROM {orders} WHERE oid = %d"$oid);
    
  return 
$order;
}
?>

Conclusie
Zoals wordt aangetoond is rules een krachtige module, met een net zo krachtige api die custom events, condities en acties kan bepalen.
Rules integreert met alle gangbare modules (en de token api), en het is imho bij het ontwikkelen van modules, indien mogelijk een integratiepunt wat het overwegen waard is.

Extra
Voor meer informatie over de api bekijk de rules.api.php file in het rules package. Het toont een aantal wijzigingsmogelijkheden en hoe u custom objects kun blootstellen aan rules.

Reactie toevoegen