📜 ⬆️ ⬇️

Creating flexible profiles in Drupal 7

Updated: 06/09/2012
I post the Webform Multiple Conditions module, which implements the functionality described in the article without modifying the Webform module.

The article suggests a patch for the Webform module, often used in CMF Drupal to create questionnaires and questionnaires. The patch allows through the administrative interface to specify several conditions for the form components, as well as to control the logic of their collaboration.

Introduction


I believe that any web developer sooner or later faces the challenge of creating a questionnaire or questionnaire. Of course, in our time it is not so difficult to find any ready-made solution, and it’s good that we have plenty of options here:
Each of the above options has its advantages and disadvantages, which I will not focus on, but I will propose to consider a solution, in my opinion, useful to all developers using Drupal in their work.

Dynamic questioning.


Suppose that we are faced with the task of creating several complex and dynamic questionnaires in which the asked questions themselves depend on the user answers on the previous pages, and where you need to provide the final site administrator with a convenient interface for editing the questions themselves and the logic of their work.
Denote the requirements for the product:
  1. A mechanism is needed to create and edit questionnaires through the administrative interface;
  2. The ability to display questions gradually over several pages is needed;
  3. The ability to display questions is necessary only under certain conditions;
  4. Conditions can be of the following types: “some of the listed values ​​are selected”, “some of the listed values ​​are not selected”;
  5. There may be several conditions at once;
  6. When combined conditions can use the operators AND / OR.

Here is an example of such a questionnaire:

image
')
There are three pages with questions and one with results. Page 2 may not be shown to the user at all if he answered “YES” to the first two questions. The number of questions on the third page, where the user always gets, varies depending on the answers to the first two questions, only the conditions are already combined through "OR" . On the results page, a text message is always displayed with thanks. Also, if the user was displayed page 2, and he answered the third question positively, then some additional text is displayed.

Creating a flexible profile in Drupal.


For Drupal, a powerful Webform module has been developed that implements many of the listed requirements, but, unfortunately, not all.
We can create and edit forms through the administrative interface, we can attach arbitrary components to them, we can even add conditions to the components, but we cannot specify several conditions and combine them. In addition, elements of the Markup type cannot be assigned conditions, which does not allow creating dynamic feedback pages with extremely static content.
This article is dedicated to correcting this shortcoming.
To work we need:
  1. Drupal 7.14
  2. Webform 7.x-3.18
Install Drupal and Webform module. After that go to the page of adding materials [Add content -> Webform | / node / add / webform), where we specify the name of our questionnaire and enter its description.
After saving the new node, go back to the edit page and select the Webform tab.
On this tab, we will attach to the form all the necessary components responsible for the questions and the questionnaire pages.

image

For the designation of questions we will use the coding pattern "question_X", where X is the sequence number of the question. Options for answers ( Options ) for simplicity, we use the same everywhere, and we encode according to the requirements of a component of the Select type ( key | output_value ).

image

Note that for a number of components, starting from the second page, it is possible to specify a condition (condtional rules), but only one.

Extend Webform


We will solve the task of modifying the behavior of the Webform module somewhat crudely and atypically for Drupal, making a number of changes directly to save time. In the future, it is quite possible to select them separately and create a separate module (see Webform Multiple Conditions ).
First we modify the interface. Let's increase the number of possible conditions displayed on the component settings page, from 1 to 10. Some functions in Drupal are always responsible for the elements themselves, which return the Forms API structure. To find its exact name, it is enough in client HTML, for example, using Firebug, to determine the value of the element input [name = ”form_id”] , which is located inside the form.

image

In this case, the value is "webform_component_edit_form" . This function is located in the file /webform/includes/webform.components.inc .

At line 529, the code begins responsible for the conditions. Now we need to perform its modification, while not breaking the functionality of the functions that perform input processing on the form at the submission (eg webform_component_edit_form_validate , webform_component_edit_form_submit ).
When debugging code, you can notice all the additional configuration of the component is stored in the Extra field in the table of components.

image

Therefore, there will be nothing terrible if we expand it by simply performing the cloning of elements 10 times. The magic number 10 is made a weak constant, that is, we define it through the Drupal variable_get function so that it is a hypothetical opportunity to make an administrative interface for modifying this value.
Replace all code representing conditional components with the following:
if ($conditional_components) { $extra_cond = array( 'conditional_component' => array( '#type' => 'select', '#title' => t('Component'), '#options' => webform_component_list($node, $conditional_components, FALSE, TRUE), '#description' => t('Select another component to decide whether to show or hide this component. You can only select components occurring before the most recent pagebreak.') ), 'conditional_operator' => array( '#type' => 'select', '#title' => t('Operator'), '#options' => array( '=' => t('Is one of'), '!=' => t('Is not one of') ), '#description' => t('Determines whether the list below is inclusive or exclusive.') ), 'conditional_values' => array( '#type' => 'textarea', '#title' => t('Values'), '#description' => t('List values, one per line, that will trigger this action. If you leave this blank, this component will always display.') ) ); $extra = $component['extra']; for ($i = 0; $i < variable_get('webform:max_conditional_rules', 10); $i++) { $condition_id = 'condition_'. $i; if (isset($extra[$condition_id]['values'])) { $condition_component_struct = $extra[$condition_id]['values']; $extra_cond['conditional_component']['#default_value'] = $condition_component_struct['conditional_component']; $extra_cond['conditional_operator']['#default_value'] = $condition_component_struct['conditional_operator']; $extra_cond['conditional_values']['#default_value'] = $condition_component_struct['conditional_values']; } $form['extra'][$condition_id] = array( '#type' => 'fieldset', '#title' => t('Conditional rule !num', array('!num' => $i + 1, )), '#collapsed' => TRUE, '#collapsible' => TRUE, '#tree' => TRUE, 'values' => $extra_cond, ); } } 


Perform a check. Let's go to the settings page of an arbitrary component located on any page except the first one. You may notice that the number of conditional components has increased.
Let's add one more additional setting to this form - the choice of the algorithm, by which the final result of fulfilling the conditions is calculated, if there are several of them at once. Let we have two algorithms: AND and OR . To do this, after the previous code, immediately add the following:
 $form['extra']['algorithm'] = array( '#type' => 'select', '#options' => array( 'and' => t('AND'), 'or' => t('OR'), ), '#title' => t('Algorithm to evaluate conditions'), '#default_value' => isset($extra['algorithm']) ? $extra['algorithm'] : 'and', ); 


Now we have several conditions at once and setting up an algorithm for combining them, which is good, but only from the administrative interface. When processing the final client web form, when the user fills in the fields, the old code remains that can work with up to one condition and knows nothing about the combination algorithms.
It remains to find this code. Go to the form page from the client (simply by clicking View) and find the corresponding input [name = "form_id"] .

image

Its value is webform_client_form_1 and when searching for exactly this function, the result is deplorable. But, as you know, Drupal has a way to set the form_id matching rules for the final function via hook_forms () , and if you find the corresponding hook, then it has an indication of the callback webform_client_form , and this function takes place:
 /** * Implements hook_forms(). * * All webform_client_form forms share the same form handler */ function webform_forms($form_id) { $forms = array(); if (strpos($form_id, 'webform_client_form_') === 0) { $forms[$form_id]['callback'] = 'webform_client_form'; } return $forms; } 

There is nothing in this function (and in the standard callback performed when the submission is webform_client_form_submit ) that handles the conditions, but if you analyze its code, you will notice a redefinition of submit callbacks through:
 $form['#submit'] = array('webform_client_form_pages', 'webform_client_form_submit'); 

Let's move on to the webform_client_form_pages function and find in it a _webform_client_form_rule_check call that returns TRUE or FALSE and controls the display or skip of the component. And this is exactly what we need.
The _webform_client_form_rule_check function is passed an argument to $ component , which represents the configuration of a component of the web form. We obtain from it information about the algorithm and all the conditions due:
 $conditional_values = isset($component['extra']['conditional_values']) ? $component['extra']['conditional_values'] : NULL; $conditional_component = isset($component['extra']['conditional_component']) && isset($node->webform['components'][$component['extra']['conditional_component']]) ? $node->webform['components'][$component['extra']['conditional_component']] : NULL; $conditional_cid = $conditional_component['cid']; 


We also set a flag at the very beginning, which will ultimately determine (depending on the chosen algorithm) whether the component should show or not:
 $one_rule_passed = FALSE; 

The next task is to extend the immediate verification code, which begins immediately after:
 // Check the individual component rules. $show_component = $show_parent; 

We proceed as follows: let us execute each condition in turn, fix the result of the check in the array, which we then analyze according to the algorithm. If the “OR” algorithm is used, then the presence of at least one positive result makes the whole expression positive, but if the algorithm is “AND” , then on the contrary, one negative result makes the whole expression negative.
To do this, replace the verification code with the following:
 // Check the individual component rules. $show_component = $show_parent; if ($show_component && ($page_num == 0 || $component['page_num'] == $page_num) && $conditional_component && strlen(trim($conditional_values))) { $input_values = array(); if (isset($form_state)) { $input_value = isset($form_state['values']['submitted'][$conditional_cid]) ? $form_state['values']['submitted'][$conditional_cid] : NULL; $input_values = is_array($input_value) ? $input_value : array($input_value); } elseif (isset($submission)) { $input_values = isset($submission->data[$conditional_cid]['value']) ? $submission->data[$conditional_cid]['value'] : array(); } $test_values = array_map('trim', explode("\n", $conditional_values)); if (empty($input_values) && !empty($test_values)) { $show_component = FALSE; } else { foreach ($input_values as $input_value) { if ($show_component = in_array($input_value, $test_values)) { break; } } } if ($component['extra']['conditional_operator'] == '!=') { $show_component = !$show_component; } } 


And immediately determine the calculation of the result of the algorithm:
 // Make the final decision if ($component['extra']['algorithm'] == 'or') { $result = $one_rule_passed; } elseif ($component['extra']['algorithm'] == 'and') { $result = array_search(FALSE, $show_component) !== FALSE; } else { $result = TRUE; } // Allow other modules to alter conditional check result $context = array('node' => $node, 'component' => $component, 'show_component' => $show_component, 'one_rule_passed' => $one_rule_passed, ); drupal_alter('webform_conditional_rules_result', $result, $context); // Private component? if ($component['extra']['private']) { $result = webform_results_access($node); } return $result; 

Calling "drupal_alter" will allow other modules to redefine the result as necessary, which you should always remember when developing modules for Drupal.

Now we have everything that is necessary for the implementation of the questionnaire, which is presented above in the article.
To do this, returning to the page with the list of web form components in the administrative interface, we set the following rules for the page_2 component:

image

On page 3 of the rule, only question 4 has a mapping (according to the diagram). We configure them:
  1. Question 1 is one of [yes]
  2. Question 2 is not one of [no]
  3. We specify the algorithm "OR".

On the results page 4, additional information is displayed only when the third question is answered positively.
Define the rule for the last component:
  1. Question 3 is one of [yes].

After all the actions described in the article, you can safely go to the form page from the client side and make sure that everything works as required.

References:

Source: https://habr.com/ru/post/144670/


All Articles