Goats, clubs, and comedians

Here's a silly example we'll use for the rest of this section. You can:

Here's a drawing of the DB:

DB

There are three entities, with the columns shown. The ids are all auto-increment INTs.

Here are some comedians. This is the simplest table.

Comedians

Here are some goats:

Goats

gender is either "f" or "m".

fave_comedian is a foreign key, into comedians. It contains the id of a comedian.

fave_comedian implements a 1:N relationship. Each goat has one fave comedian, but a comedian can be the fave of more than one goat.

Here are some clubs.

Clubs

category is "c" (core), "l" (lifestyle), or "a" (academic). Sponsored is "y" or "n". Cthulhu gives each sponsored club some cash for events.

club_members shows which goats are in which clubs.

club_members

It implements an M:N relationship. Each goat can be in more than one club, and each club can have more than one goat.

Page components

This is from the home page:

  • <!DOCTYPE html>
  • <html lang="en">
  •     <head>
  •         <?php
  •         $pageTitle = 'Home';
  •         require_once 'library/head.php';
  •         ?>
  •     </head>
  •     <body>
  •         <?php
  •         require_once 'library/top.php'
  •         ?>
  •         <h1>Welcome</h1>
  •         ...
  •         <?php
  •         require_once 'library/footer.php';
  •         ?>
  •     </body>
  • </html>

As you can see, it uses page components for the head, top, and footer regions. All pages in the app use this approach.

The app makes extensive use of helper functions. For example, this is from the page that lists the goats:

  • $errorMessage = '';
  • $goatEntities = getAllGoats();
  • if (is_null($goatEntities)) {
  •     $errorMessage = "Sorry, there was a problem looking up goats in the database.";
  • }

getAllGoats() is a functiony friend that returns all of the goats:

  1. /**
  2.  * Get all goats.
  3.  * @return null|array Goat rows, null if something broke.
  4.  */
  5. function getAllGoats() {
  6.     global $dbConnection;
  7.     $result = null;
  8.     $sql = "select * from goats order by name;";
  9.     $stmnt = $dbConnection->prepare($sql);
  10.     $isQueryWorked = $stmnt->execute();
  11.     if ($isQueryWorked) {
  12.         $result = $stmnt->fetchAll();
  13.     }
  14.     return $result;
  15. }

The DB connection is in a global, as before (line 6). The function returns the goat records, or null if something broke.

Ethan
Ethan

What if there are no goats in the goats table? Will it return null?

No, it will return an array with no elements, an MT array. It only returns null if MySQL says something broke.

Render functions

This is from the page that shows a goat's deets:

  • print "<p>Here are the deets for the goat.</p>\n";
  • $deets = renderGoatDeets($goatId);
  • if (is_null($deets)) {
  •     print "<p>Sorry, something went wrong showing goat $goatId.</p>\n";
  • }
  • else {
  •     print $deets;
  • }

renderGoatDeets() is a render function. It returns the HTML needed to show the goat's data.

We're going to show a goat's deets on three pages:

  • goat-view.php: a page that shows a goat's deets.
  • goat-confirm-delete.php: a page asking users to confirm deletion of a goat.
  • goat-delete-confirmed.php: a page deleting a goat.

I make it a functiony friend, so I could reuse the code, and save time.

Principle

Reuse

Reuse your code across programs, using functions and page components.

Here's the function:

  1. /**
  2.  * Make HTML to render a goat.
  3.  * @param int $goatId The goat's id.
  4.  * @return string|null HTML to show the goat, or null if something broke.
  5.  */
  6. function renderGoatDeets($goatId) {
  7.     global $dbConnection;
  8.     $goatEntity = getGoatWithId($goatId);
  9.     if ( is_null($goatEntity) ) {
  10.         return null;
  11.     }
  12.     $result = "<p>Name: {$goatEntity['name']}</p>\n";
  13.     $result .= "<p>Gender: " . getNameForGenderCode($goatEntity['gender'] ) . "</p>\n";
  14.     $result .= "<p>Fave comedian: ";
  15.     $faveComedianId = $goatEntity['fave_comedian'];
  16.     if (is_null($faveComedianId)) {
  17.         $result .= "(Not given)";
  18.     }
  19.     else {
  20.         $comedianEntity = getComedianWithId($faveComedianId);
  21.         $faveComedianName = $comedianEntity['name'];
  22.         $result .= "<a href='comedian-view.php?comedian_id=$faveComedianId'>$faveComedianName</a>";
  23.     }
  24.     $result .= "</p>\n";
  25.     $result .= "<p>Comments: {$goatEntity['comments']}</p>\n";
  26.     $result .= "<h3>Clubs</h3>\n";
  27.     // Render clubs the goat is in.
  28.     $clubs = getClubsGoatIsIn($goatId);
  29.     if (count($clubs) == 0) {
  30.         // Goat is not in any clubs.
  31.         $result .= "<p>{$goatEntity['name']} isn't in any clubs.</p>\n";
  32.     }
  33.     else {
  34.         $result .= renderClubList($clubs);
  35.     }
  36.     return $result;
  37. }

Notice the function calls other functions on, e.g., lines 8, 13, 20, 28, and 34. Reuse is good.

Display values

There are functiony friends that translate DB values, to display values. For example, the DB will store a club's type as "c", "l", or "a". But we want to show the user what those values actually mean. So:

  • /**
  •  * Given a club category code, return the category's name.
  •  * @param string $categoryCode The code, e.g., c.
  •  * @return string|null The name, e.g., Core
  •  */
  • function getCategoryNameForCode($categoryCode) {
  •     $categoryName = null;
  •     if ($categoryCode == 'c') {
  •         $categoryName = 'Core';
  •     }
  •     elseif ($categoryCode == 'l') {
  •         $categoryName = 'Lifestyle';
  •     }
  •     elseif ($categoryCode == 'a') {
  •         $categoryName = 'Academic';
  •     }
  •     return $categoryName;
  • }

Mess with the app

Play around with it. Get a feel for what it does. You'll learn how to make such a thing in this section of the course.