What needs to happen when you want to update an employee? We'll need to change a number of things. A few of these changes will include breaking out the HTML form and putting it into it's own method, effectively creating a reusable template. The form will then be embedded into an update record page and the index page. Classic code reuse. On the main page, with the list of employees, we'll update our list of employees to be clickable links that then show the update form on another screen. We'll call the database to get the current record and then fill up the form with the current values. The user can then change those values and save those changes to the database.
The first thing we'll do is move the HTML form into it's own method. Then we'll create a method for the HTML header and body so we can reuse that. Then we'll add a controller for it. Finally, we'll add it's own view.
This code should be put into indexviews.php in your webroot directory.
<?php
namespace IndexViews;
class IndexViews {
// $employees is a list of firstName and lastName
static public function index($employees) {
// echo the header
IndexViews::header();
// echo the $employees to the browser
foreach ($employees as $res) {
echo '<div><a href="index.php?do=update&nameId=' . $res["nameId"] . '">' .
$res["lastName"] . ', ' . $res["firstName"] . ', ' . $res["number"] . ', ' . $res["phoneType"] . '</a></div>';
}
// echo the form
IndexViews::employeeForm();
// echo the footer
IndexViews::footer();
}
// show updateEmployee form on it's own page.
static public function updateEmployee($employeeRecord) {
IndexViews::header();
?><h3>Update Employee</h3><?php
IndexViews::employeeForm(true, $employeeRecord);
IndexViews::footer();
}
// make the header a reusable template
static public function header() {
?>
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="utf-8">
<title>Employees</title>
<style>
body{ background-color: #333; color: #ddd; }
a{ color: #ddd; }
</style>
</head>
<body>
<?php
}
// make the footer a reusable template
static public function footer() {
?>
</body>
</html>
<?php
}
// flip $isUpdate to true if we are using this form to update a record
// we set $employeeRecord defaults so we don't get a bunch of notices in our logs when doing saveNewEmployee
static public function employeeForm($isUpdate = false,
$employeeRecord = ["id"=>"", "firstName"=>"", "lastName"=>"", "salary"=>"", "street"=>"",
"city"=>"", "state"=>"", "zip"=>"", "number"=>"", "type"=>"", ]) {
// make the edit employee form a reusable template
?>
<form action="index.php" method="post">
<?php
// use ternary operators to set up do and nameId to be passed back to the server
echo $isUpdate == true ? '<input type="hidden" name="do" value="saveEmployeeUpdate">' : '<input type="hidden" name="do" value="saveNewEmployee">';
echo $isUpdate == true ? '<input type="hidden" name="nameId" value="'.$employeeRecord["nameId"].'">' : '';
?>
<div class="">
Name
</div>
<div class="">
<input type="text" name="firstName" placeholder="first name" value="<?= $employeeRecord["firstName"] ?>">
<input type="text" name="lastName" placeholder="last name" value="<?= $employeeRecord["lastName"] ?>">
<input type="text" name="salary" placeholder="salary $" value="<?= $employeeRecord["salary"] ?>">
</div>
<div class="">
Address
</div>
<div class="">
<input type="text" name="street" placeholder="street" value="<?= $employeeRecord["street"] ?>">
<input type="text" name="city" placeholder="city" value="<?= $employeeRecord["city"] ?>">
<input type="text" name="state" placeholder="state" value="<?= $employeeRecord["state"] ?>">
<input type="text" name="zip" placeholder="zip" value="<?= $employeeRecord["zip"] ?>">
</div>
<div class="">
Phone
</div>
<div class="">
<input type="text" name="number" placeholder="number" value="<?= $employeeRecord["number"] ?>">
<select name="type" value="<?= $employeeRecord["phoneType"] ?>">
<option>Cell</option>
<option>Home</option>
<option>Work</option>
</select>
</div>
<input type="submit" value="Save">
</form>
<?php
}
}
We made a lot of changes in indexview.php. We templatized a lot of our code. We made it reusable. We're following the DRY principle here. Don't Repeat Yourself. Only write code once, if possible. There are certain cases where you need the same code somewhere else. There are many cases where you have the same code somewhere else, but slighly customized, so you must repeat it. That's okay!
A big thing that we changed was the list of employees in our index($employees) method, which add a link to each employee. This link is important to look at. It contains a do value for our controller to interpret and it also sends the employee nameId along as well. This allows us to load the employee details from the database and then send those details to our update view to show the current employee information.
Another thing we did was update our form to handle either updates or creates. We added two parameters to that method that accept a flag that determines either update or create modes, and we added a parameter that accepts the actual employee record as an associative array. We then added the HTML property, value="= ?>" to each field. This adds the actual field data from the database to the form. If you change the value="", the text in the text box changes with it.
We've also introduced a new concept called ternary operators. These are basically short hand if statements. Let's look at an example. The following ternary operator can be converted from:
echo $isUpdate == true ? '<input type="hidden" name="do" value="saveEmployeeUpdate">' : '<input type="hidden" name="do" value="saveNewEmployee">';
to
if ($isUpdate == true)
echo '<input type="hidden" name="do" value="saveEmployeeUpdate">';
else
echo '<input type="hidden" name="do" value="saveNewEmployee">';
Ternary operators work like this: You can echo or assign the result of the condition, you must create a condition that results in a true or false situation, you must have condition true and condition false sections included, and the conditional true and false results must be assignable.
So, let's break that down.
$a = [condition] ? [true condition] : [false condition];
If the condition is true, then [true condition] will be assigned to $a. If the condition is false, then [false condition] with be assigned to $a.
Let's create a simple real world example.
$t = true;
$a = $t == true ? 'the condition is true' : 'the condition is false';
echo $a;
The previous code will echo the condition is true.
One last thing we need to take a look at is value="<?= $employeeRecord["zip"] ?>". What are the <?= ?>tags? Those are basically PHP short hand for <?php echo "something"; ?>. In our case, we are echoing $employeeRecord["zip"], which fills in our HTML value="" property. Just a bit less typing and it makes your template a little cleaner.
Okay, let's update our controller to handle the update.
This code should be put into index.php in your webroot directory.
<?php
require_once "filter.php";
use \filter\filter;
require_once "indexmodels.php";
use \IndexModels\IndexModels;
require_once "indexviews.php";
use \IndexViews\IndexViews;
// handle HTTP GET requests
if ( isset($_GET["do"]) == true ) {
// use $_GET here, because clicking a link and sending HTTP vars is a GET request
if ($_GET["do"] == "update") {
$nameId = 0;
if (isset($_GET["nameId"]) == true && $_GET["nameId"] != "") {
$nameId = $_GET["nameId"];
}
if ($nameId > 0) {
$employee = IndexModels::getEmployee($nameId);
IndexViews::updateEmployee($employee);
}
exit;
}
}
// handle HTTP POST requests
if ( isset($_POST["do"]) == true ) {
// utilize this validation with saveNewEmployee or saveEmployeeUpdate
if ($_POST["do"] == "saveNewEmployee" || $_POST["do"] == "saveEmployeeUpdate") {
$lastName = "";
if (isset($_POST["lastName"]) == true && $_POST["lastName"] != "") {
$lastName = filter::names($_POST["lastName"]);
}
$firstName = "";
if (isset($_POST["firstName"]) == true && $_POST["firstName"] != "") {
$firstName = filter::names($_POST["firstName"]);
}
$salary = "";
if (isset($_POST["salary"]) == true && $_POST["salary"] != "") {
$salary = filter::salary($_POST["salary"]);
}
$street = "";
if (isset($_POST["street"]) == true && $_POST["street"] != "") {
$street = filter::street($_POST["street"]);
}
$city = "";
if (isset($_POST["city"]) == true && $_POST["city"] != "") {
$city = filter::city($_POST["city"]);
}
$state = "";
if (isset($_POST["state"]) == true && $_POST["state"] != "") {
$state = filter::state($_POST["state"]);
}
$zip = "";
if (isset($_POST["zip"]) == true && $_POST["zip"] != "") {
$zip = filter::zip($_POST["zip"]);
}
$number = "";
if (isset($_POST["number"]) == true && $_POST["number"] != "") {
$number = filter::number($_POST["number"]);
}
$type = "";
if (isset($_POST["type"]) == true && $_POST["type"] != "") {
$type = filter::type($_POST["type"]);
}
if ($lastName != "" && $firstName != "" && $salary != "" && $street != "" && $city != "" && $state != "" && $zip != "" && $number != "" && $type != "") {
// decide what IndexModels method we should use, depending on the request
if ($_POST["do"] == "saveNewEmployee") {
IndexModels::saveNewEmployee($lastName, $firstName, $salary, $street, $city, $state, $zip, $number, $type);
} else if ($_POST["do"] == "saveEmployeeUpdate") {
// validate the nameId from the browser
$nameId = 0;
if (isset($_POST["nameId"]) == true && $_POST["nameId"] != "") {
$nameId = $_POST["nameId"];
}
if ($nameId > 0) {
IndexModels::saveEmployeeUpdate($nameId, $lastName, $firstName, $salary, $street, $city, $state, $zip, $number, $type);
}
}
}
// after we've saved, forward the user back to our default screen
header("Location: /");
exit;
}
} else {
// show the default screen, which is a list of employees
$results = IndexModels::index();
IndexViews::index($results);
}
In our controller, we have a new section that handles HTTP GET requests. GET requests are a bit different compared to POST requests. The important thing to remember here is that when a user clicks a link, it creates a HTTP GET request to the server. PHP places the passed along GET variables into $_GET, just like it does with $_POST when it's a POST request. In our case, we are interpreting that a user clicked a link and that link is passing along do=update and nameId=[database nameId]. This allows us to load the employee record and display the record in our freshly minted template.
Down in our $_POST request section, we also have a new piece to our if statement that helps us reuse our code.
if ($_POST["do"] == "saveNewEmployee" || $_POST["do"] == "saveEmployeeUpdate")
So, we're saving and updating within one block of code. This might not be a good idea for you, depending on what you're doing and the situation you're in with a particular feature, but for our purposes, this is good reuse of code. We're utilizing the DRY principle and our code is smaller and cleaner as a result. It's also much easier to maintain in the future. If you add a field, you only need to update it in one place. Awesome!
We've also added a quick if statement that looks like:
if ($_POST["do"] == "saveNewEmployee") {
IndexModels::saveNewEmployee($lastName, $firstName, $salary, $street, $city, $state, $zip, $number, $type);
} else if ($_POST["do"] == "saveEmployeeUpdate") {
// validate the nameId from the browser
$nameId = 0;
if (isset($_POST["nameId"]) == true && $_POST["nameId"] != "") {
$nameId = $_POST["nameId"];
}
if ($nameId > 0) {
IndexModels::saveEmployeeUpdate($nameId, $lastName, $firstName, $salary, $street, $city, $state, $zip, $number, $type);
}
}
Unfortunately, we can't reuse our database code, since they are completely different calls. That's okay. Our saveEmployeeUpdate method is quick and easy to write and we can easily copy and paste a lot of our saveNewEmployee method to our saveEmployeeUpdate method and then customize it to our needs.
This is why we have this if statement above. It helps us differentiate between the new and the update calls.
We need to update our IndexModels::index method to retrieve our id to help with creating the link.
This code should be put into indexmodels.php in your webroot directory. This code should replace the method already in the class.
static public function index() {
$db = new db;
return $db->query("select n.id as nameId, lastName, firstName, number, phoneType from names n, phones p where n.id=p.nameId and isDeleted = false")->fetchAll();
}
The next step is to update our IndexModels and add two new methods. Method one, the database record that we want to update, and method two, actually save the record after the user clicks the "save" button. We'll create new methods for that in our IndexModels class.
This code should be put into indexmodels.php in your webroot directory. This code should be inserted into the class.
// return an employee record
// $nameId comes from the clicked link and is passed as part of a $_POST variable.
static public function getEmployee($nameId) {
$db = new db;
return $db->query(
"select n.id as nameId, lastName, firstName, salary, street, city, state, zip, number, phoneType from names n left join addresses a on n.id=a.nameId left join phones p on n.id=p.nameId where n.id=:nameId",
[ ":nameId"=>$nameId, ]
)->fetchAll()[0];
}
// actually save a user update to the employee record
static public function saveEmployeeUpdate($nameId, $lastName, $firstName, $salary, $street, $city, $state, $zip, $number, $type) {
$db = new db;
$db->query(
"update names set lastName=:lastName, firstName=:firstName, salary=:salary where id=:nameId",
[ ":lastName"=>$lastName, ":firstName"=>$firstName, ":salary"=>$salary, ":nameId"=>$nameId, ]
);
$db->query(
"update addresses set street=:street, city=:city, state=:state, zip=:zip where nameId=:nameId",
[ ":street"=>$street, ":city"=>$city, ":state"=>$state, ":zip"=>$zip, ":nameId"=>$nameId, ]
);
$db->query(
"update phones set number=:number, phoneType=:phoneType where nameId=:nameId",
[ ":number"=>$number, ":phoneType"=>$type, ":nameId"=>$nameId, ]
);
}
These new methods are pretty straight forward. They're just selecting a record, and then updating a record. Something to be aware of, the saveEmployeeUpdate method does three different updates and has a where clause that includes nameId.
Another thing to be aware of that might look strange, is with in the getEmployee($nameId) method, where we have:
fetchAll()[0]
Why do we have the [0] at the end? Well, when fetchAll returns, it usually returns an array. Instead of assigning the array to a variable and then returning the first element of the array, we just tack on a [0].
This code is equivalent to this short hand:
$a = fetchAll();
return $a[0];
I wrote it this way for two reasons. One, it makes the code more compact. And two, just to show you that it can be done as a short hand to make things a little faster for you. Less typing means more code written over time!