Questions & Surveys

The platform has a built-in system for collecting information from users. Before writing custom data-collection code, check whether Questions, Surveys, or Product Requirements cover your use case.

  • Questions — individual form fields (text, dropdown, radio, checkbox, confirmation)
  • Surveys — ordered collections of questions presented on a standalone page
  • Product Requirements — questions/surveys collected at purchase time (see Product Requirements)

When to use

Use caseTool
Collect responses on a dedicated pageSurvey
Attach questions to a product checkoutProduct Requirements
Reuse the same question in multiple surveys or productsQuestion (shared)
Collect data once per user (e.g. event pre-survey)Survey via Product Requirement

Admin UI

PageURL
All questions/admin/admin_questions
Create / edit question/admin/admin_question_edit
All surveys/admin/admin_surveys
Create / edit survey/admin/admin_survey_edit
View survey responses by user/admin/admin_survey_users
View one user's answers/admin/admin_survey_user_answers
View all answers to a survey/admin/admin_survey_answers

Question types

Defined as constants on the Question class:

ConstantValueRenders as
TYPE_SHORT_TEXT1Single-line text input
TYPE_LONG_TEXT2Multi-line textarea
TYPE_DROPDOWN3Select dropdown (requires options)
TYPE_RADIO4Radio button group (requires options)
TYPE_CHECKBOX5Single checkbox (requires one option — its value)
TYPE_CHECKBOX_LIST6Multi-select checkbox list (requires options)
TYPE_CONFIRMATION7Scrollable body text + checkbox agreement
For TYPE_DROPDOWN, TYPE_RADIO, TYPE_CHECKBOX, and TYPE_CHECKBOX_LIST, options are stored as QuestionOption records linked to the question.

For TYPE_CONFIRMATION, extended config is stored as JSON in qst_config:

{
  "body_text": "<p>Full terms here...</p>",
  "checkbox_label": "I agree to the terms above",
  "scrollable": true
}

Data models

Question         (qst_questions)         — a single question definition
QuestionOption   (qop_question_options)  — options for dropdown/radio/checkbox types
Survey           (svy_surveys)           — a named collection of questions
SurveyQuestion   (srq_survey_questions)  — join: survey ↔ question (with order)
SurveyAnswer     (sva_survey_answers)    — one row per user per question per survey

SurveyAnswer is unique on (sva_svy_survey_id, sva_qst_question_id, sva_usr_user_id) — resubmitting overwrites rather than duplicates.

Public survey page

The route /survey?survey_id={encoded_id} renders a survey and handles submission. Requires a logged-in session (permission 0). On success it redirects to /survey_finish.

survey_id is passed as an encoded integer — use LibraryFunctions::encode($survey->key) to build the URL.

Rendering questions programmatically

Question::output_question($formwriter, $value, $append_text) renders the correct FormWriter field for the question's type, including client-side validation wiring.

require_once(PathHelper::getIncludePath('data/questions_class.php'));

$question = new Question($question_id, true);
$question->output_question($formwriter, $existing_value);

The submitted field name is always question_{question_id}.

Validation rules

A question's rules live in qst_validate as a PHP-serialized array. A rule is in force if its key is present, regardless of the value:

Rule keyServer checkClient rule
requiredanswer is not '', NULL, or an empty selectionrequired
integerctype_digit — digits onlydigits
decimalis_numeric — any numeric valuenumber
min_length / max_lengthstring length boundsminlength / maxlength
min_value / max_valuevalue comparison boundsmin / max
Question::validate_answers($answer) returns the string 'valid' on success, or a human-readable message describing the failure. Question::output_js_validation($rules) produces the client-side equivalent for the same question. The two accept the same set of values by design — a rule tightened on one side must be tightened on the other, or a submission the browser allows is refused by the server.

"0" is a real answer and satisfies required. A checkbox-list answer arrives as an array and is measured as its stored comma-joined form, so max_length bounds what is actually persisted.

Rules are enforced server-side for every question in the survey, whether or not its field was submitted — a required question cannot be satisfied by omitting it from the post.

Reading answers programmatically

// Get one answer
$answer = SurveyAnswer::get_answer($survey_id, $question_id, $user_id);

// Get human-readable answer text (resolves option labels for dropdowns/radios/etc.)
$readable = $question->get_answer_readable($answer->get('sva_answer'));

MultiQuestion filter keys

$questions = new MultiQuestion([
    'deleted'   => false,   // qst_delete_time IS NULL
    'published' => true,    // qst_is_published = TRUE
    'type'      => Question::TYPE_SHORT_TEXT,
]);
$questions->load();

MultiSurveyQuestion filter keys

$survey_questions = new MultiSurveyQuestion(
    ['survey_id' => $survey_id, 'deleted' => false],
    ['srq_order' => 'ASC']
);
$survey_questions->load();