Score:2

How to create a group of sub tokens off an entity type?

us flag

I am trying to create sub tokens in a group off of existing Users token (sometimes referred to as "chaining"). So there are 2 parts to this. Creating the subgroup off the existing User tokens group and creating tokens within that group. I really only need the hook_token_info() part of this; not the actual token code.

I am trying to create a set of tokens of the form: [user:state:{token_id}]. Examples:

  • [user:state:big]
  • [user:state:small]

I am able to create [user:state-big] and [user:state-small] but would like to know how to have these grouped in the token browser under Users -> State.

From documentation linked to by Clive it would suggest this:

  $types['state'] = array(
    'name' => t('State'),
    'description' => t('Tokens related to user state.'),
    'needs-data' => 'user',
  );

  $tokens['user']['state']['big'] = array(
    'name' => t('User State (Big)'),
    'description' => t('The big state of the user.'),
    'type' => 'user',
  );

  $tokens['user']['state']['small'] = array(
    'name' => t('User State (Small)'),
    'description' => t('The small state of the user.'),
    'type' => 'user',
  );

  return array(
    'types' => $types,
    'tokens' => $tokens,
  );

this code gives me these in the token browser:

State   state   Tokens related to user state.

and under Users:

       [user:state]
Score:3
cn flag

Your tokens/types aren't structured quite right. You want big and small to be tokens in the state type (not the user), and you want the token you're adding to the user type to be of type state.

The code explains it better:

$types['state'] = array(
  'name' => t('State'),
  'description' => t('Tokens related to user state.'),
  'needs-data' => 'user',
);

$tokens['state']['big'] = array(
  'name' => t('User State (Big)'),
  'description' => t('The big state of the user.'),
);

$tokens['state']['small'] = array(
  'name' => t('User State (Small)'),
  'description' => t('The small state of the user.'),
);

$tokens['user']['state'] = array(
  'name' => t('User State'),
  'description' => t('The user state.'),
  'type' => 'state',
);

return array(
  'types' => $types,
  'tokens' => $tokens,
);

That will enable, e.g., [user:state:big] for use, and show the expected hierarchy in the token browser.

As an aside, you'll also need to populate the data for the state type in hook_tokens().

For example:

function module_tokens($type, $tokens, array $data, array $options, BubbleableMetadata $bubbleable_metadata) {
  $token_service = \Drupal::token();
  $replacements = [];

  if ($type == 'user' && !empty($data['user'])) {
    if ($state_tokens = $token_service->findWithPrefix($tokens, 'state')) {
      $replacements += $token_service->generate(
        'state', 
        $state_tokens, 
        ['user' => $data['user']], 
        $options, 
        $bubbleable_metadata
      );
    }
  }

  return $replacements;
}

Also bear in mind that varying cache by individual user, which is happening here, can result in a lot of cache entries (depending on the number of users and how/where this token is used).

If you can find a solution which varies the content based on role, or even change the implementation so that it incorporates a render array with a lazy builder, you might see better cache performance.

liquidcms avatar
us flag
This is awesome. Thank you for this and hopefully this serves as a clear example for others trying to do this. One small issue with it though is that although the [user:state:big/small] tokens do show up under Users top level token group; there is also a top level State token group which has [state:big/small] tokens. I removed the name/desc from the $types['state'] array and this fixed that.
liquidcms avatar
us flag
Ah, my bad. Removing Name attr from token info just removes the name in the browser, which causes it to sort to the top of the list. Would be good to know how to not include it in the browser.
I sit in a Tesla and translated this thread with Ai:

mangohost

Post an answer

Most people don’t grasp that asking a lot of questions unlocks learning and improves interpersonal bonding. In Alison’s studies, for example, though people could accurately recall how many questions had been asked in their conversations, they didn’t intuit the link between questions and liking. Across four studies, in which participants were engaged in conversations themselves or read transcripts of others’ conversations, people tended not to realize that question asking would influence—or had influenced—the level of amity between the conversationalists.