Issue
I have Choice
model which belongTo Question
model which belongTo Quiz
model. To be exact:
- A
Quiz
has exactly 4questions
. - A
Question
has exactly 4choices
.
The mentioned relationships are well received in a the Request
(showed below).
I need to do all the inserts in three transactions.
I have the following two successful transactions.
DB::transaction(function () use ($request) {
$quiz = Quiz::create(['start_at' => $request->start_at, 'duration' => $request->duration]);
$questions = $quiz->questions()->createMany($request->questions);
// ...
Now I need the confirmation that $questions
will be similar to $request->questions
in the order of the data under all circumstances, to rely on that fact and map id
from the returned collection to the choices from the Request
as foreign ids and do one more bulk insert.
// ...
$choices = [];
foreach ($request->questions as $i => $question_data) {
foreach ($question_data['choices'] as $j => $choice_content) {
$choices[] = [
'question_id' => $questions[$i - 1]->id, // Append foreign id
'content' => $choice_content,
'choice_number' => $j,
'is_correct' => $j == $question_data['is_correct'],
'created_at' => date('Y-m-d H:i:s'),
];
}
}
Choice::insert($choices);
});
{
"start_at": "2022-07-17T19:54",
"duration": "191",
"questions": {
"1": {
"content": "Sed asperiores eaque voluptatem id saepe.",
"choices": {
"1": "Ad quia impedit libero voluptatem qui.",
"2": "Voluptates quis consequuntur natus illum laborum tempore.",
"3": "Corrupti dolorum optio quam qui.",
"4": "Numquam quidem voluptatem nisi."
},
"is_correct": "4"
},
"2": {
"content": "Illum ut tempora.",
"choices": {
"1": "Consequatur minima tempora qui amet.",
"2": "Voluptate sint sapiente illum delectus possimus enim.",
"3": "A magni aut aperiam aliquam laboriosam.",
"4": "Faustino MacGyver"
},
"is_correct": "4"
},
"3": {
// ...
},
"4": {
// ...
}
}
}
Solution
Laravel’s createMany
does a foreach
on the array you provide it and returns it in the order that you passed it in. It should retain the order.
This is the source:
public function createMany(iterable $records)
{
$instances = $this->related->newCollection();
foreach ($records as $record) {
$instances->push($this->create($record));
}
return $instances;
}
This does not necessarily apply to subsequent queries of that data. That is up to the database how it decides to order it in the absence of an order by clause.
That having been said, you can avoid the entire issue by setting the choices as you create each question. Since you have a relationship, you can allow Laravel to figure out how to set the proper ID’s.
foreach ($request->questions as $question_data) {
$question = $quiz->questions()->create([
// question data here
]);
foreach ($question_data['choices'] as $j => $choice_content) {
$question->choices()->create(
[
// choice data here
]
);
}
}
Answered By – Ray Dabbah
Answer Checked By – Katrina (BugsFixing Volunteer)