4
\$\begingroup\$

I'm working on a quiz application for training purposes and I think I'm done with most of it.

However, I'm not comfortable with what I've done: it seems to be too complicated and unprofessional, and I would like to make it more organized and simple to understand.

first, the database contains a three simple tables:

quiz table:

id | name

questions table:

id | question | quiz_id (FK from quiz table) |

answers table:

id | name | questions_id (FK from questions table) | state (right or wrong)

1. the first task is a page to show all available quizzes:

the data part

public function getQuizes() {
    $stmt = $this->dbconn->prepare("SELECT id, name FROM {$this->table}");
    $stmt->execute();
    return $stmt->fetchAll();
}

the controller part

// get all quizes
$quizes = $this->quizModel->getQuizes();
// pass the data to the view
$this->callView('quiz/index',$quizes);

the view part to display data

<?php include VIEWS . 'templates/header.php'; ?>

<h3>Available Quizes:</h3>
<ul class="links-list">

    <?php foreach ($records as $record): ?>    

        <li>
            <a href=" <?= BASEPATH . 'quiz/getquiz/' . $record['id']?>"> 
                <?= $record['name']; ?> 
            </a>
        </li>    

    <?php endforeach; ?>
</ul>    
<?php include VIEWS . 'templates/footer.php'; ?>

2. the second task is a page to show quiz and it's questions and possible answers

the data part

public function getQuizById($id) {
        $results = [];
        $stmt = $this->dbconn->prepare(
            "SELECT DISTINCT quiz.name quizName, questions.question question, answers.name answer, answers.id answerId
            FROM quiz
            inner Join questions
            ON quiz.id = questions.quiz_id
            inner join answers
            on questions.id = answers.question_id
            where quiz.id = :id"
        );
        $stmt->bindParam(':id', $id);
        $stmt->execute();

        // group the information
        while( $row = $stmt->fetch(PDO::FETCH_ASSOC) ){
            $results[$row['quizName']][$row['question']][] = [$row['answerId'], $row['answer']];
        }

        return $results;
    }

controller part

// get single quiz
public function getquiz($id) {
    // get the quiz
    $quizes = $this->quizModel->getQuizById($id);
    // pass the data to the view
    $this->callView('quiz/single',$quizes);

}

single.php view to display the data

<?php include VIEWS . 'templates/header.php'; ?>

<?php
foreach ($records as $quiz => $questions):
    ?>

    <h3> <?= $quiz ?> quiz:</h3>

    <form action="<?= BASEPATH . 'quiz/postanswers' ?>" method="post">
        <ul class="links-list">

            <?php
            foreach ($questions as $question => $answers):
                ?>

                <li class="highlight-panel">
                    <?= $question ?>
                </li>

                <?php
                foreach ($answers as $answer):
                    ?>

                    <li>
                        <input type="radio" name="<?= $question ?>" value="<?= $answer[0] ?>"/>
                        <?= $answer[1] ?>
                    </li>

                    <?php
                endforeach;
                ?>

                <?php
            endforeach;
            ?>

        </ul>
        <br>
        <input type="submit" value="submit answers" />
    </form>
    <?php
endforeach;
?>

<?php include VIEWS . 'templates/footer.php'; ?>

3. the third task is a report page to show the final results to the user after posting the answers

the model part

 public function getAnswerInfo($id) {
        $stmt = $this->dbconn->prepare(
            "SELECT DISTINCT quiz.name quizname, questions.id question_id, questions.question question, answers.name answer, answers.state 
            FROM quiz
            inner Join questions
            ON quiz.id = questions.quiz_id
            inner join answers
            on questions.id = answers.question_id
            where answers.id = :id"
        );
        $stmt->bindParam(':id', $id);
        $stmt->execute();

        return $stmt->fetch(PDO::FETCH_ASSOC) ;
    }

    public function getrightanswer($qid) {
        $stmt = $this->dbconn->prepare(
            "SELECT name from answers where state = 1 and question_id = :qid"
        );
        $stmt->bindParam(':qid', $qid);
        $stmt->execute();

        return $stmt->fetch(PDO::FETCH_ASSOC);
    }

the controller part

public function postanswers() {

    $results = [];
    $groupedResults = [];
    $counter = 0;

    if( isset($_POST) ){
        foreach ($_POST as $answerid) {
            $results[] = $this->quizModel->getAnswerInfo($answerid);
        }

        foreach ($results as $key => $value){

            if($value['state'] == 1){

                $counter++;
                $useranswer = 1;

            } else {
                $useranswer = 0;
            }

            $groupedResults[$value['question']][$value['answer']][] = ['rightanswer' => $this->quizModel->getrightanswer($value['question_id']), 'useranswerscore' => $useranswer];

        }

        $quizname = $results[0]['quizname'];
        $data = [$groupedResults, $quizname, $counter, count($results)];
        $this->callView('quiz/report', $data);
    }
}

the view part to display the final report

<?php include VIEWS . 'templates/header.php'; ?>

<h3> Your answers for <?= $records[1] ?> quiz: </h3>

<table>
    <thead>
        <tr>
            <td>Question</td>
            <td>Your answer</td>
            <td>Right answer</td>
            <td>Points</td>
        </tr>
    </thead>
    <tbody>

        <?php
            // loop through the questions
            foreach ($records[0] as $question => $answer): 
        ?>
            <tr>
                <td><?= $question ?></td>

                <?php
                    // loop through the answers
                    foreach ($answer as $useranswer => $answersinfo): 
                ?>

                    <td><?= $useranswer ?></td>

                    <?php
                        // the answers info
                        foreach ($answersinfo as $value): 
                    ?>
                        <td><?= $value['rightanswer']['name'] ?></td>              
                        <td><?= $value['useranswerscore'] ?></td>
                    <?php endforeach; ?>

                <?php endforeach; ?>
            </tr>
        <?php endforeach; ?>
            <tr>
                <td>
                    Final results : <?= $records[2] . '/' . $records[3]; ?>
                </td>
            </tr>
    </tbody>
</table>

<?php include VIEWS . 'templates/footer.php'; ?>

That's what I've done with the app so far and as I said I would like to find a better way to organize everything.

I would like to hear your suggestions on how to improve it.

The full code can be found here on GitHub.

\$\endgroup\$
4
\$\begingroup\$

Structure

I'm not comfortable with what I've done: it seems to be complicated and unprofessional

Honestly, you shouldn't be. Structurally, this seems far better than the average PHP code.

You use an MVC structure as is typical in web development, and you do it correctly. Neither the view nor the controller are doing things that they shouldn't be doing.

Model

Your models are currently really data access objects. You might consider adding actual models with fields and getters.

That way, your controller and view would be decoupled from the database and you would have self-documented objects that you pass around, which would also avoid accessing some magic array where you are not sure what it actually contains (eg $answer[0]).

View

Your views are mostly well structured.

But you shouldn't include header and footer in the index, single, etc views, as it makes them not reusable. You may for example need to display a single question in multiple different contexts.

Misc

  • Your function names are not consistently using camelCase.
  • You sometimes hardcode the quiz tablename, and sometimes use a variable, which is confusing and can lead to bugs in case you actually do decide to change the table name.
  • Your variable names are sometimes not consistent and thus confusing. For example, what's a record?
  • upper-case all your SQL keywords, not just some of them.
  • some of your comments are not adding any information, eg get the quiz or pass the data to the view.
  • You should HTML encode your output to avoid XSS (not everyone who is allowed to add questions or answers should necessarily be allowed to execute JavaSCript).
  • [you didn't post the code, but your database connect code is not ideal. For one, you shouldn't just die in a class as the calling code can't recover from that. Secondly, your models currently extend your database, but models aren't a kind of database. The database should be injected into the class. And finally, you open a new connection for each model]
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.