Drupal 7 – Dynamic select options for Webform

Webform is a great module in Drupal which help collecting user data. Recently i am working on a new website which needs a webform and one of the field is a selection list contains the node titles of a specific content type. I found a blog post about this dynamic select options feature in Drupal 6 by creating a custom module.
xebee – Drupal Webform : Add a dynamic select option list

Here is a similar approach for Drupal 7.

1. Create a new directory named as webform_options with the following 2 files inside.
webform_options.info

; $Id$
name = Webform options
description = Preset options for webform field
package = "Eureka"
core = 7.x
version = 7.x-1.0

 

webform_options.module

<?php

/**
 * Reference: http://xebee.xebia.in/2011/06/14/drupal-webform-add-a-dynamic-select-option-list/
 * The following piece of code is based on the blog post above written by Anubhav
 */

function webform_options_webform_select_options_info() {
  $items = array();
  if (function_exists('_get_node_titles')) {
    $items['node_titles'] = array(
      'title' => t('Node titles'),
      'options callback' => '_get_node_titles',
    );
  }
  return $items;
}

function _get_node_titles() {
  $options = array();
  $sql = "SELECT nid, title FROM {node}";
  
  $result = db_query($sql);
  foreach ($result as $row) {
    $options[$row->nid] = $row->title;
  }
  return $options;
}

 

2. Enable the module and add a new webform field.

 

3. In the webform field setting page, pick Node titles for Load a pre-built option list. In addition, check the Listbox checkbox under Display as we want to have a selection list instead of radio button.

 

4. Save the form and you now have selection list which contains all node titles. If you want limit the options to a specific content type, just modify the SQL in webform_options.module.

 

Done =)

Reference: xebee – Drupal Webform : Add a dynamic select option list

About these ads

55 thoughts on “Drupal 7 – Dynamic select options for Webform

  1. Kai

    Hi,
    this tip works great but does not fit an additonal effort I have:

    I have an event with multiple dates (date field, with multiple dates allowed).
    Now I want to display each date with its eventname in options list.

    E.g.: event 1 is on two dates, event 2 on 4.

    23|event 1 – 1.1.2012
    23|event 1 – 1.3.2012
    24|event 2 – 1.1.2012
    24|event 2 – 3.1.2012
    24|event 2 – 15.1.2012
    24|event 2 – 22.1.2012

    By the way, can I change the safe_key to something else then the nodes id?

    Reply
    1. ykyuen Post author

      This can be done. But instead of querying the data by SQL, it is better to use the EntityFieldQuery to query the node data.

      Here is an example

      function _get_activity_codes() {
        // Get all published activity nodes
        $query = new EntityFieldQuery();
        $query->entityCondition('entity_type', 'node')
          ->entityCondition('bundle', 'activity')
          ->propertyCondition('status', 1);
        $result = $query->execute();
      
        $options = array();
        if (isset($result['node'])) {
          // Load all activity nodes into $activities
          $activity_nids = array_keys($result['node']);
          $activities = entity_load('node', $activity_nids);
      
          foreach ($activities as $activity) {
            // For each activity, set a key value pair for selection list options
            // where $activity->nid is the option key and
            // the first value of field_activity_code is the option value.
            $options[$activity->nid] = $activity->field_activity_code['und'][0]['value'];
          }
        }
        return $options;
      }
      

       

      In you case, you need to add one more for loop for retrieving multiple date values for each event.

      Hope this help.

      Reply
      1. Kai

        Hi, this sound great. Just to be sure: if I use this way, will I get one Row for each event with all the dates in this single row, or is it possible to get one row for each pair of event and date? This is what I need.

        Thanx again!

  2. Kai

    OK, got it ;-)
    In my case (as I just need the start date) I need something like

    function _webform_options_get_story_nodes() {
    
      // Get all published activity nodes
      $query = new EntityFieldQuery();
      $query->entityCondition('entity_type', 'node')
        ->entityCondition('bundle', 'seminare')
        ->propertyCondition('status', 1);
      $result = $query->execute();
    
      $options = array();
      if (isset($result['node'])) {
        // Load all activity nodes into $activities
        $activity_nids = array_keys($result['node']);
        $activities = entity_load('node', $activity_nids);
    
        foreach ($activities as $activity) {
          $var_countings = count($activity->field_myfield['und']);
          for($count = 0; $count nid . '-' . $count] = $activity->nid . '-' . $activity->field_myfield['und'][$count]['value'];
          }
        }
    
      }
      return $options;
    }
    
    Reply
  3. john

    Thanks a lot for this posting, really nice and understandable, but nonetheless I got stuck with a similar problem like Kaion:

    I’ve got an event-node-type with date-field and want the dates to appear on the webform select list. But all I am able to get are the node-titles of the according nodes to be displayed on the webform-dynamic-select-list (and all other information, which are stored in the (mysql-)node-table like language, node creation-date etc.). I have been searching for a solution on the internet for hours, but can’t find any solution :(.

    I guess I need to load the date-data from my (mysql-)_field_termim_datum-table for each node, but can’t figure out how. Do you know how? Thanks a lot in advantage!

    Reply
    1. ykyuen Post author

      Hi John, you dun’t need to load the data from the database. Just make use of the EntityFieldQuery and you retrieve all fields data of your selected nodes. In your case, the code should be sth like

      function _get_event_termin_datum() {
        // Get all published event nodes
        $query = new EntityFieldQuery();
        $query->entityCondition('entity_type', 'node')
          ->entityCondition('bundle', 'event')
          ->propertyCondition('status', 1);
        $result = $query->execute();
      
        $options = array();
        if (isset($result['node'])) {
          // Load all event nodes into $events
          $event_nids = array_keys($result['node']);
          $events = entity_load('node', $event_nids);
      
          // Check all field data of $events for debug
          // print_r($events)
      
          foreach ($events as $event) {
            // For each event, set a key value pair for selection list options
            // where $event->nid is the option key and
            // the first value of field_termim_datum is the option value.
            $options[$event->nid] = $event->field_termim_datum['und'][0]['value'];
          }
        }
        return $options;
      }
      

       

      Does it work for you?

      And you can find more details about EntityFieldQuery in the following post
      Drupal 7 – Get specific nodes using EntityFieldQuery

      Hope this help. =)

      Reply
  4. Troy

    Has anyone had problems getting the list to display in the Load pre-built option list? After installing the custom module and following the steps, I still have not yet been able to display the custom pre-built list in the “Load pre-built option list” dropdown. Any suggestions?

    Reply
      1. Troy

        Hey ykyuen,

        No luck when printing a string inside the _get_node_titles() function. But the module is appearing and enabled in the modules list. Any idea why it would not be running?

      2. Troy

        Hey ykyen,
        My module is in the folder: webviewssel
        which contains: webviewssel.info webviewssel.module
        This is what I have for my .module file which is named webviewssel.module:

        function webform_options_webform_select_options_info() {
          $items = array();
          if (function_exists('_get_node_titles')) {
            $items['node_titles'] = array(
              'title' => t('Custom Countries'),
              'options callback' => '_get_node_titles',
            );
          }
          return $items;
        }
        
        function _get_node_titles() {
         $countries = array(
            t('Europe') => array (
              'nl' =>  t('The Netherlands'),
              'be' =>  t('Belgium'),
              'fr' =>  t('France'),
            ),
            t('Africa') => array (
              'tu'  =>  t('Tunisia'),
              'sa'  =>  t('South Africe'),
            ),
            t('Asia') => array (
              'ru'  =>  t('Russia'),
              'cn'  =>  t('China'),
            ),
            'key' => t('some country that is in no group'),
          );
          return $countries;
        }
        

        My webviewssel.info just has:

        name = Webform Views Select
        description = This module will be used to create selection options from a view.
        package = “Webform”
        core = 7.x
        version = 7.x-1.0

      3. ykyuen Post author

        Rename the function webform_options_webform_select_options_info() to webviewsse_webform_select_options_info()

        Drupal makes use of the hook mechanism which is a kind of Inversion control. The above function is based on the hook_webform_select_options_info(). When you implement the hook, you have to replace the hook_xxx with <module name>_xxx.

  5. Pablo

    Hi ykyuen, thanks for your explication.
    i have done everything you listed.

    i’ve created in (..sites/all/modules/) a new directory named as webform_options.

    and i’ve created the 2 files inside, even with the same name,

    but when i go to the admin->modules->
    i cant find my new module to activate it, what im doing wrong?
    thanks again.

    Reply
  6. Hiraman

    Hi,
    I am new with Drupal. I want to add pre-built timezone drop down in webform.

    Please help me!!

    Thanks

    Reply
    1. ykyuen Post author

      Replace the webform_options.module by the following one.

      <?php
      
      /**
       * Reference: http://xebee.xebia.in/2011/06/14/drupal-webform-add-a-dynamic-select-option-list/
       * The following piece of code is based on the blog post above written by Anubhav
       */
      
      function webform_options_webform_select_options_info() {
        $items = array();
        if (function_exists('_get_node_titles')) {
          $items['node_titles'] = array(
            'title' => t('Node titles'),
            'options callback' => '_get_node_titles',
          );
        }
        if (function_exists('_get_timezones')) {
          $items['timezones'] = array(
            'title' => t('System timezones'),
            'options callback' => '_get_timezones',
          );
        }
        return $items;
      }
      
      function _get_node_titles() {
        $options = array();
        $sql = "SELECT nid, title FROM {node}";
        
        $result = db_query($sql);
        foreach ($result as $row) {
          $options[$row->nid] = $row->title;
        }
        return $options;
      }
      
      function _get_timezones() {
        $zones = system_time_zones();
        return $zones;
      }
      

      You could find the System timezones in the pre-built option list

      Reply
  7. Colin

    Hi I’m not that savvy with code…could you let me know how to just select a specific content type. I know your mentioned that one would just modify the code in the module, but I just dont know what to ‘modify’!…thank you.

    Reply
    1. ykyuen Post author

      Replace the <content type machine name> with your content type machine name.

      <?php
      
      /**
       * Reference: http://xebee.xebia.in/2011/06/14/drupal-webform-add-a-dynamic-select-option-list/
       * The following piece of code is based on the blog post above written by Anubhav
       */
      
      function webform_options_webform_select_options_info() {
        $items = array();
        if (function_exists('_get_node_titles')) {
          $items['node_titles'] = array(
            'title' => t('Node titles'),
            'options callback' => '_get_node_titles',
          );
        }
        return $items;
      }
      
      function _get_node_titles() {
        $options = array();
        $sql = "SELECT nid, title FROM {node} WHERE type = '<content type machine name>'";
        
        $result = db_query($sql);
        foreach ($result as $row) {
          $options[$row->nid] = $row->title;
        }
        return $options;
      }
      

       

      See if it works for u.

      Reply
  8. Brad

    Thank you for the tutorial! The instructions for how to do this in the official Webform documentation failed me, but this worked great!

    Reply
  9. Tom

    Thank you so much for the tutorial! Very helpfull!! Now I already managed to have a dropdown of the titles of a certain content type. But now i’m struggling with the multilanguages… Is there a way to show only the node titles of the language that is active for the user (and hide the titles of the other 2 languages)

    I already used the code you suggested to Collin and works great! I’m trying to add a line with something like global $language; or $lang_name = $language->language; but I don’t seem to manage to get it right…

    All suggestions / help are very welcome!

    Thanks in advance!

    Reply
    1. tom

      OK tried to make it work but no succes untill now. The following code gives me the least errors for now, but still pretty massiv… testsite crashed…

      I’m pretty new to coding like this. Anybody an idea what is going wrong?

      <?php
       
      /**
       * Reference: <a href="http://xebee.xebia.in/2011/06/14/drupal-webform-add-a-dynamic-select-option-list/" rel="nofollow">http://xebee.xebia.in/2011/06/14/drupal-webform-add-a-dynamic-select-option-list/</a>
       * The following piece of code is based on the blog post above written by Anubhav
       */
       
      function webform_options_webform_select_options_info() {
        $items = array();
        if (function_exists('_get_node_titles')) {
          $items['node_titles'] = array(
            'title' => t('Node titles'),
            'options callback' => '_get_node_titles',
          );
        }
        return $items;
      }
       
      function _get_node_titles() {
        $options = array();
        $sql = "SELECT nid, title FROM {node} WHERE type = 'seminaries' ';";
         
        $result = db_query($sql);
        foreach ($result as $row) {
       	global $language;
      	$lang_name = $language->language; 
          $options[$row->nid] = $row->title;
        }
        return $options;
      }
       
      Reply
      1. tom

        Forgot to mention: I have a content type ‘seminaries’ and a multilingual site (3 languages)… What I’m trying to do is generate a list of the node titles, but only thos from the user’s active language must be shown, the rest will be hidden (whereas now everything was shown)

      2. tom

        Allright!! I’m very sorry for all the spamming, but finally found the way. Here’s the code that did the trick for me

        <?php
         
        /**
         * Reference: <a href="http://xebee.xebia.in/2011/06/14/drupal-webform-add-a-dynamic-select-option-list/" rel="nofollow">http://xebee.xebia.in/2011/06/14/drupal-webform-add-a-dynamic-select-option-list/</a>
         * The following piece of code is based on the blog post above written by Anubhav
         */
         
        function webform_options_webform_select_options_info() {
          $items = array();
          if (function_exists('_get_node_titles')) {
            $items['node_titles'] = array(
              'title' => t('Node titles'),
              'options callback' => '_get_node_titles',
            );
          }
          return $items;
        }
         
        function _get_node_titles() {
        	global $language;
        	$lang_name = $language->language;
        	
        	$options = array();
        	$sql = "SELECT nid, title FROM {node} WHERE type = 'seminaries' AND language = '" . $lang_name . "';";
        	$result = db_query($sql);
        	foreach ($result as $row) {
        		$options[$row->nid] = $row->title;
        		}
        		return $options;
        		}
         

        Thanks again ykyuen for the code!

  10. Laura

    This has been immensely helpful after many hours of painful attempts at getting webforms to work for my needs. That said, I’m still stuck. Perhaps you can help?

    Here’s my situation:
    I have three webforms for three class sign-ups. At this point I’ve given up on using a single view output to work for all three, so I have three class content types (one for each class). I need to add all three types to my webform options as a select list in each form.

    This code works wonderfully for adding one type of node to the select list options, but when I try to duplicate the initial module to use for the second two lists, it breaks.

    I’m extremely ignorant with regards to PHP so I’ve no idea how to change the code to work for me. Does any of this make sense?

    Reply
    1. ykyuen Post author

      Can you follow the syntax highlight and post your code here?

      Can you you give me more details on the logic you want to have?

      Is that you want to have 3 webforms and each on the want to have a preset select option list of different values?

      Reply
  11. jim

    hi, it is possible if there are 2 date field, once the first date field are selected, the second date field will auto select a date which is 2 years/month later of the first date field, thx.

    Reply
    1. ykyuen Post author

      It should have nothing to do with the topic of this post.

      In your case, i think you need to work on Javascript to achieve this feature.

      Reply
  12. dave

    Hi, great post, very helpful. I’ve got an immediate problem needing a solution, sorry off topic a bit from your original, wondering if you could give a heads up.

    I have a webform with a picklist that specifies dept. Rather than the user having to set which dept this form originates, i’d like that set via a passed parameter on the menu URL. So for example, in Dept X webpage, the menu URL for the webform has a parameter that is used to set the value of the dept pick list on the form (ie from Dept X). That way i can hide the list and each form has its orginating dept set based on where on the website it originates.

    Reply
  13. Seth

    Hi, I’m really grateful to find this article. I’m wondering if you could help me set default values for the node titles from the URL. somewhat similar to nodereference.

    thank you

    Reply
  14. Sridhar

    I am trying to add configurable options to this module. The API for hook_webform_select_options_info() says that you can provide an “options arguments: Any additional arguments to send to the callback.”

    How do you set values of options arguments – as an array or a string? Also, whichever way I set the arguments, I am not able to access them in the callback function. How should the arguments be accessed inside the callback function?

    Thanks for the gr8 post.

    Reply
    1. ykyuen Post author

      The options argument could be set in this way.

      function webform_options_webform_select_options_info() {
        $items = array();
        if (function_exists('_get_node_titles')) {
          $items['node_titles'] = array(
            'title' => t('Node titles'),
            'options callback' => '_get_node_titles',
            'options arguments' => array('hello', 'world'),
          );
        }
        return $items;
      }
      

      And your call back function should look like this

      function _get_node_titles($arg1, $arg2) {
        // Your code
      }
      
      Reply
      1. Sridhar

        I tried that out. I set up ‘options arguments’ with array(‘hello’, ‘world’) and then I write the _get_node_titles() as:

        function _get_node_titles($arg1, $arg2) {
          $options = array();
          $options[$arg1] = $arg2;
          return $options;
        }
        

        Now I get the error “Warning: Illegal offset type in _get_node_titles()”.

        Trying to print $arg1 in _get_no_titles() returns something like: Array ( [nid] => 2 [cid] => 1 [pid] => 0 [form_key] => select [name] => select [type] => select [value] => [extra] => Array ( [items] => 2|Get a Quote 3|test 1|Welcome to Base D7 [options_source] => node_titles [multiple] => 0 [title_display] => before [private] => 0 [aslist] => 0 [optrand] => 0 [conditional_operator] => = [other_option] => [other_text] => Other… [description] => [custom_keys] => [conditional_component] => [conditional_values] => ) [mandatory] => 0 [weight] => 0 [page_num] => 1 )

        And trying to print $arg2 returns 1.

        I am running on Drupal 7.22 & webform 7.x-3.19.
        Could that be a bug with webform?

        Regards.

  15. Sridhar

    I tried it out. Doesn’t seem to be working. This is what I did – in hook_webform_select_options_info() I pass the arguments with the key ‘callback arguments’ (& not ‘options arguments’) and then set it to array(‘hello’ => ‘world’). In the callback function, I add four arguments to the signature (as per the post you mentioned) and the last one should contain the passed arguments. But, the fourth argument is nothing but an empty array. Also, nothing is returned when I set the key in the hook to ‘options arguments’, not even an empty array. The first argument returns the webform component information only, the second & third arguments return 1.

    However, I got a work around which is working. Since, in hook_webform_select_options_info() the ‘options callback’ can be set to the name of the callback function (as string) only and the ‘options arguments’ is not working as desired, create as many items as you require and then for each of the ‘options callback’ pass a lambda function using the create_function, which essentially returns the name of the function created and executes when called.

    My code looks something like this:

    <?php
    function webform_options_webform_select_options_info() {
    
      // get all content types
      $node_types = array_keys(node_type_get_names());
    
      $items = array();
      // for each of the content types, create an item
      foreach($node_types as $ntype){
        // returns function names as lambda_1, lambda_2, ...
        $callback_func = create_function('', 'return _get_node_titles("'.$ntype.'");');
        $items[$ntype] = array(
          'title' => t('Node: ' . $ntype),
          'options callback' => $callback_func,
        );
      } // foreach
      return $items;
     
    } // webform_options_webform_select_options_info()
     
    function _get_node_titles($node_type) {
     
      $options = array();
      $sql = "SELECT nid, title FROM {node} WHERE type = '$node_type'";
      $result = db_query($sql);
      foreach ($result as $row) {
        $options[$row->nid] = $row->title;
      }
      return $options;
      
    } // _get_node_titles()
    
    Reply
      1. ykyuen Post author

        Hi KN,

        I have fixed your code snippet in the above comment. good to know that you have solved the problem and thanks for your code. =D

  16. Alma

    If I want to appear directly in the webform each page, without requiring the user to specify option list
    How to write code

    Reply
  17. Alma

    I want use to different webform nid, load a different Dynamic select options
    -How get to different webform nid
    -How use to?

    Reply
    1. ykyuen Post author

      You can add different select options list in the return $items inside the hook_webform_select_options_info() function. Then you can edit different webforms and bind different select options list.

      Reply

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s