Lab 10

Lab 10

Trivia App

PHP vs. JavaScript Implementation

Adding Responsive CSS


Overview & Getting Started

While you might have already implemented your Project 4 Play Trivia script, it is good to see and consider alternative implementations.

In this lab, we will...

  1. focus on a "pure" PHP implementation that follows good practices,
  2. enhance the output with appropriate Bootstrap classes to add responsive CSS for mobile devices, and
  3. describe a JavaScript approach that uses an API (Application Program Interface).

TASK 1

Download the following starter code and use it to complete all the tasks

starter.zip

Later, if you choose, you can use this code for your own Project 4 implementation.


Playing Trivia: A Good PHP Approach

TASK 2

Read the following carefully as it explains key concepts to complete the remaining tasks.

While there are many ways to implement the trivia game for Project 4, any good PHP approach must keep track of at least three variables:

  1. The current user ($_SESSION['username']), which is established at login
  2. The current question number ($_SESSION['question_num']), which is set to 1 when the user requests play_trivia.php and is incremented each time a question is answered.
  3. The number of points ($_SESSION['points']), which is set to 0 when the user user requests play_trivia.php and then is incremented each time a question is answered correctly.

The play_trivia.php script can be viewed as a recursive function that is called when the game begins. The script calls itself each time the user selects a choice for a question.

screenshot

By clicking the form's submit button, an HTTP request is made where all the form data is transmitted to the server. We need to implement a form that transmits the user's choice $_POST['answer'] and the value of the button the user clicked $_POST['action'].

Handling Page Reload + Feedback

Clicking the reload button on the browser will trigger the same HTTP request as the user's last click. Thus, a reload could submit the user's previous choice as their choice for the current question. We can prevent reload issues by alternating between two states:

  1. The Display state displays a question with a "Submit" button and
  2. The Feedback state displays whether or not a user got a question right and a "Next Question" button

Thus, when a question is displayed, a reload will not trigger a "Submit" action because the previous action was clicking the "Next Question" button. Similarly, when a feedback message is displayed, a reload will not trigger a "Next Question" action because the previous action was clicking the "Submit" button.

Moving from State to State

The trivia application actually has four distinct states that we can detect by looking at the session-stored question number and the value of the button the user clicked to submit the form. Here is a summary of the states (please read carefully):

screenshot

  1. The Start state is when the user initially requests the play_trivia.php script. We can detect this by checking if $_SESSION['question_num'] is null, zero or undefined, or if the $_POST variable is null or undefined. In this state, we need to set $_SESSION['question_num'] = 1, set _SESSION['points'] = 0, execute a query to fetch 10 random questions, and store the questions in a session variable, i.e., $_SESSION['questions']. See below for details on how to store the 10 questions as a 2D array. Finally, we can print the "Start" button to move us to the Display state.
  2. The Display state is when $_SESSION['question_num'] is a value between 1 and 10 and the "Start" or "Next Question" button was just pressed. In this state, we use the session-stored question number to print a current question in a working form with a "Submit" button.
  3. The Feedback state is when $_SESSION['question_num'] is a value between 1 and 9 and the "Submit" button was just pressed. Here we increment $_SESSION['question_num'] and then compare the submitted choice with the correct answer. If the selected choice match the stored answer, we increment $_SESSION['points'] and print a feedback message and a "Next Question" button
  4. The Stop state is when $_SESSION['question_num'] equals 10 and the "Submit" button was just pressed. In this state, we process the submitted choice and print the feedback message just as we did in the Feedback state, but we also print the points earned for the game and update the user's games and total points. See below for query details. Finally, we can set $_SESSION['question_num'] to zero and display a link back to the home page and a button to "Play Again", which will just reloads the script. Since the question number is reset to zero, reloading the script will put us back into the Start state, which starts a new game.

TASK 3

In your starter code folder, create a file called play_trivia.php and add the following code. This code illustrates how to detect the first two state from the posted action of the submitted form. You will have to implement the other two states.

Write the code to detect the Feedback state and then echo a form with a "Next Question" button. Then, write the code to detect the Stop state and then echo a form with a "Play Again" button. In this task, you just want to display messages indicating what state you are in to make sure you can move correctly through the 4 states.

<? 

session_start();

require_once("functions.php");

if ($_SESSION['authenticated'] != true) {
  die("Access denied");	
}

print_html_header("Play Trivia");

// Start State
if (!$_SESSION['question_num'] || !$_POST || $_POST['action']=="Play Again") {
	$_SESSION['question_num'] = 1;
	$_SESSION['points'] = 0;
	
	echo '
		<p>Get 10 question from database</p>
		<form method="post" action="play_trivia.php">
			<input type="submit" name="action" value="Start">
		</form>';
}

// Display State
else if ($_POST['action']=="Start" || $_POST['action']=="Next Question") {
	echo '
		<p>Display question '.$_SESSION['question_num'].'</p>
		<form method="post" action="play_trivia.php">
			<input type="submit" name="action" value="Submit">
		</form>';	
}

// Add Feedback State

	
// Add Stop State

}

print_html_footer();

?>

Playing Trivia: Database Processing & Output

While you should have been able to implement the four states on your own, here is some solution code to help you move forward if you get stuck.

Starter Solution

TASK 4

Finish implementing the play_trivia.php. Please read all the text below because it contain implementation help and details.

Each state has a core operation that we can implement and test incrementally.

  1. Get Questions: In the Start state, we need to get 10 questions from the database and store them as a session variable using a 2D array. Thus, each time we submit the form, we will not "lose" the questions. Also, we don't have to worry about displaying duplicate questions, because the query can randomizes the order of exactly 10 questions. Here is the code to do this.

    $mysqli = db_connect();
    $result = $mysqli->query("SELECT question, choice1, choice2, choice3, choice4, answer FROM Questions ORDER BY RAND() LIMIT 10");
    echo $mysqli->error;
    $questions_array = array();
    while ($row = $result->fetch_row()) {
      array_push($questions_array, $row);
    }
    $result->close();
    $mysqli->close();
    var_dump($questions_array);
    $_SESSION['questions'] = $questions_array;	
    	
    Be sure to change the table name to your questions table. Note that for debugging, we print $mysqli->error in case there is an error. If you do not have 10 questions, you can set the LIMIT to a smaller value, i.e., 3. And, you can enter the Stop state after 3 questions. Also, we use var_dump to make sure the array is full of questions before copying it to $_SESSION['questions'].
  2. Display Question: In the Display state, we need to display a question by fetching the values from the stored session. This is quite confusing because we need to use $_SESSION['question_num'] to fetch the current question from the $_SESSION['questions'] array. But, the question numbers range from 1 to 10 and the array indices range from 0 to 9, so we must adjust accordingly.
    	
    $question_index = $_SESSION['question_num'] - 1;
    $current_question =  $_SESSION['questions'][ $question_index ];
    
    Then, we need to remember that the query fetched the question (index 0), then the four choices (index 1 to 4), and finally then answer (index 5). To make things clearer, we can copy these values to more compact and meaningful variable names.
    	
    $q = $current_question[0];
    $c1 = $current_question[1];
    $c2 = $current_question[2];
    $c3 = $current_question[3];
    $c4 = $current_question[4];
    
    Then, we can also store the answer to help process the submission.

    $_SESSION['answer'] =  $current_question[5];
    
    Finally, we can use the compact variables to print the question as a simple HTML form

    echo '
    <h3>Question '.$_SESSION['question_num'].'</h3>
    <p>'.$q.'</p>
    <form method="post" action="play_trivia.php">		
    
      <label>
        <input type="radio" name="answer" value="1">
        '.$c1.'
      </label><br>
    
      <label>
        <input type="radio" name="answer" value="2">
        '.$c2.'
      </label><br>
    
      <label>
        <input type="radio" name="answer" value="3">
        '.$c3.'
      </label><br>
    
      <label>
        <input type="radio" name="answer" value="4">
        '.$c4.'
      </label><br>
    	
      <input type="submit" name="action" value="Submit">
    	
    </form>';
    
  3. Process Submission: In the Feedback state, when the user selects a choice and clicks submit, the selected choice (value 1, 2, 3 or 4) is transmitted as $_POST['answer']. When we displayed the question, we stored the actual answer in $_SESSION['answer']. We have to compare these values and if they match, we must increment $_SESSION['points']. Then, we need to print a feedback message, i.e., whether the submitted answer was correct or not. And finally, we print the "Next Question" submit button.
  4. Record Score & Print End of Game Feedback The Stop state occurs when the user submits the last question. When this happens, we need to write a select query to get the user's total games and total points. Then, we need to increment total games by one and add the current game points to the total points. Finally, we need to write an update query to change the values in the databased.

    Here is the select query and how to fetch the values:

    $username = $_SESSION['username'];
    $sql = "SELECT games, points FROM Users WHERE username='$username'";
    $result = $mysqli->query($sql);
    $row = $result->fetch_row();
    $games = $row[0];
    $points = $row[1];
    
    Notice that $username must have single quotes because it is a string.

    Here is the update query:

    UPDATE Users SET games=$games, points=$points WHERE username='$username'
    
    Note that you can connect to the database once perform the select query, update the values of $games and $points, and then run the update query before closing the $result and $mysqli connections.

Styling Trivia Using Bootstrap

Notice that we are calling print_html_header("Play Trivia") and print_html_footer(), which includes Bootstrap's CSS and JavaScript.

TASK 5

Add CSS classes to the basic forms for answering each question. Read below for some helpful hints and documentation links.

  1. You can transform all the submit buttons into Bootstrap buttons that are colored or sized to emphasize the action:
    Bootstrap Buttons
  2. You can put the feedback message in colored alert divs to emphasize success or failure (danger or warning):
    Bootstrap Alerts
  3. You can add form controls to the radio buttons and labels so they will size appropriately based on the device. See the radio button example in the link below. Note you should add <fieldset class="form-group"> and <div class="form-check"> just like the example:
    Bootstrap Form Controls

Playing Trivia: JavaScript API Approach

The game play of a trivia application can be almost entirely implemented in JavaScript. Only two core operations (getting the questions and updating the user's games and points) require server/database support. And, as we have learned in past labs, AJAX can be used to run server-side scripts in the background.

To help support application development in JavaScript, developers often impelment APIs (Application Program Interfaces). Here is an example to illustrate the concept:

Trivoogle Scenario

Imagine working for a company call Trivoogle, which is the world-class leader in providing trivia questions to other companies that wish to make trivia applications.

As a Trivoogle developer, your job is to create JavaScript programs that reside on a Trivoogle server that other developers could link to in order to help build applications. Since the URLs of your JavaScript code originate from a Trivoogle server, other developers can call your functions in order to get trivia questions via AJAX from Trivoogle's servers. You, as the API developer, would create functions that have lots of parameters so that other developers could specify things like:

API Concept

An API is just a collection of objects and functions that are highly parameterized to help implement applications. Good API's should have good documentation to describe the parameters and the output. Companies like Google provides many API's so developers can build applications using these companies' tools.

Many API's will return data in JSON format. In the past, XML was the standard format. However, it is very common for API's to output chunks of HTML that developers can simply append into div tags.

Some API's will allow developers to specify the id of an HTML element that the API can then manipulate. Thus, developers do not even have to write the JavaScript to append the API's output to an HTML element; The API function can do it directly. The key concept is that developers are including Trivoogle's JavaScript as if it were their own code. This allows Trivoogle to deliver content to many paying clients without having to do a lot of customized programming. The developers have to implement their application to work with Trivoogle's API.

Securing APIs

JavaScript code cannot be hidden on a server like PHP code. While the code can be compressed and minified, anyone with a web server could use Trivoogle's API. Since Trivoogle wishes to charge for access to their API, they must figure out a way to control access.

The most popular approach is to require an authentication key. Thus, Trivoogle's server will not respond to AJAX requests unless the request includes a valid key. The problem is that this API key would be present in the code of other developer's web scripts. Anyone could then copy this key and use it.

Thus, Trivoogle must build a server to server authentication system that other developers must use to get dynamic keys. The idea is that when a user logs in, the developer must also use Trivoogle's authentication systems to get a dynamic key for that user. This key is sent back to the client and it get's stored on Trivoogle's API server.

When the application needs to get a trivia question, the key would be sent to verify that the request is coming from a paying client. The key is stored on Trivoogle server using sessions. The session can be set to expire or it can be destroyed when a user logs out. Thus, even if a hacker intercepted the key, they would only be able to use if for a limited time.

In general, there is no fail safe way to secure API's, so "pure" server-side applications are still very common for many applications where the code needs to remain hidden.

Dynamic JavaScript

While some API's allow developers to directly call defined JavaScript functions, other API's are "main programs" that can be customized by URL parameters. Consider this URL:

get_trivia_question.php?difficulty=hard&targetid=questiondiv

This PHP script would output the JavaScript code to insert "hard" questions in the questiondiv of the web page that linked to the code. The developer would have to define the questiondiv in their application's web page. Once loaded, this custom JavaScript program could completely implement a trivia application by making AJAX calls to Trivoogle's server. In other words, another developer could completely embed a trivia application on their web pages just by linking and customizing one link URL.

TASK 6

Think about the insanity that a server side scripting language like PHP (language #1) can execute an SQL query (language #2) to get content from a database and then output JavaScript (language #3) which can then dynamically modify the HTML (language #4) and CSS (language #5) of a web page hosted on a completely different server. While this seems insane, it allows web applications to be developed and deployed with ultimate flexibility. It also allows applications to be built using the best languages for key task. JavaScript, HTML and CSS are the best languages for creating user interfaces because these languages are standardized and are the foundation of the World Wide Web, so they represent the most accessible and widely used languages that exist in the world. SQL is certainly the leading language for selecting data from a database. The server side language is the only language where there is a debate on which one is the best. But, this general framework allows developers to use whatever server-side language they want. For this task, you only need to consider the awesomeness of this general framework and then smile because you now know the fundamentals of the most powerful application framework ever developed.

DELIVERABLE

None. This lab is attendance only.