Search code examples
phpyii2many-to-many

Yii2 - Quiz - Work with many-to-many table


I need development something similar a quiz. I have 3 tables: quiz, quiz_questions, quiz_questions_answers.

I render the questions / answers like this in form.

<?php foreach ($modelQuestions as $modelQuestion): ?>
            <?= $modelQuestion->question ?> <br/>
            <?= $form->field($modelMatch, 'answer[]')->textarea(['rows' => 6]) ?>
<?php endforeach; ?>

in controller i need save id_quiz and obs in table quiz, but i need save in many-to-many table quiz_questions_answers like id_quiz, id_question and answer for each answers.

I'm trying this in foreach loop, but how i get the "id_question" for each answer?

public function actionCreate()
    {
        $model = new Quiz();
        $modelMatch = new QuizQuestionsAnswers();
        $modelQuestions = QuizQuestions::find()->all();


        if ($model->load(Yii::$app->request->post()) && $modelMatch->load(Yii::$app->request->post())){
            $model->save();

            foreach ($modelMatch->answer as $answer) {
                    $modelMatch = new QuizQuestionsAnswers();
                    $modelMatch->id_quis = $model->id;
                    $modelMatch->id_question = ????;
                    $modelMatch->answer = $answer;
                    $modelMatch->save();
            }

            return $this->redirect(['view', 'id' => $model->id]);

        } else {
            return $this->render('create', [
                'model' => $model,
                'modelMatch' => $modelMatch,
                'modelQuestions' => $modelQuestions,
            ]);
        }
    }

this is a scenario create, and after i need prepare this for the scenario update. i'm lost..

the schema database is:

enter image description here


Solution

  • You should add the id_question as the hidden input and pre-populate the field as you have the question already saved in the database and use tabular approach for field names you should have the [] in the start not in the end, also you should group the answer with the question by using the $modelQuestions index so the answer and the question that it belongs to all are in separate arrays see the below code

    <?php foreach ($modelQuestions as $index=>$modelQuestion): ?>
        <?php echo $modelQuestion->question ?> <br/>
        <?php echo $form->field($modelMatch, "[$index]answer")->textarea(['rows' => 6]) ?>
        <?php echo Html::activeHiddenInput($model,"[$index]id_question",['value'=>$modelQuestion->id]) ?>
    <?php endforeach;?>
    

    Now when you will submit your for you will see the answers in the post array like below

    Array(
    .....
        'QuizQuestionsAnswers'=>[
            [0]=>[
    
                'answer'=>'some answer'
                'id_question'=>1,
            ],
            [1]=>[
                'answer'=>'some answer',
                'id_question'=>2,
            ],
        ],
    .....
    )
    

    Now you should save every answer. And for doing this you should use the transactions so that if any of the answers is not validated according to the model rules it should throw an error and should not save any of the answers or the quiz.

    So you should change the code to the below

    public function actionCreate()
    {
        $model = new Quiz();
        $modelMatch = new QuizQuestionsAnswers();
        $modelQuestions = QuizQuestions::find()->all();
    
        if ($model->load(Yii::$app->request->post())) {
            $transaction = Yii::$app->db->beginTransaction();
    
            try {
                if (!$model->save()) {
                    throw new \Exception(implode("<br />", ArrayHelper::getColumn($model->errors, '0')));
                }
    
                foreach (Yii::$app->request->post('QuizQuestionsAnswers', []) as $answer) {
                    $modelMatch = new QuizQuestionsAnswers();
                    $modelMatch->id_quis = $model->id;
                    $modelMatch->id_question = $answer['id_question'];
                    $modelMatch->answer = $answer['answer'];
                    if (!$modelMatch->save()) {
                        throw new \Exception(implode("<br />", ArrayHelper::getColumn($modelMatch->errors, '0')));
                    }
                }
    
                $transaction->commit();
    
                return $this->redirect(['view', 'id' => $model->id]);
            } catch (\Exception $e) {
                $transaction->rollBack();
                Yii::$app->session->setFlash('danger', $e->getMessage());
            }
    
        }
        return $this->render('create', [
            'model' => $model,
            'modelMatch' => $modelMatch,
            'modelQuestions' => $modelQuestions
        ]);
    
    }