Skip to content

Instantly share code, notes, and snippets.

@briceburg
Last active August 29, 2015 14:01
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save briceburg/12235673995d2eb3888f to your computer and use it in GitHub Desktop.
Save briceburg/12235673995d2eb3888f to your computer and use it in GitHub Desktop.
MultiFormMultiStep > A SilverStripe MultiForm Step allowing for a dynamic number of questions/answers. Useful for CMS generated surveys &c.
<?php
/**
* MultiFormMultiStep allows answers to be collected for a dynamic number of
* questions as a single MultiFormStep. This is useful if you have an unknown
* amount of questions (e.g. dynamically generated by a CMS based survey maker).
*
* Define the next and previous steps as usual -- but to get to the next step,
* isLastQuestion must return true. Similarly, to get to the previous step
* isLastQuestion must return true.
*
* The following is simplified example assuming Questions are contained in a
* DataList of MultiFormQuestion objects. Answers can be yes/no. I have
* extended to for more complicated scenarios such as dynamic answer texts
* and user-selectable questionnaires.
*
* MultiFormQuestion extends DataObject with QuestionText and ID fields
* QuestionnaireFormStepFirst extends MultiFormStep and is the previous step
* QuestionnaireFormStepLast extends MultiFormStep and is is the next step
*
* @author Brice Burgess @iceburg.net
*
*/
class MultiFormMultiStep extends MultiFormStep {
private $initialized = false;
private $answers = null;
private $skip_fields = array('question-id');
/**
* @var RIMQuestionnaire $questionnaire
*/
private $questionnaire = null;
/**
*
* @var RIMQuestion $current_question
*/
private $current_question = null;
public function setForm($form) {
$ret = parent::setForm($form);
$this->initializeStep();
return $ret;
}
public function getNextStep() {
return ($this->isLastQuestion()) ?
'QuestionnaireFormStepLast' :
__CLASS__;
}
public function getPreviousStep() {
return($this->isFirstQuestion()) ?
'QuestionnaireFormStepFirst' :
__CLASS__;
}
public function getFields() {
if(!$this->initialized) {
$this->initializeStep();
}
if(!$this->current_question) {
return $this->redirectHome('Questionnaire reached an invalid state.');
}
$answer_key = 'answer:' . $this->current_question->ID;
$fields = new FieldList(
new LiteralField('question-text', $this->current_question->QuestionText),
new HiddenField('question-id','question-id',$this->current_question->ID)
);
$fields->add(
new OptionsetField($answer_key, 'Please Choose', array(1 => 'yes', 0 => 'no')));
return $fields;
}
public function loadData() {
// cache loadData as $this->answers to avoid successive serialization
if(!$this->answers) {
$this->answers = parent::loadData();
}
return $this->answers;
}
public function saveData($data) {
// save multiple answers for this step
$answers = $this->loadData();
$data = array_merge((array) $answers, $data);
foreach($this->skip_fields as $key) {
if(isset($data[$key])) {
unset($data[$key]);
}
}
return parent::saveData($data);
}
public function Link() {
$question_id = null;
$request = $this->getForm()->getController()->getRequest();
if($request->requestVar('action_prev')) {
$question_id = ($this->current_question) ?
$this->getPreviousQuestionID() :
$this->getLastQuestionID();
}
elseif($request->requestVar('action_next')) {
$question_id = ($this->current_question) ?
$this->getNextQuestionID() :
$this->getFirstQuestionID();
}
return ($question_id) ?
Controller::join_links($this->form->getDisplayLink(), "?question-id={$question_id}&MultiFormSessionID={$this->Session()->Hash}") :
Controller::join_links($this->form->getDisplayLink(), "?MultiFormSessionID={$this->Session()->Hash}");
}
public function getNextQuestionID() {
$next = false;
foreach(MultiFormQuestion::get() as $question) {
if($next) {
return $question->ID;
}
if($question->ID == $this->current_question->ID) {
$next = true;
}
}
return 0;
}
public function getPreviousQuestionID() {
$prev_question_id = 0;
foreach(MultiFormQuestion::get() as $question) {
if($question->ID == $this->current_question->ID) {
return $prev_question_id;
}
$prev_question_id = $question->ID;
}
return 0;
}
/**
* Set the current question by ID
* @param int $id
*/
public function setQuestionByID($id) {
if($question = DataObject::get_by_id('MultiFormQuestion', $id))
$this->setQuestion($question);
}
/**
* Set the current question
* @param MultiFormQuestion $question
*/
public function setQuestion($question) {
$this->current_question = $question;
}
public function initializeStep() {
if($this->initialized) return;
// set the question
$request = $this->getForm()->getController()->getRequest();
if($question_id = $request->requestVar('question-id')) {
$this->setQuestionByID((int) $question_id);
}
// mark initialized
$this->initialized = true;
}
public function isLastQuestion() {
return ($this->getLastQuestionID() == $this->current_question->ID);
}
public function isFirstQuestion() {
return ($this->getFirstQuestionID() == $this->current_question->ID);
}
public function getFirstQuestionID() {
$first = MultiFormQuestion::get()->first();
return $first->ID;
}
public function getLastQuestionID() {
$last = MultiFormQuestion::get()->last();
return $last->ID;
}
public function redirectHome($msg = null) {
$msg = ($msg) ? '?msg=' . urlencode($msg) : '';
$controller = $this->getForm()->getController();
$controller->redirect($controller->Link() . $msg);
return new FieldList();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment