laryn's picture

In a custom module, how can I create a custom token? (Transcribing @bwpanda's answer to this question in Zulip for posterity.)

Accepted answer

/**
 * Implements hook_token_info().
 */
function surg_token_info() {
  return array(
    'types' => array(
      'species' => array(
        'name' => t('Species'),
        'description' => t('Tokens related to Species nodes.'),
        'needs-data' => 'node',
      ),
    ),
    'tokens' => array(
      'species' => array(
        'title' => array(
          'name' => t('Title'),
          'description' => t('The title of the Species node.'),
          'type' => 'node',
        ),
      ),
    ),
  );
}

/**
 * Implements hook_tokens().
 */
function surg_tokens($type, $tokens, array $data = array(), array $options = array()) {
  $langcode = isset($options['language']) ? $options['language']->langcode : LANGUAGE_NONE;
  $sanitize = !empty($options['sanitize']);
  $replacements = array();

  if ($type == 'species' && !empty($node = $data['node'])) {
    foreach ($tokens as $name => $original) {
      if ($name == 'title') {
        // Create a title of the form: 'GENUS SPECIES [(NOTATION)]'.
        $genus = taxonomy_term_load($node->field_genus[$langcode][0]['tid']);
        $title = $genus->name . ' ' . $node->field_species[$langcode][0]['value'];
        if (!empty($notation = $node->field_notation[$langcode][0]['value'])) {
          $title .= ' (' . $notation . ')';
        }

        $replacements[$original] = $sanitize ? filter_xss($title) : $title;
      }
    }
  }

  return $replacements;
}

BWPanda: I first create a token type called 'Species' which holds all the tokens (in this case just 1) for my Species nodes (and it requires 'node' data to work properly). I then create a token in the Species group called 'title' which I will use as my Species node titles (I did all this because in D7 I was using custom PHP code in auto nodetitle, which isn't available in Backdrop since the PHP module was (rightly) removed).
So that's the hook_token_info() implementation.

BWPanda: Now for the token code itself (in hook_tokens():
I make sure I'm only dealing with 'Species' tokens where node data is available. Then, for each token (again, in this case just 'title') I generate the title based on the different node field values. I get the term ID from the term reference field, and use it to load the full term, where I add the term name to the beginning of my title. Then I add the value of my Species textfield. Finally, if the Notation field has a value, I append it in brackets on the end. I then add that to the array and return it.

Comments

/**
 * Implements hook_token_info().
 */
function surg_token_info() {
  return array(
    'types' => array(
      'species' => array(
        'name' => t('Species'),
        'description' => t('Tokens related to Species nodes.'),
        'needs-data' => 'node',
      ),
    ),
    'tokens' => array(
      'species' => array(
        'title' => array(
          'name' => t('Title'),
          'description' => t('The title of the Species node.'),
          'type' => 'node',
        ),
      ),
    ),
  );
}

/**
 * Implements hook_tokens().
 */
function surg_tokens($type, $tokens, array $data = array(), array $options = array()) {
  $langcode = isset($options['language']) ? $options['language']->langcode : LANGUAGE_NONE;
  $sanitize = !empty($options['sanitize']);
  $replacements = array();

  if ($type == 'species' && !empty($node = $data['node'])) {
    foreach ($tokens as $name => $original) {
      if ($name == 'title') {
        // Create a title of the form: 'GENUS SPECIES [(NOTATION)]'.
        $genus = taxonomy_term_load($node->field_genus[$langcode][0]['tid']);
        $title = $genus->name . ' ' . $node->field_species[$langcode][0]['value'];
        if (!empty($notation = $node->field_notation[$langcode][0]['value'])) {
          $title .= ' (' . $notation . ')';
        }

        $replacements[$original] = $sanitize ? filter_xss($title) : $title;
      }
    }
  }

  return $replacements;
}

BWPanda: I first create a token type called 'Species' which holds all the tokens (in this case just 1) for my Species nodes (and it requires 'node' data to work properly). I then create a token in the Species group called 'title' which I will use as my Species node titles (I did all this because in D7 I was using custom PHP code in auto nodetitle, which isn't available in Backdrop since the PHP module was (rightly) removed).
So that's the hook_token_info() implementation.

BWPanda: Now for the token code itself (in hook_tokens():
I make sure I'm only dealing with 'Species' tokens where node data is available. Then, for each token (again, in this case just 'title') I generate the title based on the different node field values. I get the term ID from the term reference field, and use it to load the full term, where I add the term name to the beginning of my title. Then I add the value of my Species textfield. Finally, if the Notation field has a value, I append it in brackets on the end. I then add that to the array and return it.