The tables
Here are the tables for the goat app:
Users are going to use a form to record each goat's fave comedian. Like this:
How will that work?
The HTML
Let's check out the HTML that we'll need to generate.
- <label>Fave comedian<br>
- <select name='fave-comedian'>
- <option value='0'>(Not given)</option>
- <option value='1' selected >Aisling Bea</option>
- <option value='9' >David Mitchell</option>
- <option value='10' >Eddie Izzard</option>
- <option value='11' >Frankie Boyle</option>
- <option value='5' >Isy Suttie</option>
- <option value='7' >Jimmy Carr</option>
- <option value='8' >Joe Wilkinson</option>
- <option value='2' >Paula Poundstone</option>
- <option value='4' >Roisin Conaty</option>
- <option value='12' >Russel Howard</option>
- <option value='3' >Sara Pascoe</option>
- <option value='6' >Sarah Millican</option>
- <option value='15' >Trevor Noah</option>
- </select>
- </label>
select
doesn't have the multiple
attribute, so users can only choose one comedian.
Each option has the name of a comedian, and that comedian's id. For example:
- <option value='4' >Roisin Conaty</option>
Roisin (pronounced Ro-sheenn) is record 4.
When editing a goat's data, the goat might already have a fave comedian set. We want to make sure that comedian is selected when the form first displays. You do that with the selected
property:
- <option value='1' selected>Aisling Bea</option>
PHP for the form
We want PHP that...
- Makes an
option
tag for every comedian in the DB. - Adds the
selected
property to the one that is the goat's current fave.
goat-add-edit.php
is the page with the form. It has this on it:
- <p><?= makeGenderWidget($goatGender) ?></p>
- <p><?= makeFaveComedianWidget($goatFaveComedianId, $comedianRows) ?></p>
- <p><?= makeClubMembershipWidget($goatClubMemberships, $clubRows) ?></p>
I made functiony friends to make widgets. The middle widget is the one we're going to talk about, the one for the fave comedian.
Ethan
This function is in the library file, like useful-functions.php
? Then it's easy to reuse.
Actually, I didn't put these friends in a library. I put them at the end of goat-add-edit.php
, the page that makes the form.
- </body>
- </html><?php
- ...
- /**
- * Make HTML to render fave comedian widget.
- * @param int $faveComedianId The favorite comedian
- * @param array $comedianRows Available comedians.
- * @return string HTML to make the widget.
- */
- function makeFaveComedianWidget($faveComedianId, $comedianRows) {
- ...
- return $result;
- }
Adela
Why? If they're just on that page, the functions are hard to reuse. And that's why we make functions, for reuse.
That's one reason to make functions, but not the only one. Separating code into functions also makes code easier to write. When I was writing goat-add-edit.php
, I just put in the call to the function...
- <p><?= makeFaveComedianWidget($goatFaveComedianId, $comedianRows) ?></p>
When I was writing that code, I thought to myself:
Self, don't worry about the function's deets for now.
That reduced the load on my brain.
Later, I wrote the function. When doing that, I didn't have to think about the rest of the page. I could focus just on the function.
Make your program easy to think about. You'll spend less time wrestling with bugs.
Keeping the load on your brain under control is very important in programming. Functiony friends help you with that.
Here's the code for making the dropdown (the select
) for the fave comedian. Remember, we want to make this HTML:
- <label>Fave comedian<br>
- <select name='fave-comedian'>
- <option value='0'>(Not given)</option>
- <option value='1' selected >Aisling Bea</option>
- <option value='9' >David Mitchell</option>
- <option value='10' >Eddie Izzard</option>
- <option value='11' >Frankie Boyle</option>
- <option value='5' >Isy Suttie</option>
- <option value='7' >Jimmy Carr</option>
- <option value='8' >Joe Wilkinson</option>
- <option value='2' >Paula Poundstone</option>
- <option value='4' >Roisin Conaty</option>
- <option value='12' >Russel Howard</option>
- <option value='3' >Sara Pascoe</option>
- <option value='6' >Sarah Millican</option>
- <option value='15' >Trevor Noah</option>
- </select>
- </label>
The PHP:
- /**
- * Make HTML to render fave comedian widget.
- * @param int $faveComedianId The favorite comedian
- * @param array $comedianRows Available comedians.
- * @return string HTML to make the widget.
- */
- function makeFaveComedianWidget($faveComedianId, $comedianRows) {
- $result = "
- <label>Fave comedian<br>
- <select name='fave-comedian'>
- <option value='0'>(Not given)</option>\n";
- foreach ($comedianRows as $comedianRow) {
- $comedianId = $comedianRow['comedian_id'];
- $comedianName = $comedianRow['name'];
- $selected = '';
- if ($comedianId == $faveComedianId) {
- $selected = 'selected';
- }
- $result .= "<option value='$comedianId' $selected>$comedianName</option>\n";
- }
- $result .="
- </select>
- </label>\n";
- return $result;
- }
You send in two values:
- The id of current fave. If there isn't one, it will be null.
- The records for all of the comedians.
Lines 8-11 make HTML for the start of the widget. It will be the same always, for every goat, and as comedians are added and removed.
Lines 12-20 are repeated for every comedian. Line 19 outputs the HTML:
- $result .= "<option value='$comedianId' $selected>$comedianName</option>\n";
Notice the variable $selected
is output every time through the loop. It's set to MT towards the start of the loop (line 15):
- $selected = '';
Then, if the comedian the loop is processing, is the fave, $selected
is changed.
- if ($comedianId == $faveComedianId) {
- $selected = 'selected';
- }
Processing fave comedian
The form sends the data to another page, through the session:
- $_SESSION['goat_fave_comedian'] = $goatFaveComedianId;
Before sending the data, though, the form page does a little processing.
- $goatFaveComedianId = getParamFromPost('fave-comedian');
- if ($goatFaveComedianId == 0) {
- // (Choose) option selected on prior page.
- $goatFaveComedianId = null;
- }
Remember that getParamFromPost()
returns null if it can't find a value for the param fave-comedian
. But there's another case that might cause problems. What if the user select the first option?
Here's the HTML for that option:
- <option value='0'>(Not given)</option>
The value
is 0. No comedian has 0 for their id. When we do...
- $goatFaveComedianId = getParamFromPost('fave-comedian');
... $goatFaveComedianId
will be incorrect.
So, let's add an if
to take care of that:
- $goatFaveComedianId = getParamFromPost('fave-comedian');
- if ($goatFaveComedianId == 0) {
- // (Not given) option selected.
- $goatFaveComedianId = null;
- }
If the user doesn't touch the dropdown, or if they select the first option, $goatFaveComedianId
will be null.
Saving
goat-save.php
picks it the user's choice, a comedian id, or null.
- $goatFaveComedianId = $_SESSION['goat_fave_comedian'];
Then inject the value into SQL, e.g.:
- $sql = "
- INSERT INTO goats (
- name, gender, fave_comedian, comments
- )
- VALUES (
- :name, :gender, :comedian, :comments
- )
- ";
- $stmnt = $dbConnection->prepare($sql);
- $isWorked = $stmnt->execute(
- [
- 'name' => $goatName,
- 'gender' => $goatGender,
- 'comedian' => $goatFaveComedianId,
- 'comments' => $goatComments
- ]
- );
Recall that null is what MySQL uses for an MT field. We've already made sure that, if the user does not choose a comedian, $goatFaveComedianId
will be null.
Clubs
Looking further down the form made by goat-add-edit.php
, we see another dropdown for club membership:
Here's the HTML that made it:
- <label>Club memberships<br>
- <select name='club-memberships[]' multiple size='5'>
- <option value='1' selected>Butting</option>
- <option value='3' >Fashion</option>
- <option value='5' >Singing</option>
- <option value='2' >Sleeping</option>
- <option value='4' selected>Theoretical Physics</option>
- </select>
- </label>
There's the multiple
property in line 2, along with []
at the end of the name
. That means the processing page will get an array for this widget, which we need if users are to select more than one element.
You can see that clubs 1 and 5 are select
ed.
More PHP
To make the widget, we need to know two things:
- Which clubs the goat is in.
- The names and ids of all the club.
This function will find the clubs a goat is in.
- /**
- * Get the clubs a goat is in.
- * @param int $goatId Goat id.
- * @return null|array Records, null if something went wrong.
- */
- function getClubsGoatIsIn($goatId) {
- global $dbConnection;
- $result = null;
- if (!is_null($goatId) && is_numeric($goatId)) {
- $sql = "
- select
- clubs.*
- from
- club_members, clubs
- where
- club_members.goat_id = :goat_id_to_show
- and club_members.club_id = clubs.club_id
- order by clubs.name;
- ";
- $stmnt = $dbConnection->prepare($sql);
- $isQueryWorked = $stmnt->execute([':goat_id_to_show' => $goatId]);
- if ($isQueryWorked) {
- $result = $stmnt->fetchAll();
- }
- }
- return $result;
- }
Pass it a goat id, and it will return an array of the clubs. It returns all of the data about the clubs, rather than just their ids.
So, to make the widget, we need to know two things:
- Which clubs the goat is in. >>DONE<<
- The names and ids of all the club.
The second one is easier.
- /**
- * Get all clubs.
- * @return null|array Club rows, null if something broke.
- */
- function getAllClubs() {
- global $dbConnection;
- $result = null;
- $sql = "select * from clubs order by name;";
- $stmnt = $dbConnection->prepare($sql);
- $isQueryWorked = $stmnt->execute();
- if ($isQueryWorked) {
- $result = $stmnt->fetchAll();
- }
- return $result;
- }
To make the widget, we need to know two things:
- Which clubs the goat is in. >>DONE<<
- The names and ids of all the club. >>DONE<<
Here's the first line of the function that makes the widget:
- function makeClubMembershipWidget(array $goatClubMemberships, array $clubRows) {
The first param is the clubs the goat is in. The second is all of the club data. Here's what the debugger says about the first param, $goatClubMemberships
:
The goat is in two clubs, so there are two elements in the array. Each element gives the deets of a club:
The second param of...
- function makeClubMembershipWidget(array $goatClubMemberships, array $clubRows) {
... is $clubRows
.
Adela
It looks like $goatClubMemberships
is a subset of $clubRows
.
Aye! Good thinking, Adela.
Here's PHP to make the widget.
- /**
- * Make HTML for club membership widget.
- * @param array $goatClubMemberships The goat's memberships.
- * 'club_id' element in each row has the id of the club the goat is in.
- * @param array $clubRows All of the clubs.
- * @return string HTML to render widget.
- */
- function makeClubMembershipWidget(array $goatClubMemberships, array $clubRows) {
- $memberOfClubIds= array_column($goatClubMemberships, 'club_id');
- $result = "
- <label>Club memberships<br>
- <select name='club-memberships[]' multiple size='5'>\n";
- foreach ($clubRows as $clubRow) {
- $clubId = $clubRow['club_id'];
- $clubName = $clubRow['name'];
- $selected = '';
- // Is the goat in the club?
- if (in_array($clubId, $memberOfClubIds)) {
- $selected = 'selected';
- }
- $result .= "<option value='$clubId' $selected>$clubName</option>\n";
- }
- $result .= "
- </select>
- </label>\n";
- return $result;
- }
Line 9 has something new:
- $memberOfClubIds= array_column($goatClubMemberships, 'club_id');
Remember that $goatClubMemberships
is an array of arrays:
There's one element in $goatClubMemberships
for each club. Each of those elements has an element called club_id
. Every club has to have an id, so the ids will be there for every record.
- $memberOfClubIds= array_column($goatClubMemberships, 'club_id');
...extracts the club_id
s for every element in $goatClubMemberships
, and returns an array of them. Here's what the debugger sees:
So, $memberOfClubIds
is an array of the ids the goat is a member of. That comes in handy later in the function.
Here's the loop that makes the option
tags.
- foreach ($clubRows as $clubRow) {
- $clubId = $clubRow['club_id'];
- $clubName = $clubRow['name'];
- $selected = '';
- // Is the goat in the club?
- if (in_array($clubId, $memberOfClubIds)) {
- $selected = 'selected';
- }
- $result .= "<option value='$clubId' $selected>$clubName</option>\n";
- }
The loop runs across $clubRows
. That's an array of all of the clubs. So the code in the loop is run for every club. Each time through the loop, $clubRow
is the deets for one club.
By line 18, $clubId
has the id of the club being processed. Line 18 is:
- if (in_array($clubId, $memberOfClubIds)) {
in_array()
returns true of the $clubId
is in the array $memberOfClubIds
. That's the ids of the clubs the goat is in, that we made earlier with array_column()
.
Georgina
Hey, that's cool!
Aye, it is.