New terms from treehouse – Enhancing a Simple PHP Application Part 2 – Adding Search: Controller & View, Adding Search: Model, Paginating a List: Controller, Paginating a List: Model and View

Adding Search: Controller & View

Creating the Search Page

We’ll make the page by making a subfolder called search, so we can keep our urls clean, then putting an index.php file inside of it. Then, we’ll include the config file, and set the page title and search variable to underline it in the nav when the page is selected. Then, we’ll include the header and footer files.

Screen Shot 2014-12-17 at 3.00.49 PM

Now, we’ll add a new list item to the nav in the header for the search, updating the class and such for search.

Screen Shot 2014-12-17 at 3.03.09 PM

Screen Shot 2014-12-17 at 3.04.07 PM

Building the Search Form

Here we’ll add some html for the header and form. Note that for the text input he gave it the name s, which is common for search fields. If you search for something, notice how you get s=value you entered in the query string.

Screen Shot 2014-12-17 at 3.09.17 PM Screen Shot 2014-12-17 at 3.08.45 PM

If you leave the form’s action attribute blank, it will redirect back to the current page. For the method attribute, if you give it no value the default value is GET. You should use POST when the form is creating records or taking some other action like sending an email. For retrieving or getting data, like a search, you should use GET. Still, it’s good practice to specify them anyway in the form element. To indicate the current folder, ie for a redirect, use ./, which is for the root of the current directory.

Screen Shot 2014-12-17 at 3.13.07 PM

Handling the Form Submission

We’ll start by making a blank variable $search_term to eventually hold the value entered in the search field. Then we’ll make a conditionally based on whether a value is set for the s get variable. If it is, we’ll add that value to the $search_term variable, and at the same time trim it. Then, we’ll add another conditional that checks if the $search_term variable is not blank. If it’s not, we’ll pull in the product.php file (the model).

Screen Shot 2014-12-17 at 3.20.29 PM

We’ll go to the model now to make a function to perform when the search is performed. We’ll call it get_products_search, and it will take one argument, the search term. We’ll start with an empty array called $results that we can add to, and make a variable $all equal to the return value of the function that gives us the main array. Then, we’ll add a foreach loop to go through those and give us the results. The conditional inside should look for matches, and add them to results. We’ll then return the $results variable so we can use it later.

Screen Shot 2014-12-17 at 3.25.11 PM

Now we can go back and add this function and it’s return value to our search page, passing our $search_term variable as its argument. We’ll also add some troubleshooting code to display the code on the screen if there’s a search entry.

Screen Shot 2014-12-17 at 3.31.32 PM

Screen Shot 2014-12-17 at 3.31.26 PM

Displaying the Products

Now to actually have the products display on the screen. First we’ll remove the troubleshooting code. We’ll then add a conditional below the form checking if the $search_term is not set. If it is, we’ll add ul tags surrounding a foreach loop, which goes through the array and echo’s out the return value of the function get_list_view_html for each element.

Screen Shot 2014-12-17 at 3.41.51 PM

Screen Shot 2014-12-17 at 3.42.43 PM

Accounting For Empty Results

We’ll also make the search field retain the value entered, using the same method as before for the contact form error scenario with the htmlspecialchars() function to prevent people writing malicious code.

Screen Shot 2014-12-17 at 3.49.06 PM

Now to account for what happens if there are no matches. We can simulate this by commenting out the part that adds to the array in the model.

Screen Shot 2014-12-17 at 3.50.54 PM

We can then use the native php function empty() to check if an array has any elements. We’ll use this and the negation operator to make a if else block, where if there is nothing in the array, it displays a message saying no results were found.

Screen Shot 2014-12-17 at 3.56.21 PMScreen Shot 2014-12-17 at 3.56.30 PM

Adding Search: Model

Introducing the strpos Function

Now to make the searching work with the conditional we set up in the products model file. The strpos (string position) function (looks for one piece of text inside of another. The first argument it takes is the piece of text you want to search in, the second is the one you want to search for. It will return the place in the position of text it occurred.

Screen Shot 2014-12-19 at 3.43.34 PM Screen Shot 2014-12-19 at 3.43.38 PM

It can search variables as well. Here we have it search for something not there as well. It looks like it didn’t return anything, but it did.

Screen Shot 2014-12-19 at 3.44.52 PM Screen Shot 2014-12-19 at 3.45.08 PM

Introducing Boolean Values

Boolean values can be true or false, and can be written without quotation marks. If we use var_dump instead of echo, we can see more info about the return values. You can see now that if the text isn’t found it returns a boolean value of false.

Screen Shot 2014-12-19 at 3.52.28 PM Screen Shot 2014-12-19 at 3.52.12 PM

Introducing the PHP Manual

The PHP Manual contains the documentation for everything you need to know, including functions. For example, trim. It will tell you the version it was first available in, gives a description and technical info. Arguments listed in square brackets are optional. Info on the return value is displayed in front of the function name. The word before the argument tells you the data type it accepts for that argument, like string. If it says mixed, it accepts multiple types. It will also list out the return values you get depending on what you enter.

Screen Shot 2014-12-19 at 3.57.55 PM

Introducing Identity Comparison

With strpos, the value we’re searching for is referred to as a needle, and the one we’re searching in is the haystack. It takes an optional third argument that lets you offset the search so you don’t start at the beginning.

Screen Shot 2014-12-19 at 4.03.58 PM

Now, under the return values section they give you a warning that if it doesn’t find it it may return a non-boolean value which evaluates to FALSE. So, you should use the === operator, as that checks for the data type as well as the value.

Screen Shot 2014-12-19 at 4.05.53 PM

Screen Shot 2014-12-19 at 4.06.36 PM

Type juggling means that php will convert the two values to the same type, and then see if they match. So, 5 == “5” would be true. But, so would 0 == false. So, for booleans its good to use === instead. 0 === false would be false. To check if things are not equal and not of the same type, you’d use !== (not !=== because the ! takes the place of the first = sign).

The stripos function is slightly different in that it ignores if the characters are upper or lower case, so we should use that instead.

Performing the Search

Now back to our code. We’ll put the stripos function in the conditional, and have the the thing it searches be the array element that gets loaded into it by the foreach loop, $product, with the name key set so it only searches that value. We’ll have it search the $s variable, which is the search term argument that’s passed to the main function. We’ll use the !== operator to check if it returned a value that is not boolean false. If it didn’t, we’ll add it to the list to be displayed. If no matches are found, we’ll display the message that we couldn’t find any.

Screen Shot 2014-12-19 at 4.17.25 PMScreen Shot 2014-12-19 at 4.17.42 PM  Screen Shot 2014-12-19 at 4.16.51 PM

Paginating a List: Controller

Introducing Pagination

What currently happens:

1. Someone clicks the shirts link, and our rewrite rule redirects them to shirts/shirts.php.

2. The controller code in that file requests all the shirts from the product.php model page.

3. The view code, also on the shirt.php page, then displays all the shirts at once.

What will happen with pagination

1. When someone goes to /shirts/, the controller will have to determine which of the shirts pages is being requested (1,2,3 or 4), then which subset of shirts it needs for that page.

2. The controller requests just those shirts from the model products page.

3. The view will then display just those 8 shirts. We’ll add the pagination links above and below the shirts, and the view needs to know which page its on so we can apply the class to that element.

Getting the Count of Products

We’ll need a function that returns the total number of products in the array. To do that, we’ll define a new one. We’ll set a variable equal to the return of get_products_all, then set another variable equal to the count of that, then return that second variable.

Screen Shot 2014-12-19 at 4.35.21 PM

Retrieving the GET Variable

We can use a GET variable for a page number without any rewrite rules because the items available and their order may change, and it makes it easy for people to navigate from page to page by changing the URL. If you’re on the first page though, the url should just be

Screen Shot 2014-12-19 at 4.37.36 PM

First, we’ll set a variable $current_page equal to the get value of the variable pg. We’ll add some test code to check if this worked.

Screen Shot 2014-12-19 at 4.40.17 PM Screen Shot 2014-12-19 at 4.40.14 PM

Now, if there is no pg variable, the $current_page variable will be blank, so we’ll add a conditional to check for this. If the pg GET variable is empty, we’ll set the $current_page variable to 1.

Screen Shot 2014-12-19 at 4.42.50 PM

Adjusting Larger Page Numbers

What if someone enters a pg number higher than the number of pages we have? Lets redirect to the last page, but we need to know how many pages we have. To do this, we’ll ask the model how many pages we have, then calculate the number of pages from that.

We’ll get the total number by setting a variable equal to the return value of our get_products_count variable. We’ll also put the number of products per page in a variable as well, in case that changes later. We want to divide them, but what if we had an uneven result? We can use the ceil() function, which always rounds floats up.

We’ll then add a conditional which checks if the current page is larger than the current page. If it is, we’ll redirect to the last page.

Screen Shot 2014-12-19 at 5.03.46 PM

Adjusting Invalid Page Numbers

What if someone puts a string in the query string, or not a number? We can use the inval() function to convert whatever what entered to become an integer. For most strings, this will set them to 0.

Screen Shot 2014-12-19 at 5.06.55 PM

Let’s add another conditional for if that happens and $current_value gets set to 0.

Screen Shot 2014-12-19 at 5.08.15 PM

So, if someone were to enter words now, it would just redirect them to the main shirts page.

Calculating Shirts From Page Number

Right now our shirts page is showing all the shirts due to the variable $products. We want this to instead show a subset, which takes a starting and ending number.

Screen Shot 2014-12-19 at 5.17.30 PM Screen Shot 2014-12-19 at 5.17.25 PM

Screen Shot 2014-12-19 at 5.18.02 PM

We’ll calculate the ending shirt by multiply the page number by the number of shirts per page. To calculate the starting shirt, will take the the current page number and subtract one from it, to get the previous page’s number, then multiply that by the number of pages to get the last shirt of the last page.. Then, we’ll take that and add one to it.

Now, the last page might not have the full 8 shirts on it, like if we had 31 products total. So, we’ll add a conditional that checks if the $ending shirt if greater than the total number of shirts, and if it is we’ll just set it equal to the total number of shirts.

Screen Shot 2014-12-19 at 5.27.35 PM

Paginating a List: Model and View

Creating the Model Function

We now need to make a function that returns the subset of products for the appropriate page. We’ll go to products.php and make a new function called get_product_subset, which will take two arguments, the position start and the end. We’ll set it up similar to the other functions where we needed a subset, where we declare an empty array, make a variable of the products array, and adding a forearch loop for that. We’ll save the conditional for later, but if it’s true have it add that element to the empty $subset array.

Screen Shot 2014-12-23 at 12.33.01 PM

Limiting the Products

Now, for the conditional. We’ll need a position variable that increases by one for each element in the array. Then, we’ll need to check if that falls within our desired shirt range. We want to only add elements whose position is greater than or equal to the starting position, and less than or equal to the ending one.

We need to add 1 to our position variable each time we loop. We could write $position = $position + 1;, but you can also instead write $position += 1;

Screen Shot 2014-12-23 at 12.46.32 PM

Testing Model Code

We could test on our controller page, but right now there can only be 4 different pages, and 8 items per page. If we test on our models page we can try out some other scenarios. We’ll make a new file called model-test.php, include the products.php file, and var_dump the function. We’ll check for some ranges, like 1 to 2.

Screen Shot 2014-12-23 at 12.51.30 PM Screen Shot 2014-12-23 at 12.51.25 PM

Now to try this on the actual shirts page. The get_list_view_html function is taking the variable $product, so at the top we’ll set that equal to our new subset function and pass in the $start and $end variables.

Screen Shot 2014-12-23 at 1.06.32 PM

Introducing While Loops

Now to add the pagination links. A while loop is like a conditional except it runs multiple times, until its condition is no longer met. You first set a counter variable outside of it, usually named $i and set to 0. Inside the loop, you’ll add a line to increase $i by one for each loop run. For the condition, we’ll test if $i is less than the total number of pages. Here, we’ll echo out $i to show it increasing with each loop.

Screen Shot 2014-12-23 at 1.12.04 PM Screen Shot 2014-12-23 at 1.11.58 PM

To make it easier to mingle the php and html, we can change the top curly bracket of the while loop to a colon (:), and the ending one to endwhile. This makes it easy to wrap each section in its own php tags.

Screen Shot 2014-12-23 at 1.14.56 PM

Adding HTML Markup

First, we’ll wrap the echoed $i in an anchor tags. For the link, we’ll use php to set it equal to the current directory (./) plus ?pg= plus the looped page value for this loop run. This adds the links, and they work!

Screen Shot 2014-12-23 at 1.20.47 PMScreen Shot 2014-12-23 at 1.20.52 PM

Now, we want to make it so that for the page we’re on, that that link isn’t a link and is instead a span. You can do something similar with if conditionals by replacing the curly braces with a colon and endif. We’ll check if $i is equal to the current page. If it is, we’ll add a span. If it’s not, we’ll add a link. Note: you can write an else block after an if block using the colon method with else :.

Screen Shot 2014-12-23 at 1.25.36 PMScreen Shot 2014-12-23 at 1.25.49 PM

Using Partials

We also want the pagination links below our shirts, but we don’t want to copy and paste code. So, we’ll make another include file called list-navigation.html.php. The .html is a common naming practice because this is view code that exists simply to display something on the page. This is aka as a partial. We’ll copy our code into there, then include it when needed in our shirts.php page.

Screen Shot 2014-12-23 at 1.30.00 PM Screen Shot 2014-12-23 at 1.29.55 PM

We should do the same thing for the get_list_view_html code.

Screen Shot 2014-12-23 at 1.32.20 PM

Understanding Comments

Comments are essential in describing what bits of code do in case someone else needs to come in on the project or you need to come back and work on this at a later date. Here are some we added for the shirts.php controller code, and the products.php model page. For functions, you should describe what it does, the arguments it receives and what its return value is. The extra stars are like bullet points and are there just for formatting.

Screen Shot 2014-12-23 at 1.37.48 PMScreen Shot 2014-12-23 at 1.40.17 PM

For functions, people often use a standard format called phpdoc, Arguments are listed as @param, and return values are marked as @return.

Screen Shot 2014-12-23 at 1.41.32 PM


Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s