yoda79
yoda79

Reputation: 37

How to pull data from database in a template-within-a-template? [F3]

I'm building a simple quiz application in F3.

Each quiz has many questions. Each question has many (possibly different) options. I've set up my MySQL database and tables correctly as such: (1) Quizzes_table, (2) Questions_table, (3) QuestionOptions_table, (4)... and a few others to record the user responses, etc.

I am not sure how to use the F3 structure to call the options for each question up properly from within the wider question template. Here's how the mockup looks like: http://www.gauravkeerthi.com/reallymeh-mockup/quiz1.html

And this is the project git: https://github.com/gauravkeerthi/reallymeh

QuizController.php: (for now I've hardcoded the quiz1.html link for testing purposes)

class QuizController extends Controller {

    function beforeroute() {
        // session management if required
        // render header
        echo \Template::instance()->render('header.html');
    }

    function render($f3){
        $quizzes = new Quizzes($this->db);
        $quiz = $quizzes->getById(1)[0];
        $f3->set('quiz',$quiz);

        $thisquiz = new Questions($this->db);
        $questions = $thisquiz->getByQuizId(1);
        $f3->set('questions',$questions);

        echo \Template::instance()->render('quiz1.html');
    }
}

This is the section within the quiz1.html that calls up the options:

<repeat group="{{ @questions }}" key="{{ @count }}" value="{{ @question }}">
     <div class="row">
        <div class="col-md-6">
           <h5>{{ @count + 1 }}. {{ @question.text }}</h5> 
        </div>
        <div class="col-md-6">
           <select class="form-control bg-silver"> 
              <include href="options.html" with="id={{@question.id}}" />                                           
           </select>                     
        </div>
     </div>    
</repeat>

And this is where I am stuck: I want to achieve something like a "QuestionOptionsController" where I use a getByQuestionId($question.id) and pull all the data from the QuestionOptions and populate the select with the database. So in essence, I want the options to look something like this (but it is obviously wrong because I have no way to pull the correct data now).

My desired options.html with the data pulled from the table into @options:

<repeat group="{{@options}}" value="{{@option}}">
   <option>{{@option.text }}</option>
</repeat>

How do I pull the data into @options correctly? Should I use the QuizController and just pull ALL the relevant QuestionOptions_Table data into the view and then sort it out inside the view (seems messy to me) or is there a better way?

Upvotes: 0

Views: 426

Answers (1)

xfra35
xfra35

Reputation: 3908

You could create a QuestionsOptions model and implement a getOptions() method in your Questions model.

class Questions extends \DB\SQL\Mapper {

  function getOptions() {
    $options=new QuestionsOptions($this->db);
    return $options->find(['question=?',$this->id],['order'=>'weight']);
    // NB: field names to be adjusted
  }

  ...

  function __construct(\DB\SQL $db) {
    parent::__construct($db,'Questions_table');
  }

}

and then in your quiz1.html template:

<select class="form-control bg-silver">
  <repeat group="@question.getOptions()" value="@option">
    <option>{{ @option.text | esc }}</option>
  </repeat>
</select>

UPDATE:

As you underlined it in your comment, this solution may be considered as breaking separation of concerns. If we think about it, that's the whole idea of passing the model object to the view which breaks it: not only getOptions() can be called from within the view, but also save()...

In order to have a stricter separation of concerns, I advise to cast the model before passing it to the view. That also brings the advantage of HTML encoding everything properly.

So your controller would look like:

function render($f3){
    $quizzes = new Quizzes($this->db);
    $quiz = $quizzes->getById(1)[0];
    $f3->set('quiz',$quiz->cast());

    $thisquiz = new Questions($this->db);
    $questions = [];
    foreach ($thisquiz->getByQuizId(1) as $question) {
      $options = [];
      foreach ($question->getOptions() as $option)
        $options[] = $option->cast();
      $questions[] = $question->cast()+['options => $options];
    }
    $f3->set('questions',$questions);

    echo \Template::instance()->render('quiz1.html');
}

And your view would look like:

<select class="form-control bg-silver">
  <repeat group="@question.options" value="@option">
    <option>{{ @option.text }}</option>
  </repeat>
</select>

As you can see, the controller code looks a bit more complex now. But now your view does only manipulate arrays and HTML-encoded strings.

Upvotes: 1

Related Questions