Hey all,
I wrote up a short tutorial about using the new hook_webform_select_options_info() in Webform 3.x to dynamically populate a Webform. Hope this is helpful to folks! Let me know if you have questions.
Kosta
Providing dynamic select options to Webform module for Drupal
The Webform module for Drupal is tremendously useful, and with the 3.x release, it has become much easier to work with. In this tutorial, I am going to explain how to dynamically populate Webform options in Drupal.
The scenario
Why would you want to do this? Let's suppose you are using Webform to allow your site users to register for upcoming events. In the old days it worked like this: you added a select component to your Webform, and then hand-entered the upcoming events. When the date of an event passed, you would have to go back to your component and update the list. It was either that, or you had to implement hook_form_alter, which is no fun.
With Webform 3.x, quicksketch has provided us with hook_webform_select_options_info(), which we can use to provide our dynamic select options to Webform 3.x.
Implementing hook_webform_select_options_info()
First we need to create a module. In our module file we would add the following:
<?php
>
/**
* Provide a select options component to Webform. The values are
* populated via a call to _demo_get_options, which returns
* an array of nodes.
*
* @return void
* @author Kosta Harlan
*/
function demo_webform_select_options_info() {
$items = array();
if (function_exists('_demo_get_story_nodes')) {
$items['story-nodes'] = array(
'title' => t('Story nodes'),
'options callback' => '_demo_get_story_nodes',
);
}
if (module_exists('views')) {
$items['views'] = array(
'title' => t('A view'),
'options callback' => '_demo_get_view_options',
);
}
return $items;
}
?>This function is providing two items to Webform, "Story nodes" and "A view". You can optionally define a file callback if you want to separate keep a clean separation of your core module functions and the callback you are providing for Webform. Check out webform_hooks.php in your webform module folder for more information.
At this point, all we've done is tell Webform that there are two new options for the select options component. Now we need to provide the functions for the callbacks that we specified above:
<?php
>
/**
* Returns an array of Story nodes keyed on the node ID.
*
* @return array
* @author Kosta Harlan
*/
function _demo_get_story_nodes() {
$nodes = array();
$select = db_query(db_rewrite_sql("SELECT nid, title FROM
{node} WHERE type = 'story' ORDER BY title"));
while ($node = db_fetch_object($select)) {
$nodes[$node->nid] = $node->title;
}
return $nodes;
}
?>The function above is a simple example of a database query. It selects all nodes of the story type and orders them by title. Not terribly useful, but you can tweak the query to provide the results you are looking for.
For most folks, an easier and more sustainable option will be to create a View of the nodes that you want to display to your users, then implement the function below:
<?php
>
/**
* Options callback for webform_select_options_info().
* The assumption is that your view is displaying node data.
*
* @return array of items to populate the select list with.
* @author Kosta Harlan
*/
function _demo_get_view_options() {
// Change 'my_sample_view' to the machine name of your View.
$view = views_get_view('my_sample_view', true);
$view->execute();
$items = array();
foreach ($view->result as $item) {
$full_node = node_load($item->nid);
$items[$item->nid] = $full_node->title;
}
return $items;
}
?>This will then return an array of items based on the View that you defined. If you end up using Views, I would strongly recommend exporting your View as a module and adding it as a dependency in your new module's .info file.
Usage
Now in your webform, click on "Form Components" and under type pick "Select options". Click on "Add", and under "Load a pre-built options list", pick the option you defined above.
And that's it! Now any time you add or delete a Story node, or the results returned by 'my_sample_view' change, the options displayed to the user in your Webform will also change.
The tarball of the code is attached.
Questions? Comments? Let me know.

Comments
Kewl, you could probably use
Kewl, you could probably use one of the calendar module views with this. E.g., have a calendar view that uses an event content type. Then use a slight variation of the upcoming sub-view to populate your choices. Viola - A nice calendar display of events and auto registration choices...
I recently did a blog post
I recently did a blog post that went out to Drupal Planet on this exact same topic (http://fleetthought.com/programmatically-adding-pre-built-option-lists-d...). Slightly different example though. Cool stuff in the new version of the module.
Mark W. Jarrell
Online Applications Developer
Richland Library
http://www.richlandlibrary.com
http://fleetthought.com
Twitter: attheshow
Cool, thanks for sharing that
Cool, thanks for sharing that Mark.
I think next on the list to make this functionality more useful is to address better Views integration: http://drupal.org/node/767290
Worked only the first time
Glad I found this! It's just what I need.
On the form field's settings I was able to select a view as a pre-built option list only the first time after I uploaded.
After I customized some of the module's code (to use my own view), only the default pre-built options keep appearing. Even when I remove the module and upload and enable the original demo-module again.
I don't know how these kind of functions works behind the scenes.
Does anybode have a clue what is happening?
Code is exactle the same, exept the content-type in line 40 and the view name in line 54.
I only get it to work by editing webform/includes/webform.options.inc directly:
function _webform_options_info() {
$items = array();
if (module_exists('views')) {
$items['products'] = array(
'title' => t('Products'),
'options callback' => 'webform_options_products',
);
}
return $items;
}
/**
* Option list containing Products
*/
function webform_options_products() {
$view = views_get_view('products_options', true);
$view->execute();
$items = array();
foreach ($view->result as $item) {
$full_node = node_load($item->nid);
$items[$item->nid] = $full_node->title;
}
return $items;
}
The view outputs field nid.
Options appear to be harcoded
I have the same issue as @Tino. Options are NOT dynamically loaded from the view I created. They behave as if they were hard-coded and cannot be changed - not useful.
Is this the expected behavior?
If not, anyone have a fix for this?
Thanks to @Daugilas for indirectly suggesting Webform Views Select which works nicely in D7.
Being. Knowing. Sharing. Ip Man
Change available node based on previous field
Thanks for the tutorial.
I have been able to get a list of events to display in a select list. Is it possible to change what nodes are displayed based on input from another webform field. For example the nodes I'm displaying are events with a date field, so I would like to be able to filter them based on a previously enter date.
Items not appearing in the Load a pre-built option list
Hi There,
Great tutorial - and I think I'm almost at the point of getting it to work... I'm trying to draw the titles from my node type 'company' to display in a select drop-down. Module has been created, as well as the .info file, but I can't seem to get the company titles to appear in that "Load a pre-built option list" area after creating a select list on a current webform. My code is below:
<?php
function demo_webform_select_options_info() {
$items = array();
if (function_exists('webform_query_get_company_nodes')) {
$items['company-nodes'] = array(
'title' => t('Companies'),
'options callback' => 'webform_query_get_company_nodes',
);
}
if (module_exists('views')) {
$items['views'] = array(
'title' => t('A view'),
'options callback' => 'webform_query_get_view_options',
);
}
return $items;
}
function webform_query_get_company_nodes() {
$nodes = array();
$select = db_query(db_rewrite_sql("SELECT nid, title FROM
{node} WHERE type = 'company' ORDER BY title"));
while ($node = db_fetch_object($select)) {
$nodes[$node->nid] = $node->title;
}
return $nodes;
}
?>
Any ideas? Thanks!
Drupal 7 break this
Hi guys! I used this codes a lot, and it is very helpful. By the way sql syntax in D7 has changed, so I had to change it.
According to http://drupal.org/update/modules/6/7#dbtng , here is a working function for getting the content type:
function _mialistawebform_get_content_type_nodes() { $nodes = array(); $select = db_query("SELECT nid, title FROM {node} WHERE type = 'offerta' ORDER BY title"); //qui il content_type foreach ($select as $node) { $nodes[$node->nid] = $node->title; } return $nodes; }Greetings
Thanks, Kosta.
This worked well for my need to display data from a database table as a drop-down field, even without using the view.
seats to workshops
Both tutorials are really helpful, thank you guys. I'm try to use the idea to build an option list to workshops / trainings that have limited seating. What hook could a use, to alter the form, so that a select option was DISABLED? Something like:
$form_item['#attributes'] = array("disabled" => "disabled");
EDIT-
The tarball link at the top isn't working.
user warning
I f I use the code:
logged in as administrator everything works like a charm but as an anonymous user I get the following error:
user warning: Column 'nid' in field list is ambiguous query: SELECT DISTINCT nid, title FROM node INNER JOIN node_access na ON na.nid = n.nid WHERE (na.grant_view >= 1 AND ((na.gid = 0 AND na.realm = 'all') OR (na.gid = 0 AND na.realm = 'content_access_author') OR (na.gid = 1 AND na.realm = 'content_access_rid'))) AND ( type = 'faq' )ORDER BY title in /var/www/vhosts/nuovo.appiacellulari.com/httpdocs/sites/all/modules/contrib/prodotto/prodotto.module on line 58by the way: if I use the function that invokes views I get a wsod no matter what I do
content access
I discovered that the error is caused by content_access module.
It's a bit tweaky, if I enable the module I still get
user warning: Column 'nid' in field list is ambiguous query: SELECT DISTINCT nid, title FROM node INNER JOIN node_access na ON na.nid = n.nid WHERE (na.grant_view >= 1 AND ((na.gid = 0 AND na.realm = 'all') OR (na.gid = 0 AND na.realm = 'content_access_author') OR (na.gid = 1 AND na.realm = 'content_access_rid'))) AND ( type = 'faq' )ORDER BY title in /var/www/vhosts/nuovo.appiacellulari.com/httpdocs/sites/all/modules/contrib/prodotto/prodotto.module on line 58even if I allow all permission on the specific node (allowing content_access per node setting) nothing seems to allow an anonymous user to access and retrieve the list of items (nodes) and place 'em inside the select...
I'm trying some workaround but I'm a bit lost... anyone? thank you
This appears to be a bug in
This appears to be a bug in the prodotto module. This query is broken:
SELECT DISTINCT nid, title
FROM node
INNER JOIN node_access na
ON na.nid = n.nid
WHERE (na.grant_view >= 1
AND ((na.gid = 0 AND na.realm = 'all')
OR (na.gid = 0 AND na.realm = 'content_access_author')
OR (na.gid = 1 AND na.realm = 'content_access_rid')))
AND ( type = 'faq' )
ORDER BY title
this should read
SELECT DISTINCT n.nid, n.title
FROM node n
INNER JOIN node_access na
...
Thank you for the early reply
Thank you for the early reply afreeman!
apart from the .info file that's all there's is to mention about prodotto.module
<?php
/
* @file prodotto.module
* TODO: Enter file description here.
*/
/
* Implementation of hook_webform_select_options_info().
*/
function prodotto_webform_select_options_info() {
$items = array();
if (function_exists('prodotto')) {
$items['faq'] = array('title' => t('faq'), 'options callback' => 'prodotto',);
}
return $items;
}
function prodotto() {
$nodes = array();
$select = db_query(db_rewrite_sql("SELECT nid, title FROM {node} WHERE type = 'faq' ORDER BY title"));
while ($node = db_fetch_object($select)) {
$nodes[$node->nid] = $node->title;
}
return $nodes;
}
I think the culprit is
I think the culprit is this:
$select = db_query(db_rewrite_sql("SELECT nid, title FROM {node} WHERE type = 'faq' ORDER BY title"));
If you take a look at the documentation for db_rewrite_sql():
http://api.drupal.org/api/drupal/includes--database.inc/function/db_rewr...
that explains where all of those joins (etc) are coming from. If all you are trying to accomplish is pull a list of nodes of type "faq" I suggest removing db_rewrite_sql() and just going with a straight db_query(). If you actually need the rewrite functionality you need to (at minimum) change your query to something like this:
$select = db_query(db_rewrite_sql("SELECT n.nid, n.title FROM {node} n WHERE type = 'faq' ORDER BY title"));
The best way for me, is to
The best way for me, is to form a view and then load view results into webform select field.
I also found this module: http://drupal.org/project/webform_viewreference it creates a new webform field type "viewselect". Which enables the functionality.
At this point it has an issue: does not have "Node Reference" (submodule of CCK) dependability. So just install that one too before, otherwise you'll get an error.
Drupal 7 changes - db_rewrite_sql / db_fetch_object discontinued
I was trying to make it work on Drupal 7 without success. After some digging, I found out that the database API had been completely revamped in Drupal 7 and db_rewrite_sql and db_fetch_object didn't exist anymore. So I thought it would be a good idea to share what I had to change so other newbies like me don't get lost...
What changes:
In his original _demo_get_story_nodes() function he had:
<?phpfunction _demo_get_story_nodes() {
$nodes = array();
$select = db_query(db_rewrite_sql("SELECT nid, title FROM
{node} WHERE type = 'story' ORDER BY title"));
while ($node = db_fetch_object($select)) {
$nodes[$node->nid] = $node->title;
}
return $nodes;
}
?>
For Drupal 7, change it to:
<?phpfunction _demo_get_story_nodes() {
$nodes = array();
$select = db_query("SELECT nid, title FROM
{node} WHERE type = 'story' ORDER BY title");
foreach ($select as $node) {
$nodes[$node->nid] = $node->title;
}
return $nodes;
}
?>
Nevertheless, thank you very much kostajh for your solution.
Now, if you are as newbie as I am, you might want to see all I did. It might help you...
What was I trying to achieve?
I created a content type called study. One of it's fields is status which can have values "0" and "1".
I was trying to create a select field with dynamic options that brings the title of all nodes of type study with status "1".
What did I do?
1 - created new module called studyoption with two files: studyoption.info and studyoption.module.
2 - Placed both files inside /sites/all/modules/custom/studyoption of my drupal site.
2 - studyoption.info has the following content:
3 - studyoption.module has the following content:
<?php
function studyoption_webform_select_options_info() {
$items = array();
if (function_exists('_studyoption_get_study_nodes')) {
$items['study-nodes'] = array(
'title' => t('Ongoing Studies'),
'options callback' => '_studyoption_get_study_nodes',
);
}
return $items;
}
function _studyoption_get_study_nodes() {
$nodes = array();
$select = db_query("
SELECT nid, title
FROM {node}
WHERE type = 'study'
AND nid IN (SELECT nid
FROM {field_data_field_status}
WHERE field_status_value = '1')
ORDER BY title
");
foreach ($select as $node) {
$nodes[$node->nid] = $node->title;
}
return $nodes;
}
?>
Hope this helps others...
Thanks again kostajh for your solution.
Can someone provide an
Can someone provide an example of how to get a a D7 field as the safe_key instead of the nid? Here is the views part:
<?phpforeach ($view->result as $item) {
$full_node = node_load($item->nid);
$items[$item->nid] = $full_node->title;
}
return $items;
}
?>
and it populates the webform Load a pre-built option list with [nid] | [title] like this:
18|Minneapolis16|Stillwater
15|Roseville
14|White Bear Lake
13|South St Paul
Instead I would like it to populate with [field_email] | [title] like this:
somebody@gmail.com|Minneapolissomebody-else@gmail.com|Stillwater
someone@gmail.com|Roseville
somebody-special@gmail.com|White Bear Lake
someyoungguy@gmail.com|South St Paul
I just don't know how to craft the PHP to achieve this. Any hints appreciated!
Again thanks for the awesome example!
A sample using EFQ:<?php
A sample using EFQ:
<?php
$build = array();
$type = 'my_type'; // change as needed
$query = new EntityFieldQuery();
$query
->entityCondition('entity_type', 'node')
->entityCondition('bundle', $type)
->propertyCondition('status', 1)
->propertyOrderBy('nid', 'ASC') // Order Asc by NID
->propertyCondition('promote', 0, '<>');
$result = $query->execute();
if (!empty($result['node'])) {
$nodes = entity_load('node', array_keys($result['node']));
foreach ($nodes as $node) {
$build[$node->nid] = strip_tags($node->title);
}
}
return $build;
?>
laught, light n laughter
This also works in Drupal 7 FAPI?
Could I also use this in FAPI:
'options callback' =>
To date, I have been using this:
options => $options;
Then I would define the $options variable in the same function.
However, defining the options with 'options callback' in a separate function may lead me to my ultimate goal of defining dynamic option checkboxes from an array loop (only one ajax db_query after the first click; or maybe page load) instead of a db query each time a selection is made from a complementary (additional) checkbox field.
Many thnx for your help/guidance with this question.
-Ben
I get to work in D7 Drupal
I get to work in D7
Drupal core 7.12
Webform 7.x-3.17
but I get a notice when I send the form. Is a problem with the select.inc itself not with this custom module. Anyone who had the same problem has solve it?
Notice: Undefined index: #webform_component in theme_webform_display_select() (line 522 of /blah/blah/blah/blah/sites/all/modules/webform/components/select.inc).By the way here there is my code to list the node titles of the "article" content type. I'm using the views function.
I created a view called "articles" that list the "article" content.
<?php
/<strong>
* Provide a select options component to Webform. The values are
* populated via a call to _demo_get_options, which returns
* an array of nodes.
*
* @return void
* @author Kosta Harlan
*/
function demo_webform_select_options_info() {
$items = array();
if (module_exists('views')) {
$items['views'] = array(
'title' => t('A view'),
'options callback' => '_demo_get_view_options',
);
}
return $items;
}
/</strong>
* Options callback for webform_select_options_info().
* The assumption is that your view is displaying node data.
*
* @return array of items to populate the select list with.
* @author Kosta Harlan
*/
function _demo_get_view_options() {
// Change 'my_sample_view' to the machine name of your View.
$view = views_get_view('articles', true);
$view->execute();
$items = array();
foreach ($view->result as $item) {
$full_node = node_load($item->nid);
$items[$item->nid] = $full_node->title;
}
return $items;
}
?>
Question
Hi. I have used this and is very usefull.
I have a question: I need print $full_node->title + 2 other field of the node type.
List will be: Title + field 2 + field 3
Can I do it with your module?
cheers
very good!!! usage is very simple! I was looking for some time and decided on the first try, then I found your tutorial. very good!!!
Emanuel Canuto
Works great! One bug that I found.
I have used this several times now, and it works great! However, there is one bug that I found. As an example, I have used this to add a 'Title' field for employees on a system. I have a view set up to list all of the employees with the ability to 'Edit' each employee's information. If I go back to edit an employee, the list is empty.
Any suggestions?
There's any new feature about it?
I can't believe that we have to do it by hand. There's any module that could create a dropdown list in my Webform based on a view (nodes from a type of Content Type( ?
Webform Views Select will do that for Drupal 7
Webform Views Select
Try Webform Options by Bundle
@elaberge: I'm trying Webform Options by Bundle in a project and it's working pretty well so far. Still searching for a Views-based solution, so I can (for example) pull the body field into the webform
Add AJAX to populate another option
Hi,
there's a way to populate or change the pre-build list of one option related on the value of another. I tried to write some ajax code but it doesn't work.
Thanks
webform_select_views
hello, can sameone help my please, i don't interestand haw it works webform_views_select, I am a beginner in drupal
webform_select_views
hello, can sameone help my please, i don't interestand haw it works webform_views_select, I am a beginner in drupal
Maybe you figure this out by now but in case someone else . . .
finds this. Here is a short summary.
Firstly you need to create a view that lists the items you want. This needs to be a Entity Reference Source view type. Currently it does not matter what you put in the view, only the title of the entity will be shown in the webform select list. There is an issue to change this.
In webform add a new element of the type Entity select. In the Entity Reference Settings for this element select the entity type (normally content). For the Reference method, select Views: Filter by an entity reference view. In the next field View used to select the entities, select the entity reference view that you previously created. Save.
I hope this helps someone looking for how this works.
Frederick