# Blog Details Page

In this step, you'll create a 'blog details' page in the control panel. This page will allow you to edit existing blog pages, and to create new ones. This will be the most complicated page we've worked on yet. You'll be using some algorithms that should look familiar to you based on some of the sample code we've written together in class. So hopefully that will help to understand the complexity of the code we are about to add to the project.

### Pre-Reqs

- Understanding of how MySQL stores dates, and how to convert to that format
- Understanding of date format specifiers
- Understanding URL query string variables (aka URL parameters)
- Some OO PHP
- How to populate a select box with dynamic options.
- Understanding XSS attacks and how the htmlentities() function can help to prevent them.
- Hidden form inputs

### Creating the Blog Details Page

1. In the control-panel folder, create a file named blog-details.php.
2. Put this code into the file.
3. Take a good look at this code. You should understand all of it. Note the following:
   - It follows the same pattern that all of the other pages on the site use.
   - It includes authentication-check.inc.php, which means that if you haven't logged in before visiting the page, you'll be redirected to the login page.
   - It displays an HTML form on the page. You'll be able to edit blog posts and add new ones by filling out the form.
   - It has an IF statement to handle GET and POST requests.
4. Open the browser and navigate to the page. You'll have to login first, and you should be redirected to the control panel home page. From there, click on the link to the Blog List page. Once you reach the blog list page, click on the link that says "Add New Blog Page". You should be taken to the blog details page. It looks pretty bad right now, but we'll fix that in a minute by adding some CSS code.

### Adding CSS code to clean up the blog details page

1. It might make sense to create a separate style sheet for the control panel, but to keep things simple, we'll just add some rules to the main.css style sheet. Open main.css and add these rule sets before the media query. Note that we should really spend more time making the form look better, but in order to keep things moving we'll cut some corners.
2. Reload the blog details page in the browser. It should be much easier to read now.

### Testing out the Blog Details page

1. Navigate back to the blog-list.php page in the browser.
2. Click one of the links that say EDIT.
3. Notice the url. It has a query string variable named pageId which indicates the id of the blog post that you want to edit.
4. Navigate back to the blog-list page again.
5. Click on the link that says "Add New Blog Page".
6. Press the 'Save' submit button and notice the message that is echoed out at the top of the page.
7. Note that the blog details page will handle both editing blog pages, and adding new ones.
8. Make sure you understand all the code in the IF statement that controls the behavior of the page. This is a very important algorithm to understand.
9. Now take a look at the HTML code in the blog details page, and notice that there is a hidden input for the pageId. Users should be oblivious to the pageId, so we'll keep it hidden from view.
10. Note that there is an input named 'path', we'll ignore that for now. But I'm hoping that we can use it in the future to drastically improve the SEO (search engine optimization) of our blog posts.
11. Note that there is an empty select box in the form. We'll need to populate that with 'categories' from the database (you will be able to categorize your blog posts).
12. Finally, note that the user must enter the date that the blog post is 'published'. We may eventually add a calendar (date picker) that pops up to help users enter a valid date, but for now we'll just force the user to type in the date.
13. If you want, go ahead and comment out the echo() function calls that display messages at the top of the page (there are 3 of them).

Now that you have an idea of how the blog details page should work, you can start to solve the 'small problems'.

### Adding Methods to the PageDataAccess class

The blog details page will allow you to add new blog pages (by using INSERT queries), and edit existing pages (by using UPDATE queries). Now you'll add methods to the PageDataAccess class to run those queries.

1. Add this method to the PageDataAccess class.
2. Now add this method to the PageDataAccess class.
3. Now run some tests to make sure the methods are working. Open page-data-access-tests.php and this code.
4. Open phpMyAdmin in the browser ( localhost/phpmyadmin )
5. Open the test page in the browser to run the insert
6. Use phpMyAdmin to verify that the row was inserted properly
7. Uncomment the code that tests the update, then refresh the test page in the browser
8. Use phpMyAdmin to verify that the row was inserted and updated properly

### Adding 'Date' Functions to the config file

Now you'll add a function that validates a date entered by the user, and another function that will convert a date string from one format to another. Most people are familiar with dates that look like this: m/d/Y. But MySQL requires that dates are stored like this: Y-m-d. So you'll need to convert dates from m/d/Y to Y-m-d before inserting or updating them in the database.

1. Open the config file and add this function.
2. Now add this function.
3. We probably should have done this sooner, but let's now create a test page for functions that are in the config file. Create a file named config-function-tests.php in the tests folder and put this code in it.
4. Note that these tests are more like formal unit tests than the ones we have previously written for this project. They include code that determines whether each test passed or failed.
5. Open the test page in the browser and make sure all tests passed.
6. Spend some time to make sure you understand both the date functions and the test code! Now you have peace of mind that your functions work before using them in the blog details page! If you are having problems understanding the test code, then set a break point near the top of the page and use the debugger to step through it.

### Creating the CategoryDataAccess class

Now you'll start to work on another 'small problem' before getting back to work on the blog-details page. You will be able to categorize your blog posts, and the categories will be stored in the categories table in the database. You could add methods to the PageDataAccess class, but to keep organized and modular, it's common to create a separate 'data access' class for each table in your database.

1. Create a new file in the 'includes' folder and name it CategoryDataAccess.inc.php.
2. Add this code to the file.
   By now, the code should look familiar to you.
3. Now add this method to the CategoryDataAccess class.
   It selects all categories from the database.
4. Create a file in the 'tests' folder and name it category-data-access-tests.php
5. Add this code to the file.
6. Open the test page in the browser to confirm that your code is properly pulling the categories from the database.

### Populating the Drop Down List in the Blog Details Page

Now that you've solved some small problems, and run tests to verify that your code is working, you can get back to work on the blog-details page.

1. Open the blog-details page in your editor and include the CategoryDataAccess class, just under the include to the PageDataAccess class.
2. Now add this function to the bottom of the page. It takes a an array of categories and generates HTML OPTION elements for each one. Note that we did something very similar to this in simple-form.php.
3. Finally, add this code in between the opening and closing SELECT tags on the page.
4. Reload the blog details page in the browser
5. Note that later, we'll add an argument when we call createCategoryOptions() that will 'select' the proper category when blog pages are being edited.

### Adding Page Data to the Blog Details Page

- In the blog-details.php page, add this code just under the $pageTitle and $pageDescription variables.
  Note that it creates an associative array that will eventually store all the input entered into the form. It also instantiates the PageDataAccess class and an array to hold any validation error messages.
- If a GET request is used to view this page, and the pageId query string parameter is in the URL, it means that an existing page is being edited (rather than inserting a new one). So you need to fetch the page that matches the ID in the query string of the URL.
  Add this method call inside the IF statment that checks to see if $_GET['pageId'] 'is set'.
- Also when editing and existing page, you need to 'bind' it's data to the form.
  Replace the entire HTML form on the page with this code.
- Note that the form now includes data from the $page array. PHP code is used to populate the 'value' attributes for text inputs and the innerHTML for textareas. Also note that the ternary operator is used to determine whether or not to 'check' the 'active' radio buttons.
- Note that if a new page is being created (rather than editing an existing one), then the default values for the $page array are used (the default values are mostly empty strings). But when updating a page, then the page defaults are replaced with values from the database query.
- Note that now a second argument is passed into the createCategoryOptions() function (the categoryId) , which sets the select box to the proper option.
- Refresh the page in the browser. Navigate back to the blog list page and then return to the blog details page by clicking both an EDIT link, and the Add New Blog Page link.

### Validating the User Input

- Inside the ELSE IF block that handles POST requests, add this code.
  Note that it collects the user input that was posted and uses it to populate the $page associative array.
  Note that it references a function that has not yet been defined (validatePageInput()), you'll define it soon.
- Also note the use of the 'null coalesce operator' for the 'active' radio buttons.
  Remember that if no radio buttons in a group are 'checked', then no data will be posted when the form is submitted.
- You can't run this code until you've created the validatePageInput() function, so do that now...
  Put this code at the bottom of the page, under the createCategoryOptions() function.
- Now load the page and test that the validation errors are working properly.

### Displaying Validation Error Messages

Right now you are just dumping out the error messages at the top of the page, but in this step you'll make it quite a bit more user-friendly.
But before doing so, add yet another function to the config file...

1. Open the config file and add this function.
2. Note that the function simply takes a string and wraps it in a SPAN tag with a class attribute set to validation-message (see main.css for .validation-message code).
3. Now go back to the blog-details.php page, and replace the entire HTML form with this code.
   This version of the form uses the function that we just added to the config file to display validation error messages.
4. You can comment out the code in blog-details.php that var dumps out the $validationErrors array since you no longer need it.
   You can actually comment out (or delete) the entire ELSE block within that IF statement.
5. Test the page out in the browser to make sure the validation is working, and that the messages are properly displayed.

### Inserting or Updating the Data

Now you'll focus on the IF statement that checks to see if the $validationErrors array is empty. That IF statement currently looks like this:

    if(empty($validationErrors)){
    	// TODO: the input is valid, so we'll send it to the database
    	// But we need to determine whether to INSERT or UPDATE
    }

Update it to look like this:

    if(empty($validationErrors)){

    	// we need to convert the published date to Y-m-d format (this code smells to me!)
    	$page['publishedDate'] = convertDateForMySQL($page['publishedDate']);

    	// Insert or Update (depends on $page['pageId'])
    	if($page['pageId'] > 0){
    		// UPDATE
    		$pda->updatePage($page);
    	}else{
    		// INSERT
    		$pda->insertPage($page);
    	}

    	header("Location: " . PROJECT_DIR . "control-panel/blog-list.php");
    	exit();
    }

- Note first that you must convert the published date to the format required by MySQL.
  Also note the comment (about 'smelly' code). As the project has been evolving, I've been feeling uneasy about some of my design decisions.
  I put the convertDateForMySQL() function in the config file (along with other functions), but you could argue that it doesn't belong there (maybe it belongs in the PageDataAccess class). Many times you'll have these doubts and questions. Sometimes you'll run into situations that make you realize you should have done things differently. Sometimes you find that you were correct to do something in a certain way. You can never fully predict the future, but in order to keep a project moving, you have to try something.
- Note that the code checks the pageId to determine if a blog page is being edited (UPDATE) or created (INSERT).

### Final Thoughts

The code for this page is a tightly choreographed dance!

- Notice how the name attributes of the form inputs match the columns from the 'pages' table in the database. We are using associate arrays as our model object (if you are in the Mobile Apps class you know that we created a 'Task' class for our model objects).
- Notice how the keys in the $error array match the names of form inputs.
- I have used variations of this algorithm for collecting user input many times in my career!

### Follow Up Questions:

- We have duplicate code in the insertPage() and updatePage() methods (in the PageDataAccess class). What could we do to minimize the duplicate code?
- In the insertPage() method, look at the $qStr variable. Why are we required to use curly braces around the variables that are being embedded into the query?
- How can you use a CSS selector to target a tag that has an attribute that is set to a certain value?
- Create a diagram that summarizes the algorithm used to solve the problem of adding and editing blog pages.
- Explain the role of the pageId in the algorithm.