Integrating Validation Errors
Setting an Error Message Variable
Our goal is to change the contact form errors so they display with styling rather than taking you to a new page with just the text. We’ll start by removing the echo commands for the error messages and putting them into a variable $error_message instead. We’ll also remove the exit command. Note that we can use the same variable name because each message is in a different conditional.
Next, we’ll add a conditional around the code to send the email only if there are no errors, so we’ll check if it’s set with isset.
Now, there’s still a conditional in there that could display an error message, We’ll rewrite it so if it’s successful, we redirect, and if not, we set the error message variable.
Displaying the Error Message
There are two outcomes to this, either the email is sent successfully, or we set the error message variable. We’ll set a quick conditional to show that if it’s set.
To have this display better, we’ll move the code for it to the top of the form.
Next, we’ll make it so that the the welcome message above that displays only if there’s no error message.
The last tweak we should do is make it so that if the form is submitted with no info, we give a more specific error than “You must give a valid email address”. The issue here is that the email check is later in the code, and is therefore overwriting the first conditional that checks if any of them are blank.
To fix this, we’ll add some conditionals around the last three error messages to check if the $error_message is already set. Note that the AND conditional can be written as && instead, and usually is.
Re-Displaying the Submission
When someone fills out the form and there’s an error, we display the error message but also remove what they’ve entered! That’s annoying.
Luckily, if the request method is POST we’ve loaded what they’ve entered into variables at the top of the conditional, and can use these later for the value attributes in our input fields. Since textarea’s don’t have value attributes, we must put it in between the opening and closing textarea tags.
Escaping Output
2 Core principles – filter input, and escape output. Right now, if someone entered malicious code into our form, it would display on the screen.
Even worse, what if that code closed our form and created a new one that submitted the data to their server? The page will look completely different.
They could send that out to people. They’d visit this page on your server, and their credentials would be sent off to the malicious one! This is called Cross-site Scripting (XSS). To combat this, you can escape output in php with a simple function call name htmlspecialchars() wrapped around the things you’re outputting to the screen. It converts special characters like opening and closing angle brackets to their entity names, so they get displayed instead of being treated as html.
Cleaning URLs with Subfolders
Introducing Subfolders
Right now our addresses have things like .php or a variable in their url. If we clean them up it can help with SEO and make them easier to remember.
Subfolders will help with this. You know how servers go to index.html by default, while not displaying domain.com/index.html? We can use that with subfolders for the same outcome. First we’ll do our receipt.php page. We’ll make a folder called receipt, then rename the php page to be in there.
If we navigate to that folder, we’ll see this.
To fix that, rename it to index.php. This will display the markup, but at the same time the styling and includes won’t work, as well as links to other pages.
Most websites are set up like this, with sub folders hierarchy. To reference a parent directory, it’s just like CSS with ../. This will fix our includes and links, but we still need to fix the styling.
Using Root-Relative Web Addresses
The links in the header do not work, because they’re looking for links in a directory they don’t have access to on the receipt page. A better way for these is with an root relative url, which start with a slash, which tells the browser to start at the root of the domain. /shirts.php would have the browser start at the root, then look for a file in the root folder called shirts.php.
So, we’ll edit the links in our header file. You can link to the home page with just a slash. We’ll do that for the main nav links, and also for the css files. Now, everything works.
Using Absolute Server Paths
Let’s say you want to include a file for some code, but you don’t want to use a relative link. You don’t want to use an absolute link either because you don’t want to have to wait for a web request to a different web page. Instead, you write the absolute server path like this (it depends on your OS).
If you’re not sure what to write for that, you can use $_SERVER[“DOCUMENT_ROOT”]. Note that it does not have a trailing slash at the end.
So, can now actually use that by concatenating it to the beginning of our root relative paths. Since there is no slash at the end of the document_root, we can leave the one at the beginning of the root relative one, so when this is live it lines up correctly.
Introducing Constants
If we were to move our site files to a subfolder in the root directory, our absolute server paths would no longer work. To fix this, we’ll define one variable for the root of our site that we could use on all pages. We’ll make a new include file called config.php. Note: for files with just php, you should leave off the closing ?>, because the browser will add it automatically for you, and it will help avoid whitespace.
However, this value is not meant to change, so it’s better here to use a constant instead of a variable. You set them to a value, but unlike variables, nothing can change their value later in your programming. To create one, write the define function, which takes two arguments, the name aka label, which should be in all caps, the second being its value. We’ll then make another constant set to the root path whatever we’re in, then the base_url’s value.
We’ll now include the config file at the top of our receipt.php file using a require_once and a relative path. This is ok because we only need to do it once per file and this is the only thing we need to change if the hierarchy changes. This let’s us have access to the constants for the rest of the page, so we’ll plug ROOT_PANEL in for the includes. Since we have a slash at the end of root panel, we don’t need it at the start of the part we add after. We used base url for the css in the header.
I was a bit confused on when to use BASE_URL vs root panel. Apparently base url is for linking to a domain for files like js, images, css and the other main pages, while root panel is for includes of other php files. This is somewhat explained here, but I’m still a bit shaky on this.
If we needed to change the subfolder the project was in from shirts4mike_local to something else, all we’d need to do now would be to change what we have in the config file.
Moving the Other Pages
Basically, we need to do what we did for the receipt page for the other ones as well. This includes:
1. Making a subfolder for it and putting it in there, then changing the php files name to index.php.
2. require_once the config file.
3. Change links to css, images, other pages and js to use BASE_URL.
4. Change includes and requires to use the ROOT_PATH constant.
Note that if you need to link to the home page, all you really need is <?php echo BASE_URL ?>.
Cleaning URL’s with Rewrite Rules
Introducing Rewrite Rules
Apache is the web server software most commonly used to run php, and was included with MAMP. By default it will do things like display the index.php file in a folder, but we can change its behaviors with the rewrite rule.
We’ll start by working on the shirts and shirt pages and making a subfolder for them. Once they’re in there, if we go to that page we get a directory listing. Rather than change the name of one to index.php, we’ll use a rewrite rule. The easiest way to do this is to create and include a .htaccess file in the root of the directory. Note that files that start with a . are consider hidden by the OS, When you upload this to your web server, make sure that you don’t forget to include it.
The .ht stands for hypertext, and you can set various web server configuration rules here. Your web hosting server must support these for them to work though. MAMP does by default, as do most. Turn them on with the command RewriteEngine ON. Note that this isn’t php and has its own syntax. To make a rule, write RewriteRule then a space. It has two parts, the first is the url it’s requesting, and starts with a ^ and ends with a $. The second part says what happens when this pattern is found. Below, when shirt/ is found, apache will load the shirts/shirts.php file by default. The url will still be domain/shirts/ though.
Note that we also had to go in and update the shirts page to require_once the config file, update the links with base url and root path, and also update the product.php page so the path to the images now uses base url.
Get Variables and Rewrite Rules
Right now the shirt details pages use get variables and look like this: domain.shirt.php?id=101/.
Here, we make a new rewrite rule. It checks for the address shirts/, then to check for a range of numbers we do brackets, and inside that 0-9. To check for any number of numbers, add a +. Then, we write the second part for what we want apache to load when the first part it requested. For the GET variable at the end, we want it load what was entered in the first part. To get that, go back to the first part and wrap the part you want in parentheses. Then, to back-reference that in the second part, write a dollar sign and 1. The first set of parentheses is 1, the second will be 2, etc.
Now to edit the shirt file to make this work. We’ll require once the config file, then edit all our links and includes with the appropriate base url or root path constants.
Now, if someone were to write domain/shirts/108/, they’d be taken to the correct page. However, we also need to update the links on the shirts page to use that format now instead of actually writing out the GET variable. We can do this in our products.php page, in the function we created that adds the list items.
Redirecting Old Web Addresses
When you change web address like we’ve done here, it’s important to redirect the old ones to the new ones, for if someone links to an old page, or one comes up in a search engine. Let’s start with the old receipt.php page.
However, if we just do that it will load the receipt.php url, which is not the redirect we want. To do that, add the [R] flag at the end of it.
We’ll update that for the contact and shirts pages as well. Another thing – when a server responds to a browser, it sends a response code, like 200 for success or 404 for file not found. 301 is to say that the requested resource has moved permanently, so search engines know to index the new resource rather than the old one. To modify the flag, just add =301 to the R.
Rewrite Rules with Query Strings
Now to make some rules for the old shirt pages that had the GET variables using a rewrite condition. To check the query string (the part of the url that comes after the ?), write %{QUERY_STRING}. We’re looking for one that is an id with one or more numbers, so we can use what we did before for that part. If the url matches this condition, we want it to do the rewrite rule which we’ll write below. If the condition is true, the rule will check if the url match its own condition, in this case shirt.php, then redirect to the new one. To back reference the number from the condition, simply wrap what you want from there in parentheses, and in the rule write %1. Lastly, by default apache will add the query string to the new web address as well. To prevent this, just add a ? with nothing on it.
Adding a Trailing Slash
If you were to go to domain/shirts without a slash at the end, the web server would add one for you at the end since it’s a folder. If you were to try to go to domain/shirt/101 without the ending slash, it would give you a not found error, since that page is not a folder. This is also because our rewrite rule for the shirt pages is looking for a slash at the end.
We’ll write a rule that looks for shirt urls ending in a number without the trailing slash. We’ll wrap the whole thing in parentheses and then backreference it with a slash at the end.
Refactoring the Codebase
Introducing Refactoring
Many times when you first write some code, you don’t write it the best way possible. This is often due to not understanding the needs of the feature. You refactor to make it easier to read and maintain, and to add to and improve later.
Duplicating SKU as an Array Element
Here we have our products array. Notice how the key is the SKU (Stock Keeping Number).
The array_reverse() function takes one argument, the array you want to reverse, and returns a new element with the array in reverse order. However, the key’s for the elements are not reserved and are now the standard 0, 1, 2 etc. Note that for this to work you must set array_reverse() = to a variable, as it doesn’t reverse the original array itself, it returns a new array that’s a copy that’s been reversed.
The array_shift() function takes one argument, the array you want to shift, and removes the first element from the array. It’s return value is the first value from the original array, the one it removed. This once again doesn’t reserve the key values and replaces them.
The removed array looks like this. It doesn’t have the key from before either.
We don’t want to remove the sku from the key, we just also want it in the array. So, we’ll use automated duplication with a foreach loop.
Modifying List View Code
Since the SKU is now also in the array, we can modify our get_list_view_html() function to no longer need the key, and instead use just the array. First remove the product_id as an argument, then modify the code to use $product[“sku”] instead.
Then, we can update where the function is called, on the shirts page, and the home page.
Home:
Separating Concerns: Models
Right now the code for the 4 most recent products on the home page is on the home page itself, which isn’t great if we want to use it elsewhere later. Separation of Concerns principle involves major divisions between the major types of code. We have two at the moment: 1. Data/business logic like shirt pricing. 2. Code that generates web pages like html. We should have each in separate sections, like when we moved the products array into it’s own file. This is a way to avoid duplication preemptively. A model is a file that contains variables and code, and that everything in there is related to data, and our product.php file fits this concept.
Moving Recent Products to the Model
Here we’ll achieve a separation of concerns by moving the recent items logic over to the model file. We’ll start by going to products.php and creating a new function called get_products_recent(). We’ll have it create an empty array called $recent. Then, we’ll need to loop through each shirt one by one to see if it’s one of the last four shirts. Even though we don’t have access yet to a variable with all the shirts, will put on in called $all, then add some comments on what the conditional should do. Then, we’ll have the function return the value of the new array.
Right now we have our products array just sitting there, but if we were to put it into a function we could call it and get it’s value whenever needed. We’ll do that, and at the end of this function return the value of $products after everything has been added to it.
Now, we can go back to our get_products_recent function and call that function. Note that we can’t just write get_products_recent();. It’s RETURNING a value, so we must set it equal to a variable to be able to access that value. In this case we’ll use $all.
In our shirts.php page, our loop was using the $products array, but right now it can’t since we put that in a function. To use it now, we simply have to set the new function equal to a variable. We’ll also make sure to put this in the shirt details page.
We’ll also add this in to the code on the home page. Right now the code here for determining if these are the last four shirts is mixed in with the code for displaying them. Let’s work on separating them y copying it over to the products.php page.
We’ll now need to change all the references to $products to $all, since we put that array in a function. We can also remove the $list_view_html variable declaration, as that’s for how the array will be displayed, and we’re focused on just getting the last 4 items. Now, the first part of that foreach loop make sense to keep since they’re here to determine the last four items, so we’ll keep those and copy them into our other foreach loop. We can remove the rest of the html related code here too. Now, we’re all set! Instead of making any html, this loop simply adds to a variable called recent. We have the function return that variable, so now we can use it whenever needed by calling and setting this function to a variable.
Separating Concerns: View
The part of the code for displaying the content is called a view. It doesn’t contain code for data or business logic. Our home page is a view.
First, we’ll make a variable $recent and set it equal to the return of the get_products_recent() function. Then, we’ll switch out $products for $recent. We’ll remove the code for displaying the last 4 items, since our get_products_recent() function already does that for us. We’ll then simply have the loop concatenate each element of the $recent array onto our $list_view_html variable, then echo that out to the page.
Separating Concerns: MVC
MVC stands for Model View Controller. A controller combines data from the model with instructions on how to display things from the view, and sends the final html containing both down to the browser. So, if we wanted to change the home page to show the best selling shirts instead of the most recent, we’d edit the controller. To change the logic of which shirts are most recent, we’d edit the model. To change the homepage from a grid view to a list view, we’d edit the grid.
In our last example, that $recent = get_recent_products() code would actually be controller code. While putting it in its own file is apparently too advanced for us right now, you can get some separation of concerns by putting it at the top of the file.