Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

jesse_orange_newable's avatar

Saving drafts in a quiz like app

Hi all,

I'm having a brain fog moment so any ideas would be appreciated.

I have an app where users select yes or no to series of questions and they need to be able to save their current answers as a draft and come back later to finish the questions, so, what should I save in this table?

At current I have a question table

$table->id();
$table->string('question_id');
$table->string('question');
$table->string('type');
$table->smallInteger('yes_weight');
$table->smallInteger('no_weight');

I have a Vue file that renders my questions:

<template>
	<div v-if="questions.length" class="container">
		<div class="row">
			<div class="col-12">
				<div class="row mb-3">
					<div class="col-12 col-md-4">
						<router-link :to="{ name: 'risk-assessment-test' }" class="btn btn-slate btn-block">Back to dashboard</router-link>
					</div>
					<div class="col-12 col-md-4">
						<button @click="saveDraft" class="btn btn-block btn-slate">Save progress</button>
					</div>
					<div class="col-12 col-md-4">
						<button @click="calculateScores" class="btn btn-block btn-slate">Scores on the doors</button>
					</div>
				</div>

				<!-- Loop through the question objects that contain the three types of questions -->
				<div v-for="(question, index) in questions" :key="index" class="row">
					<div class="col-12">
						<!-- Loop through the identification questions -->
						<div v-for="(identificationQuestion, i) in question.identification" :key="'identification_' + i" class="row mb-3">
							<!-- Display the identification question -->
							<div class="col-12 col-md-6 col-lg-9">
								<h5>{{ identificationQuestion.question }}</h5>
								<p>{{ identificationQuestion.type }}</p>
							</div>
							<!-- Display the answers for the identification question -->
							<div class="col-12 col-md-6 col-lg-3">
								<select class="form-control" v-model="answers.identification[identificationQuestion.id]" required>
									<option :value="identificationQuestion.yes_weight">Yes -> {{ identificationQuestion.yes_weight }}</option>
									<option :value="identificationQuestion.no_weight">No -> {{ identificationQuestion.no_weight }}</option>
								</select>
							</div>
						</div>
						<!-- Loop through the assessment questions -->
						<div v-for="(assessmentQuestion, i) in question.assessment" :key="'assessment_' + i" class="row mb-3">
							<!-- Display the assessment question -->
							<div class="col-12 col-md-6 col-lg-9">
								<h5>{{ assessmentQuestion.question }}</h5>
								<p>{{ assessmentQuestion.type }}</p>
							</div>
							<!-- Display the answers for the assessment question -->
							<div class="col-12 col-md-6 col-lg-3">
								<select class="form-control" v-model="answers.assessment[assessmentQuestion.id]" required>
									<option :value="assessmentQuestion.yes_weight">Yes -> {{ assessmentQuestion.yes_weight }}</option>
									<option :value="assessmentQuestion.no_weight">No -> {{ assessmentQuestion.no_weight }}</option>
								</select>
							</div>
						</div>
						<!-- Loop through the control questions -->
						<div v-for="(controlQuestion, i) in question.control" :key="'control_' + i" class="row mb-3">
							<!-- Display the control question -->
							<div class="col-12 col-md-6 col-lg-9">
								<h5>{{ controlQuestion.question }}</h5>
								<p>{{ controlQuestion.type }}</p>
							</div>
							<!-- Display the answers for the control question -->
							<div class="col-12 col-md-6 col-lg-3">
								<select class="form-control" v-model="answers.control[controlQuestion.id]" required>
									<option :value="controlQuestion.yes_weight">Yes -> {{ controlQuestion.yes_weight }}</option>
									<option :value="controlQuestion.no_weight">No -> {{ controlQuestion.no_weight }}</option>
								</select>
							</div>
						</div>
					</div>
				</div>
				<!-- Outer question loop ends here -->
			</div>
		</div>
	</div>
</template>

<script>
	export default {
		components: {},
		mounted() {
			this.retrieveQuestionsForTest();
		},
		data() {
			return {
				test_id: this.$route.params.test_id,
				questions: [],
				answers: {
					identification: {},
					assessment: {},
					control: {}
				}
			};
		},
		methods: {
			retrieveQuestionsForTest() {
				let url = '/api/risk-assessment/questions';
				axios
					.get(url)
					.then((response) => {
						console.log(response.data.questions);
						this.questions = response.data.questions.map((question) => {
							// Add a score property to each question object
							return {
								...question,
								score: 0
							};
						});
					})
					.catch((error) => {
						console.log(error);
					});
			},
			saveDraft() {
				let url = '/api/risk-assessment/draft/' + this.test_id;

				axios
					.patch(url, this.answers)
					.then((response) => {
						console.log(response.data);

						this.$swal.fire({
							customClass: 'w-100',
							position: 'top-end',
							type: 'success',
							title: `Draft Saved`,
							text: 'All progress has been saved.',
							backdrop: false,
							showConfirmButton: false,
							timer: 2500
						});
					})
					.catch((error) => {
						console.log(error);
					});
			},
			calculateScores() {
				console.log('Calculating...');
				this.questions.forEach((question) => {
					let score = 0;
				});
			}
		}
	};
</script>

<style lang="scss" scoped></style>

I have a draft table that stores:

  • A test id
  • A user id

What would be the best way to store the other information, as in the actual answers the user gave?

As I need it so that when a draft is loaded you get all of the previous answers so I was thinking maybe a question answer table to store the test id, question id and the value?

The questions are structured in this way going into the component above:

{
  "message": "Retrieved questions",
  "questions": [
    {
      "identification": [
        {
          "id": 1,
          "question_id": "1",
          "question": "Does the supplier handle personal and/or sensitive data?",
          "type": "identification",
          "no_weight": 1,
          "yes_weight": 10
        }
      ],
      "assessment": [
        {
          "id": 2,
          "question_id": "1",
          "question": "Is the data processed only inside the UK or the EU?",
          "type": "assessment",
          "no_weight": 0,
          "yes_weight": -3
        }
      ],
      "control": [
        {
          "id": 3,
          "question_id": "1",
          "question": "Is the supplier registered with the Information Commissioner Office (ICO)?",
          "type": "control",
          "no_weight": 0,
          "yes_weight": -1
        },
        {
          "id": 4,
          "question_id": "1",
          "question": "Does the supplier have a GDPR policy and GDPR breach process in place?",
          "type": "control",
          "no_weight": 0,
          "yes_weight": -1
        },
        {
          "id": 5,
          "question_id": "1",
          "question": "Does the supplier have controls in place around sharing data with third parties?",
          "type": "control",
          "no_weight": 0,
          "yes_weight": -1
        },
        {
          "id": 6,
          "question_id": "1",
          "question": "Does the supplier have a cyber security accreditation, e.g ISO27001?",
          "type": "control",
          "no_weight": 0,
          "yes_weight": -1
        },
        {
          "id": 7,
          "question_id": "1",
          "question": "Does the supplier have data-security and cyber-security processes in place to deal with threat/loss?",
          "type": "control",
          "no_weight": 0,
          "yes_weight": -1
        },
        {
          "id": 8,
          "question_id": "1",
          "question": "Does the supplier do penetration testing?",
          "type": "control",
          "no_weight": 0,
          "yes_weight": -1
        }
      ]
    },
    {
      "identification": [
        {
          "id": 9,
          "question_id": "2",
          "question": "Is the supplier's environmental impact likely to negatively influence Newable?",
          "type": "identification",
          "no_weight": 1,
          "yes_weight": 10
        }
      ],
      "assessment": [
        {
          "id": 10,
          "question_id": "2",
          "question": "Is the supplier from any of the following industries: oil and gas (etc.)?",
          "type": "assessment",
          "no_weight": -2,
          "yes_weight": 0
        },
        {
          "id": 11,
          "question_id": "2",
          "question": "Is the supplier a service provider?",
          "type": "assessment",
          "no_weight": 0,
          "yes_weight": -2
        }
      ],
      "control": [
        {
          "id": 12,
          "question_id": "2",
          "question": "Does the supplier have an environmental accreditation such as Green Mark or ISO140012?",
          "type": "control",
          "no_weight": 0,
          "yes_weight": -1
        },
        {
          "id": 13,
          "question_id": "2",
          "question": "Does the supplier actively manage its environmental impact?",
          "type": "control",
          "no_weight": 0,
          "yes_weight": -2
        },
        {
          "id": 14,
          "question_id": "2",
          "question": "Does the supplier have an Environment Policy?",
          "type": "control",
          "no_weight": 0,
          "yes_weight": -1
        }
      ]
    },
    {
      "identification": [
        {
          "id": 15,
          "question_id": "3",
          "question": "Does the supplier carry on a regulated activity?",
          "type": "identification",
          "no_weight": 1,
          "yes_weight": 10
        }
      ],
      "control": [
        {
          "id": 16,
          "question_id": "3",
          "question": "Does the supplier have the necessary permission or registration to carry out the regulated activity?",
          "type": "control",
          "no_weight": 0,
          "yes_weight": -5
        }
      ]
    },
    {
      "identification": [
        {
          "id": 17,
          "question_id": "4",
          "question": "Does the supplier's activities have the potential to cause social harm (e.g modern slavery, bribery)?",
          "type": "identification",
          "no_weight": 1,
          "yes_weight": 10
        }
      ],
      "assessment": [
        {
          "id": 18,
          "question_id": "4",
          "question": "Does the supplier employ non-UK or non-EU staff?",
          "type": "assessment",
          "no_weight": -1,
          "yes_weight": 0
        },
        {
          "id": 19,
          "question_id": "4",
          "question": "Is the supplier involved in policy-making/politics?",
          "type": "assessment",
          "no_weight": -1,
          "yes_weight": 0
        },
        {
          "id": 20,
          "question_id": "4",
          "question": "Is the supplier from an industry with higher risk of human rights issues?",
          "type": "assessment",
          "no_weight": -1,
          "yes_weight": 0
        }
      ],
      "control": [
        {
          "id": 21,
          "question_id": "4",
          "question": "Does the supplier comply with labour regulations (e.g protects its employees confidential data)?",
          "type": "control",
          "no_weight": 0,
          "yes_weight": -1
        },
        {
          "id": 22,
          "question_id": "4",
          "question": "Does the supplier have an anti-bribery policy in place?",
          "type": "control",
          "no_weight": 0,
          "yes_weight": -1
        },
        {
          "id": 23,
          "question_id": "4",
          "question": "Does the supplier have a modern slavery policy in place?",
          "type": "control",
          "no_weight": 0,
          "yes_weight": -1
        },
        {
          "id": 24,
          "question_id": "4",
          "question": "Has there been a lot of staff turnover with the supplier?",
          "type": "control",
          "no_weight": 0,
          "yes_weight": -1
        },
        {
          "id": 25,
          "question_id": "4",
          "question": "Does the supplier have health, safety and wellbeing processes in place?",
          "type": "control",
          "no_weight": 0,
          "yes_weight": -1
        }
      ]
    },
    {
      "identification": [
        {
          "id": 26,
          "question_id": "5",
          "question": "Is the supplier critical to Newable's operations?",
          "type": "identification",
          "no_weight": 1,
          "yes_weight": 10
        }
      ],
      "control": [
        {
          "id": 27,
          "question_id": "5",
          "question": "Does the supplier have sufficient financial strength to operate?",
          "type": "control",
          "no_weight": 0,
          "yes_weight": 0
        },
        {
          "id": 28,
          "question_id": "5",
          "question": "Are the supplier's latest FY audited accounts filed at Companies House?",
          "type": "control",
          "no_weight": 0,
          "yes_weight": 0
        },
        {
          "id": 29,
          "question_id": "5",
          "question": "Is the contract with the supplier recurring or longer than 3 months?",
          "type": "control",
          "no_weight": 0,
          "yes_weight": 0
        },
        {
          "id": 30,
          "question_id": "5",
          "question": "Does the supplier have any claims pending or have had any in the past five years?",
          "type": "control",
          "no_weight": 0,
          "yes_weight": 0
        },
        {
          "id": 31,
          "question_id": "5",
          "question": "Is the company's current financial performance satisfactory?",
          "type": "control",
          "no_weight": 0,
          "yes_weight": 0
        },
        {
          "id": 32,
          "question_id": "5",
          "question": "In the last 4 years, has the supplier made a significant loss?",
          "type": "control",
          "no_weight": 0,
          "yes_weight": 0
        },
        {
          "id": 33,
          "question_id": "5",
          "question": "Does the supplier use IT and/or systems for the delivery of the service?",
          "type": "control",
          "no_weight": 0,
          "yes_weight": 0
        },
        {
          "id": 34,
          "question_id": "5",
          "question": "Is the contract size critical to Newable's operations?",
          "type": "control",
          "no_weight": 0,
          "yes_weight": 0
        },
        {
          "id": 35,
          "question_id": "5",
          "question": "Does the supplier have a good track record for customer service?",
          "type": "control",
          "no_weight": 0,
          "yes_weight": 0
        }
      ]
    }
  ]
}
0 likes
2 replies
PovilasKorop's avatar

I think you should store them exactly as you would store them in non-draft, just with another field of is_draft 0 or 1 values.

jesse_orange_newable's avatar

@PovilasKorop I will try this approach. It's mainly because the yes and no weightings affect an overall score so I needed to maintain some kind of order.

question_id is probably not the best name but it essentially denotes what question is linked or related to another question.

Also, I've seen your stuff on YouTube :)

1 like

Please or to participate in this conversation.