Dynamic Site Navigation
An Overview
In this activity we will use the MVC architecture to build a dynamic navigation bar for use in the views of the PHP Motors web site. Each of the following will be done:
- A model will be constructed and a function built to query the database.
- The connection file and model will be included into the controller's scope.
- The function will be called from the controller.
- The array of classifications returned by the function will be broken apart and used to build an unordered list.
- The unordered list will then be used in the site views (template and home) as the main navigation bar. (Note: We will not use this in our Error view)
- Finally, a test will be done to ensure that the navigation bar responds to adding, updating and deleting car classifications in the database.
- Testing will be done for each step to demonstrate troubleshooting methods.
Create the Model
Remember that a model is where all database functionality exists. Controllers use the functionality in the model to interact with database servers to make dynamic web sites operate.
- You should have a model folder in the phpmotors web site.
- Create a new PHP file in the model folder. Name the file main-model.php.
- After the opening PHP tag, add a comment to the top of the file to indicate that it is the "Main PHP Motors Model".
- You will now create a new function to get the classification information from the carclassification table in the phpmotors database.
Create the Function
- If needed, review PHP Functions before beginning.
- Create a new function using the "function" key word and name the function getClassifications().
- When creating functions, name them to describe what they do.
- Note that no parameters will be placed between the parentheses as the function will not need outside information to run.
- Build the function to look like the code below:
function getClassifications(){ // Create a connection object from the phpmotors connection function $db = phpmotorsConnect(); // The SQL statement to be used with the database $sql = 'SELECT classificationName FROM carclassification ORDER BY classificationName ASC'; // The next line creates the prepared statement using the phpmotors connection $stmt = $db->prepare($sql); // The next line runs the prepared statement $stmt->execute(); // The next line gets the data from the database and // stores it as an array in the $classifications variable $classifications = $stmt->fetchAll(); // The next line closes the interaction with the database $stmt->closeCursor(); // The next line sends the array of data back to where the function // was called (this should be the controller) return $classifications; }
- Note: We should always use Prepared Statements when interacting with databases as it provides an additional layer of protection for the database against outside attacks!
- Double check your code to make sure it is accurate and no warnings or errors appear in the code.
- Save the file.
New Symbol Meanings
Just as with the PDO connection object last week, the code above for the Prepared Statement includes a symbol that you may wonder about. Let's explain:
Single Arrow - —>
In Object Oriented programming of PHP we create instances of a class and refer to them as objects. Objects have methods (functions) that they can perform. The single arrow " -> " represents a call to a method of an object. For example, when you write this line of code in the creation of a prepared statement:
$stmt = $db->prepare($sql);
What you are doing is calling the "prepare" method of the database connection object. You are sending it a sql query that has been stored in a variable named "$sql". If everything works, then a new PDO Prepared Statement, which is a new and different type of object, is created and
stored into a variable named "$stmt".
In real life, this is a bit harder to illustrate but let me try. Your car (even if you don't own one, pretend) is a car object. Meaning that your's isn't the only one, but it represents this abstract concept of "car". Your car has methods: move forward, move reverse, turn left, turn right, blow horn, etc... So, when you prepare to back up we could represent it as $car->reverse();
Open the PHP Motors Controller
The PHP Motors main controller is index.php and should be at the root of the phpmotors folder (ie. cse340/www/phpmotors/index.php or xampp/htdocs/phpmotors/index.php). Open it.
- In order to use the model we have two files that must be brought into the scope of the controller (refer to PHP Include and Require as needed).
- The two files are the connections.php file and the main-model.php file.
- These files must be brought into scope in that order because:
- the connections file contains all of the functions for talking to the database server. Without this file the model cannot do it's job.
- the main-model is needed to get the data from the database which the controller will use to build the dynamic navigation menu.
- Use the require_once function to bring both files into scope as shown below. This code should go at the top of the file, just under the comment.
// Get the database connection file require_once 'library/connections.php'; // Get the PHP Motors model for use as needed require_once 'model/main-model.php';
- The require_once function does three things:
- it attempts to bring the requested code into scope, and
- if it fails the application throws an exception (error) and quits, and
- if it succeeds PHP will ignore any future requests for the same code if repeated in the same file, which saves time and is more efficient.
The main controller file should look similar to this:
<?php
/* PHPMotors Main Controller
* This file is accessed at http://lvh.me/phpmotors/
* or at http://lvh.me/phpmotors/index.php
*
* This file controls all traffic to the http://lvh.me/phpmotors/ URL
*/
// Get the database connection file
require_once 'library/connections.php';
// Get the PHP Motors model for use as needed
require_once 'model/main-model.php';
$action = filter_input(INPUT_POST, 'action');
if ($action == NULL) {
$action = filter_input(INPUT_GET, 'action');
}
switch ($action) {
case 'something':
break;
default:
include 'view/home.php';
}
Call the getClassifications() Function
With the connection and model pages in the scope of the controller, code can be written to call on them to do their jobs.
Note: While we could repeat this code every time we needed a view, that would be inefficient. Instead, we will write the code prior to the switch statement therefore making the resulting code available to all other code in the scope of the controller. This is referred to as page scope, but includes any files that are included or required into that page's scope (see variable scope at PHP.net).
- Create one or two empty lines beneath the lines where the connection and model files were required and brought into the controller's scope.
- Call the getClassifications() function and assign what it returns to a variable named $classifications as shown below:
// Get the array of classifications $classifications = getClassifications();
- Let's test to see what, if anything, we got from the database.
- Write the following code on the next lines:
echo "<pre>"; var_dump($classifications); echo "</pre>"; exit;
var_dump
is a PHP function that displays information about a variable, array or object.- We wrap the
var_dump
in apre
tag to make the output legible. - The
exit
directive stops all further processing by PHP. - Using
var_dump
andexit
are great ways to test and see that what you thought you were getting is actually what you got!
- Write the following code on the next lines:
- Save all files.
- Run the controller in the phpmotors folder.
- Make sure the Docker containers (or XAMPP and the Apache and MySQL database servers) are running
- Open a new browser window or tab, type
localhost/phpmotors/
(or browse to http://lvh.me/phpmotors/) and you should see the result of the var_dump on the screen. Remember that the controller is named "index.php" and it is a default name, meaning that if we don't specify a name, then the server will look for and deliver a file with a default name automatically.
- If things are working, the var_dump should have dumped something similar to what you see below to your browser
window:
array(5) { [0]=> array(2) { ["classificationName"]=> string(7) "Classic" [0]=> string(7) "Classic" } [1]=> array(2) { ["classificationName"]=> string(6) "Sports" [0]=> string(6) "Sports" } [2]=> array(2) { ["classificationName"]=> string(3) "SUV" [0]=> string(3) "SUV" } [3]=> array(2) { ["classificationName"]=> string(6) "Trucks" [0]=> string(6) "Trucks" } [4]=> array(2) { ["classificationName"]=> string(4) "Used" [0]=> string(4) "Used" } }
- While it may look like a mess, in reality it tells us that we have a multidimensional array with 5 sub-arrays. That is exactly what we wanted!
- If you got nothing, go back and double check your code, talk with a peer, contact a TA or lab assistant or the professor. But it needs to be working.
- When it is working and you're done testing comment out the var_dump and exit lines using
//
at the beginning of each line or delete the lines.
Build the Navigation List with Links
With the array of classifications being returned from the database we will break it apart and use PHP and HTML to build an unordered list of links. Do the following:
Below the var_dump and exit lines (if commented out) or on the first empty line beneath the getClassifications() function call, write the following code:
// Build a navigation bar using the $classifications array
$navList = '<ul>';
$navList .= "<li><a href='/phpmotors/index.php' title='View the PHP Motors home page'>Home</a></li>";
foreach ($classifications as $classification) {
$navList .= "<li><a href='/phpmotors/index.php?action=".urlencode($classification['classificationName'])."' title='View our $classification[classificationName] product line'>$classification[classificationName]</a></li>";
}
$navList .= '</ul>';
An Explanation
-
$navList = '<ul>';
- creates an unordered list as a string and assigns it to the $navList variable. -
$navList .= "<li><a href='/phpmotors/index.php' title='View the PHP Motors home page'>Home</a></li>";
- creates a list item with a link to the controller at the root of the phpmotors folder as a string. The string is then added to the value already stored in the variable. That's what the.=
operator does, it adds to a variable. -
foreach ($classifications as $classification) {
- This begins a foreach loop that will find each of the sub-arrays in the $classifications array and break them apart, one at a time, and stores each one into a new variable called $classification. Refer to foreach loops at php.net. -
$navList .= "<li><a href='/phpmotors/index.php?action=".urlencode($classification['classificationName'])."' title='View our $classification[classificationName] product line'>$classification[classificationName]</a></li>";
- This is a list item with a link that points to the controller in the phpmotors folder, but this time it is followed by a question mark (e.g. ?) and then by a key - value pair. The key is action and the value is the classification name inside of the $classification variable. The $classification['classificationName'] is inside of a PHP function - urlencode - which takes care of any spaces or other special characters so they are valid HTML. The whole piece is concatenated into the string as a whole. As with all previous code in this example, the string is being added to the $navList variable. -
}
- this lone right curly brace closes the foreach loop. -
$navList .= '</ul>';
- This line closes the unordered list.
Time Again to Test
If the code above is written correctly, we should have built an unordered list with links that will send to the controller a request for each of the classification types in the database. Let's see if it works!
- Immediately below the last line, where the unordered list was closed and stored in the $navList variable.
- Write the following two short lines of code:
echo $navList; exit;
- Save the index.php page.
- Return to the browser tab with the home page visible and reload the page to once again run the controller.
- When the controller runs, it should display on the screen an unordered list with six items as shown below:
- If it appears, "High Five"!
- If not, double-check your code, compare it against the illustrations above, talk to someone else, get some help.
- After it is working, just as with the previous test, comment out those two lines or delete them from the controller.
Put it to work
Since we built all of this outside of the switch structure it is available to any resource that we include into the controller's scope using the switch control structure, including the home.php view. Let's make it happen:
- Open your home.php file
- Find your <nav> area.
- Comment out the modularization code that you should already have in place.
- Add the following code within the <nav></nav>:
<?php echo $navList; ?>
- This tells PHP to find and write the value of the $navList variable to the screen.
- Save the home.php file.
- Once again, run the index.php file.
- The home page should load, but with a new navigation bar that looks like the image below:
- Your home link may or may not be highlighted, that doesn't matter.
- As you hover over each link you should see a title telling you that if you click the link you will see products that belong to that classification (the links don't work yet).
- Look closely at the link's path. You will see that it points to the phpmotors folder and then has a question mark followed by the word "action", an equal sign, and the classification name. This is the way to pass a "name - value" pair to a controller.
Apply to All
When the dynamic navigation bar is working correctly in the home view, apply it to your template as well, but NOT to the 500.php error page! The navigation bar will now be delivered from the controller, but the header and footer will continue to be delivered from the code snippet modules.