====== Filter Questions (PHP Filters) ====== If the participant's answer to a question is to have an effect on the further course of the interview, it is a filter question. You can define filters either directly in the question ([[en:create:simple-filters|question filter]], simple variant) or in the **questionnaire** (//PHP filter//). PHP filters allow much more sophisticated filters than question filters, but require the use of a little PHP program code. A warning in advance: Most project administrators that use SoSci Survey have never before written computer programming code. You'll learn some very basic knowledge about programming with PHP in this chapter. __Don't worry__: The first step is always the hardest. You'll see that it's not as hard as it may seem – and you'll be awarded by a steep learning curve. Some little frustration, you may suffer, will be compensated by a even more happy aha! **Important**: For a better understanding, we recommend the chapter [[en:create:php|Introduction to PHP]]. **Important**: Should a filter not work as it should, please take a look into [[en:create:filter-solving|Problem solution for filters]]. **Important**: SoSci Survey saves all data from a respondent. If you allow a back button, the following situation may occur: A respondent answers question A, goes back, changes a filter question, and will not be presented question A any more in the subsequent questionnaire. The answers given to question A are still in the data record – although the data tells that this respondent should not have seen question A. **Tip**: If you separate respondents into a control group and an experimental group ([[en:create:questions:random|Random generator]], [[en:create:randomization|Randomization]]) and present them different questionnaire pages, it may be useful to (early) use ''[[:en:create:functions:setpageorder]]'' instead of ''[[:en:create:functions:gotopage]]'': The earlier in the interview SoSci Survey knows that pages will be skipped, the better can it adapt the progress bar. ===== Example: Yes-No Filters ===== Before explaining filters and their function in depth, we will show you a quick-and-dirty solution for those of you who don't have the time. A Yes-No-Filter is probably the most used filter. **Example 1:** If a questionee answers "yes", another question is shown. **Example 2:** If a questionee answers "no", multiple pages are skipped. ==== Preparations ==== What you need to know to implement the filter: * The ID of the variable that contains the yes-no-question * the answer codes of this question * the page on which the questionnaire should continue, in case the questionee answers “no” You will find the ID of the variable in the **Variables Listing**. This could look like this: [PT01] Dropdown selection TV usage PT01 TV usage 1 = yes 2 = no -9 = no answer So the ID of the variable is “PT01”, the answer code for “yes” is “1” and the code for “no” is “2”. Ideally, you have configured the question to demand a complete answer (tab //Check for completion//). In this case, the code -9 won't be applicable. **Attention:** Should there be more variables, e.g. ''PT01_01'' and ''PT01_02'', check the question type. It is important you chose the question type "Selection" or "Dropdown Selection" if you only allow the choice of one option, __not__ "Multiple Choice". ===== Entering a PHP Code ===== The filter should be entered as a //PHP code// when **Creating A Questionnaire**, at the exact point where the filter should be active (e.g. where the second question is shown or skipped). Simply drag-and-drop a //PHP code// from the right sight into your questionnaire page ([[::en:create:php|Introduction to PHP]]). **Important:** A PHP filter can __never__ be on the same page as the filter question (the question the answer refers to, in this case ''PT01''). SoSci Survey loads the questionnaire page by page in order to show the questionee the whole page. The moment the page with the filter question loads, the questionee cannot possibly have answered the questions - and thus the filter cannot react to the answer (''[[:en:create:functions:value|value()]]''). ===== Frequent Use Cases ===== If you can handle the PHP function ''[[:en:create:functions:value]]'', you can implement a number of filters with it. ==== Whether to Display a Question or Not ==== The following PHP code uses ''[[:en:create:functions:value]]'' to check whether response code ''1'' has been stored for variable ''PT01''. If this is the case, the question ''AB01'' is displayed using ''[[:en:create:functions:question|question()]]''. Note the double equal sign (''=='') after the ''value()'' command. if (value('PT01') == 1) { question('AB01'); } **Tip**: This filter can also be used as a [[:en:create:simple-filters]] without PHP code. **Tip**: If you want to save yourself some typing work, simply drag the question you want to display from the right into the PHP code. Then you can adjust the display options of the question. After confirming with the green check mark, the PHP code for the question will be appended at the bottom of the //PHP code// element. Then all you have to do is move the code to the right place (between the curly brackets). **Tip**: You can also write several ''question()'' commands between the braces ''{'' and ''}''. ==== Show Different Questions ==== Depending on a previous answer (or a group assignment, e.g. by [[:en:create:randomization]]), should question A or question B be shown? In the following example, depending on the answer to a selection question (AF01), either question FF01 or FF02 is displayed. if (value('AF01') == 2) { question('FF01'); } else { question('FF02'); } **Important**: Questions FF01 and FF02 __must not__ be pulled to the side. Otherwise they will be displayed twice: once by the ''question()'' command and once by being dragged to the side. If the question FF01 is to be displayed for multiple answers in AF01, the [[:en:create:filter-boolean]] ''or'' is used. if ((value('AF01') == 2) or (value('AF01') == 4)) { question('FF01'); } else { question('FF02'); } It can also be selected from more than 2 questions. if (value('AF01') == 1) { question('FF01'); } elseif ((value('AF01') == 2) or (value('AF01') == 4)) { question('FF02'); } else { question('FF03'); } ==== Vary Content ==== Instead of questions, contents, e.g. stimuli, can also be varied. More about this in the chapter [[:en:create:randomization]]. In the following example - depending on the code in variable AF01 - either the text module with the identifier "stimulus1" or "stimulus2" is displayed. if ((value('AF01') == 2) or (value('AF01') == 4)) { text('stimulus1'); } else { text('stimulus2'); } ==== Skip Pages ==== When **compiling the questionnaire**, you now select the page on which you want to continue with a "no". Enter an //identifier// for this page, e.g. "no TV" (assign an identifier for a page). Then go to the page where you have placed the //PHP code// element for the filter (see above). Write the following: if (value('PT01') == 2) { goToPage('keinTV'); } This code uses ''[[:en:create:functions:value|value()]]'' to check whether the value 2 was stored in the variable PT01. If this is the case, processing with ''[[:en:create:functions:gotopage|goToPage()]]'' jumps directly to the page with the identifier "noTV". Subsequent contents of the current page are ignored. **Tip**: Read the rest of this chapter. It's worth it! ===== The Keyword IF ===== Filters are programmed through IF-THEN-ELSE-relations in the element //PHP-code//. **If** and **else** are the keywords in programming language. Following **if**, you set the condition into brackets. If this condition is complied with, the command following the condition will be executed: ''**if (**condition**)** demand'' Additionally you may add the command **else** that will be executed in case the condition above is not fulfilled: ''**if (**condition**)** command_1 **else** command_2'' In order to present everything clearly (and to allow multiple commands), the commands are written in curly brackets (''{'' and ''}''). The indent simplifies the readability. if (condition) { command_1 } else { command_2 } ===== Show or do not Show Questions in the Questionnaire ===== A filter always uses two things: A cause and a consequence on the questionnaire's process. * The cause is usually an answer by the questionee * The most common consequence is a certain question (or a whole block of questions) be shown or not. The following example bases on a question on page 2 of the questionnaire, asking the questionee how much he earns per month. He had to choose between several categories: 1=no income, 2=up to 500$, and so on. This will be the filter question. It's important to know that SoSciSurvey doesn't know the questionees answer when building page 2. The server won't know this until the questionee has answered the question and pressed the “next” button (this is when the data will be sent back to the server). This is why putting filter questions and their respective filters on the same page won't work. After getting the questionees data through him pressing “next”, the server can start building page 3. {{fig.filters.principle.png?nolink|Prinzipielle Funktion von Filtern durch Springen}} Now, what if the questions on page 3 should be skipped if any option except option 1 was chosen? (in other words: Page 3 should only be shown if the questionee chose option 1) We need to think a little further: To achieve this, the filter needs to redirect all questionees who did __not__ choose option 1 to page 4. This is how most filters work in SoSciSurvey: You skip certain questions or whole pages. In some cases you need to cling several filters together. An example: on page 10 you show a multiple choice question with 5 car brands to choose from. About each brand, you want to ask 4 questions on 2 pages. To do so, you will need 5 filters. The first filter will be on page 11. It checks whether option 1 was chosen. If not, it will skip page 11 and 12 and redirect to page 13 (because the questions about brand 1 were spread about two pages). On page 13 the next filter kicks in: it checks whether option 2 was chosen. Then it goes on like this. When the filter reaches a point where an option was actually chosen (let's say, option 3 was checked) then the filter on page 15 does not redirect but asks the questions on page 15 and 16, right until page 17 where the next filter checks if option 4 was chosen. {{fig.filters.jump.png?nolink|Überspringen von Fragen durch Filter}} ===== Conditions ===== A condition is always put into round brackets (). It may be complied with (the condition will return “true”) or not (“false”). A condition can look differently. The most common use is the comparision of two values (e.g. two numbers): * ''=='' (two equal signs) checks if two values are the same, * ''!='' (exclamation mark and equal sign) checks if two values are not the same, * ''>'' (greater than sign) checks if value one is greater than value two (only works with numerical values), * ''<'' (less than sign) checks if value one is less than value two (only works with numerical values). **Attention**: To check if two variables are the same, always use two equal signs (''=='')! Using only one equal sign (''='') will try to allocate a value to a variable! **Tip**: To check if a variable has the value 1 __or__ 2, read [[en:create:filter-boolean|]] ===== Skip Pages ===== Back to the simple example: On page 1 of the questionnaire we ask the question AB01 (selection about the income). Our goal is to ask the questions EK01 and EK 02 only if the questionee chose option 1 in AB01. This means also: if the questionee chooses an option greater than 2 (income up to 500$), page 3 will be skipped. The following example describes a questionnaire with 3 pages. On page 2 a filter will check whether the questionee has selected an income greater than 500$. In this case, the rest of the page will be skipped and the questionnaire continues on page 3. **Tip**: How to implement php in the questionnaire is fully described in the chapter [[php|Introduction to PHP]]. Essentially, to use filters you need to drag-and-drop the element //PHP-Code// (above the list of questions in the “compose questionnaire”-area) into the questionnaire. **Tip**: You don't need to enter the questions themselves as PHP-code - you can still drag-and-drop them from the selection list into the page ([[php#Comfortable_programming_questions_and_ texts|Comfortable Programming]]) ===== Assigning Page IDs ===== For page 3 in the questionnaire you will need a page ID. Let's use “tvconsum”. We need this to be able to jump to this page using ''[[:en:create:functions:gotopage|goToPage()]]'' The ID can simply be assigned when composing the questionnaire. Click on the page and enter the ID on the upper left. {{scr.filters.page_ident.png?nolink|Eine Seiten-Kennung zuweisen}} ==== Using as Little PHP Code as Possible ==== {{scr.filters.simple.page1.png?nolink|Seite 1}} {{scr.filters.simple.page2.png?nolink|Seite 2}} {{scr.filters.simple.page3.png?nolink|Seite 3}} ==== Using as much PHP Code as Possible ==== Usually you will try to use PHP-code only where needed – like above. Just because questionnaires can be written much quicker in PHP, this manual often uses the following form. Contentwise, the following form is identical to the above. **Page 1** question('AB01'); // Monthly income **Page 2** if (value('AB01') > 2) { goToPage('tvconsumption'); // Directly goes to page 3 } question('EK01'); // Study funding question('EK02'); // Other income **Page 3 with ID "tvconsumption"** // Page 3 must have the ID "tvconsumption" question('TK01'); // Daily TV consumption question('TK02'); // Favorite TV station **Important**: Questions can be dragged-and-dropped into the questionnaire page as well as being implemented via ''question()'' into PHP. While you are technically able to do both, doing so will result in the question being displayed twice and receiving respective error messages. ==== Tips about the Function value() ==== * There are two easy ways to find out which value will be saved in an answer: The **Variables listing** and the debug mode while testing the questionnaire ([[questionnaire|Create a Questionnaire]]). * The function ''[[:en:create:functions:value|value()]]''prints the answer the questionee gave. This will only work after the questionee has hit the “next”-button after he answered the question. * All functions specific to SoSciSurvey are listed and explained at [[functions|PHP functions]]. ===== Instruction Blocks ===== The example shows a curly bracket following the if-condition (''{''). Curly brackets in PHP summarize several functions/commands (e.g. several questions). Although there is not always more than one command following the IF-condition, we recommend that you always use the brackets: If you add another command later on and forget the brackets, you will most probably spend a long time figuring out why that error occurs. **Tip**: Use curly brackets for every instruction block and indent the PHP-code using spaces (don't use tabs as the entry fields have problems with those). This way you won't lose the overview even if writing long passages of php. **Example for page 2** if (value('AB01') <= 2) { question('AB02'); // Study funding question('AB03'); // Other income } else { question('AB04'); // Working hours per week question('AB05'); // Employment question('AB06'); // Employment limitation } question('AB07'); // This question will be visible to everyone ===== Step by Step ===== This paragraph explains the programming of a small filter step by step. ==== The Filter Question ==== Back to using IF-THEN-conditions as filter questions: Every question can become a filter question, when its answer is used as a condition. First, create any question. - Create a new section in the **list of questions**, //ID// “TF”, //Name// “Filter test” - Create a new question, //Name// “Filter selection”, //Type// “Selection” - Enter the following into the question TF01: - //Question title// “Do you have an internet connection at home?”, - //This question demands a complete answer// “Yes”. - Click //save// - In the //quick input section// for options, enter two items: “yes” and “no”. {{scr.filters.question_yes_no.png?nolink|Eine Auswahl als Filterfrage}} Look at the **variables listing**. You will find the following {{scr.filters.varlist.select.png?nolink|Auswahl in der Variablen-Übersicht}} The ID of the question is TF01. The question type is selection, so the answers are saved right under the ID TF01 – if the question would be a scale for example, the answers would be saved below the items instead of the ID. The variables listing tells you the values TF01 can contain: 1 for the answer “yes” and 2 for “no”. -9 is also possible (the question was not answered), though it should not occur as we configured the question to demand a complete answer. You find more details in the chapter [[:en:results:values|Codes and output values]]. Create a new questionnaire at **compose questionnaire**, //Name// “filter1”, //Title// “My first filter question”. ** Tip**: The option **This question demands a complete answer** should in general be used very sparse. But as filters need to decide things upon this answer, it is recommended to use the option for filter questions. ==== Filters about the Selection ==== To use TF01 as filter question, you need the command ''[[:en:create:functions:value|value()]]''. This function looks up, which answer the questionee gave to this question. In our example, the command ''%%value('TF01')%%'' would print 1 or 2 (not -9 because it must be answered). As usual, the requirement is that the question was asked on a questionnaire page before the current one. If the question TF01 was asked on page 1 of the questionnaire, a PHP-Code on page 2 would look like this: if (value('TF01') == 1) { question('IN01'); // Type of internet connection question('IN02'); // Commercial use of the internet connection? question('IN03'); // Service provider } question('AB01'); // Job description The condition checks, whether the answer was equal to 1. In our words, whether the questionee has chosen “yes”. If this condition is true, page 2 will ask three questions about the internet usage (IN01 to IN03). All questionees – regardless of their answer to the filter question TF01 – will see the question AB01. Did the questionnee answer “no” to TF01, he will only see one question on page 2: AB01. Did he answer “yes”, he will see four questions in total on page 2. ==== Using else ==== Using the command else you can ask questions that should be shown, in case the IF-condition returns “false”. In our example, we could ask, why the questionee does not have an internet connection: if (value('TF01') == 1) { question('IN01'); // Type of internet connection question('IN02'); // Commercial use of the internet connection question('IN03'); // Service provider } else { question('IN04'); // Why no internet connection at home? } question('AB01'); // Job description ===== Skip Pages ===== If not using any ''else''-commands and having no other questions on the page, it can happen that the software loads an empty page. Although SoSciSurvey will not show empty pages to questionees since version 2.2.02, this happening is considered bad programming. Empty pages would happen in the following example, if the questionee had answered "no": if (value('TF_01') == 1) { question('IN_01'); // Type of internet connection question('IN_02'); // Commercial use of the internet connection question('IN_03'); // Service provider } In this and some other cases, you better use the command ''[[:en:create:functions:gotopage|goToPage()]]''. This will directly jump to the stated page in the questionnaire. In the example above, page 2 is only relevant for people with internet connection. All others should jump to page 3. To use the ''goToPage()''-command, you need to configure an ID for the page the questionnaire should jump to. In the following example the target page has the ID ''%%"usage"%%''. If you only need to jump to the page following the current one, you can also use the ID ''%%"next"%%''. The following examples will output the same page (if the questionnee has answered “no”), because following the ''goToPage()'' command, nothing will be executed. The ''goToPage()''-command leaves the current page and will never return. ** Option 1: Jump to page “usage” if there's nothing to ask ** if (value('TF_01') == 1) { question('IN_01'); // Type of internet connection question('IN_02'); // Commercial use of the internet connection question('IN_03'); // Service provider } else { goToPage('nutzung'); // Jump to page 3 with the ID "usage" } ** Option 2: Skip the irrelevant questions ** if (value('TF01') != 1) { // You could also check if: value() == 2 goToPage('usage'); // Jump to page 3 with the ID "usage" } question('IN_01'); // Type of internet connection question('IN_02'); // Commercial use of the internet connection question('IN_03'); // Service provider Example 2 can be used to skip more than one page as well. **Tip**: Asking some benchmark data reveals the questionee is not of interest to you? [[#screenoutfilter_unsuitable_participants|Screenout: Filter unsuitable participants]] **Tip**: Ever since SoSci Survey Version 2.2.02 empty pages will be skipped by default. Thereby you often times don't have to worry about empty pages. ===== The Proper ID ===== The example uses a selection. A simple selection will save just one value, the chosen option. This can be called with ''value()'' and the ID (e.g. TF01). If using scales, text entry fields or multiple choice questions, you need to use the ID of the respective item, not the question (e.g. TF01_02)! {{scr.filters.multiselect.png?nolink|Beispiel für eine Mehrfachauswahl TF02l}} The multiple choice selection TF03 contains 4 items. The item with the ID 3 (TF03_03) asks if the questionee uses a mobile phone to access the internet. You will always find the right ID in the **variables listing**. {{scr.filters.varlist.checkbox.png?nolink|Variablen einer Mehrfachauswahl}} You get the following display in the debug mode, if you click //next// on Page 1 (showing question TF02). In the example, the first and third item were checked. {{scr.filters.debug.png?nolink|Debug-Information beim Testen des Fragebogens}} All items of a multiple choice question will either have the value 1 (not selected) or 2 (selected). If you want to filter the questionees that use the mobile phone on page 2, you use the following PHP-code: ** Filter for multiple choice selections ** if (value('TF02_03') == 2) { // Has item 2 been selected? question('IN10'); // Download speed question('IN11'); // Usage of video conferences } else { question('IN09'); // Internet connection at home? } A big drawback to multiple choice questions is the fact, that you cannot check if the questionee has answered the question as no checked items is considered a valid answer. You can avoid this problem by using a 2-step-scale instead of a multiple choice selection and naming the extremes “yes” and “no”. So you will get the same values for all items (1 for no, 2 for yes) and an additional “-9”-value for items that were not answered. In the example we chose the minimum “no”, the maximum “yes” and the direction of scale as //descending//, because the “yes”-option should show on the left. {{scr.filters.example_scale.png?nolink|Variablen in einer Skala}} The chapter [[filter-itemcount|At least one item]] shows some tricks on how to use scales in filters. ===== Screenout: Filter Unsuitable Participants ===== In case your survey has a certain target group, you might want to filter unsuitable participants and dismiss them after some screening questions. To do so, ask the screening questions early in the questionnaire and use a common filter afterwards. * Using the command ''[[:en:create:functions:gotopage#end_questionnaire|goToPage('end')]]'' will lead your questionnee to the end of the questionnaire. The interview will be marked as completed and the questionnee will be shown the sendoff. * Combining the commands ''[[:en:create:functions:text|text()]]'' and ''[[:en:create:functions:buttonhide|buttonHide()]]'' can be used to show an individual sendoff. The interview will __not__ be marked as “completed”. Save the sendoff text in **Text elements and labels**. // Screenout with individual text if (value('AB01') > 3) { text('screenout'); // Show send-off buttonHide(); // Hide the next-button pageStop(); // In case other content shows with the filter } **Tip**: By repeating a filter you are able to check different variables. Using boolean operators you can check several variables in one filter ([[filter-boolean|Linking Multiple Conditions]]). ===== Tips about Filters / Problem Solving ===== Filters are very flexible tools. You can nest them if needed. You can check complex conditions. We will explain more in further chapters (see below). Usually filters don't work as needed on the first try. If things don't work, there are several possibilities to check for errors: * Check the **Variables listing** whether you have got the right variables ID. * The filter must not be on the same page as the filter question (!) In that case the filter will be executed before the questionee has answered the question. * Check your questionnaire in debug mode (Start questionnaire in debug mode). In that case, SoSciSurvey will tell you the submitted value of every ID after hitting //next//. * The debug mode is also helpful, in case of empty pages or unexpected outcomes of goToPage()-commands. For every jump using goToPage(), debug information will show the line “create Page X”. * SoSciSurvey can tell you what value was saved under a certain item/a certain question. * You can print the value of value(): \\ ''debug('TF02_03');'' * You can print whether a condition returns true (it is complied) or false (not complied):\\ ''debug(value('TF02_02') == 2);''. * Jumping through pages using ''goToPage()'' is recommended only, if questions should be skipped. In case you want to ask alternate questions, the way to do so is by implementing them into the IF-block. * The command ''goToPage()'' will immediately build the stated page (not after hitting //next//). It will attach this page to the current page (if there were questions asked before the filter). * After having jumped through pages using ''goToPage()'' and hitting next, the questionnaire will continue normally. ===== Advanced Filters ===== Most filters can be sufficiently programmed using the above programming basics. But not all. The following chapters will show you solutions for more complex filters: * [[filter-texts|Filter by Text Input]] * [[filter-boolean|Linking Multiple Conditions]] - Logical Operators * [[filter-elseif|Conditions with More Than 2 Options]] - 1, 2 or 3 * [[filter-itemcount|At Least One Item Selected]] - Loops * [[filter-items|Use Selected Items in Another Question]] - Arrays