-
Notifications
You must be signed in to change notification settings - Fork 1
Home
Unlike other PHP apps, that embed the HTML/CSS within the PHP scripts, we are going to be placing a separate layer between the HTML/CSS front-end and the PHP back-end with a data-binding JS framework called KnockoutJS.
We also do not want unnecessary code repetition. So we are utilizing PHP to serve up parts of our web page view that remain the same, such as the navigation bar and the footer for instance.
- index.php - our homepage
- layout
- header.php - the navbar / header items of the html - links stylesheets and scripts (although scripts should be at the bottom in the footer for faster loading)
- footer.php - finishes off the body and html tag, eventually, we'll move the script links down here to encourage faster loading
- js
- app.js - the so-called Knockout ViewModel we are using, this enables the data-binding with the Document Object Model (which is the HTML structures/tags we're using in index.php)
- jquery.js - jQuery - a better version of Javascript - allows us to interact with DOM elements
- knockout.js - KnockoutJS - the js for the overall library we are using for our viewmodel
- php
- connect.php - provides the connection information for our database from cPanel, always will be included in our other php functions
- getSample.php - demonstrates how to handle a GET request
- postSample.php - demonstrates how to handle a POST request
We should always try to have our php functions in a separate folder, because we want our PHP functions to try not to have any HTML embedding. We simply just want to use our PHP functions solely to receive data from the database to pass to the front-end.
Our layout folder will always have a header and a footer - those remain constant so that we can include them on index.php or about.php. Don't be fooled. Even though index.php has a php extension, we still want to only have pure HTML in index.php or any other .php files that are not within the php folder. They only have the php extension so that they can do an include header.php and an include footer.php. So when you're designing your pages, try to incorporate data-binding as much as possible within your HTML so that we don't have to embed actual PHP code. It's just easier to understand that way.
Of course, here I just added all of the frameworks/libraries we needed in the js folder, but in general, I've installed all of those through bower on the actual project. So those dependencies should be covered.
The file connect.php is the php file that all of our php functions will connect to. It's important to always do a include_once 'connect.php' at the top of all of your php files. It will give you a connection to our database server.
From there, within your own php files, you can select the database (in this case, we will just have one) and then do a mysql_query. sprintf(SQL Query) is a safe way to prep the query statement for data retrieval/insertion. If you look at the postSample.php, you can actually use C-string-like value replacements for strings or ints should you pass in values you want to plug into your queries from the Javascript.
After querying, the main thing to notice is the while loop. I create an empty array that can then be dynamically sized. mysql_fetch_array fetches one row from the SQL result at a time until it's null and there's nothing to fetch. We can then add that to a row in our array. Always do that every time because the result returned from the query is not always compatible to be sent back the front-end/data-binding layer as JSON. Always do the following statement to send the data received from SQL as JSON.
echo json_encode($resultArray)We can use $_POST['attributeName'] to get the value that we passed in from our AJAX requests so that we can then execute our Update / Insert / Delete queries. You can also do this for Select queries too. It's just a way to pass custom parameters from the front-end to the back-end.
I forgot to do this on getSample.php, but it's good practice to close your connection after you've used it.
We always do a if(!$query) error check. It's good practice and it can be propagated back to the AJAX request so that we can notify the user if something went wrong.
I highly recommend the tutorials that the website for Knockout provides. They were super helpful for me to understand just how Knockout could bridge the gap between front-end and back-end in terms of data display.
The main function of Knockout is to data-bind. What this means is that if a user were to change a value that they typed in a text box for instance or if we wanted instantaneous UI updates of new data we just received from the database, this can all be done almost asynchronously or instantaneously with Knockout.
Knockout uses a modified format of Model-View-Controller that they lovingly dub Model-View-View-Model. Model indicates our data layer. So for our purposes, Model is our php functions that return back some kind of data. View indicates our HTML/CSS front-end layer, what the user sees. This is index.php. View-Model is app.js, which is primarily Knockout functions and everything of that sort. So let's dive in deeper.
I did a $(document).ready... first because I want to make sure that everything loads when the user first loads our webpage. Within the function, you'll notice a ko.applyBindings(new ViewModel()). This is to ensure that our Knockout View Model loads all initial data that we need to be loaded (like the table of award shows because why not right?).
Next we define our View Model as a Javascript object (basically a fancy function). For scoping purposes and easier understanding, I declared this as self. You'll notice it used all over the View Model. I find it easier to understand.
We can create custom objects within our ViewModel as well. You can see that I created one for award shows so that we can keep all of the data together in one cohesive object.
There are two types of variable types that we will primarily use in this View Model. The first is ko.observable(some default value here (could be string or int)) and the other is ko.observableArray([]). Observable means that these variables update automatically based on UI changes and user inputs. That data change propagates through the Document Object Model to our variables. We can then not have to worry about whether or not these values are the most up-to-date values before performing an AJAX request or something that requires a database CRUD operation.
You'll notice within AwardShow that based on the parameters passed in, I have observable values for those as well. So when the object is created, if any of its attributes or variables get updated by some other user input or user action, those changes are automatically noticed as well. It's fine to have an || within declaring ko.observables because if the user doesn't enter a value or if there's a null value, we can set it to default value instead.
$.getJSON('php/someFunction.php', function(dataReceivedBack) {
var mappedData = $.map(dataReceivedBack, function(dataItem) {
return new CustomObject(dataItem.Attribute1, dataItem.Attribute2, dataItem.Attribute3, ...);
});
self.observableArrayName(mappedData)
});So the thing about observables is that they act like functions. Mostly because they are indeed capturing data changes all the time. So if you want to set the value of an observable array or an observable to something to overwrite its previous value, you can do self.observableVar(someData). This will set its value based on some data that you receive back. This is once you have declared self.observableVar as equal to either a ko.observable() or a ko.observableArray().
This function above is executed when ko.applyBindings is executed. If you go to the demo home page, you'll notice that the awards are already populated in the table. That's because this function executed at the very beginning, populating the observable array with data grabbed from a GET request.
Now let's look at self.postToDB. We can find a function to be executed in the HTML/CSS so that if a button is clicked or something else happens, this function will be executed. This function is an example of an AJAX request. We will typically do ajax requests in the format shown, where we give the url of the php function we wrote, then the type (post or get) of request, then the data. I've shown an example of using a query string to pass back user populated values, but you can also do the following to pass back JSON:
data: {
"value1": "some value here replaceable by KO variables",
"value2": "some value here replaceable by KO variables"
}This creates a JSON object to be send back with attributes value1, value2 and values for those attributes to be taken from observables. Again, because observables act like functions, you always need parentheses to access their values. encodeURIComponent(...) serves to make sure that the data being passed back is URL safe (like inserting ASCII values for spaces or other URL-unsafe characters).
Every AJAX request can have callbacks. Success and error are the common ones. When the AJAX request is finished executing, it will execute the function in a callback to indicate that it is indeed finished. We should use these to tell the user that yes, their data went through or yes, their data was received back.
So we have our header/navbar information in header.php, and our footer information in footer.php. We do include statements at the top and the bottom so that we don't have so much code repetition. But let's look at index.php, where we integrate the data-binding aspects within our HTML.
Knockout has many bindings that can be integrated in different ways into our HTML. I suggest you go check them out at their website. Their documentation is pretty good.
If we have an observableArray, such as the one we have for our AwardShow objects, we can do a foreach binding and we can see all of the award shows dynamically populated. Saves code, saves writing, and saves mistakes.
Within the <input> tags, we can see the value binding. When the user types something in that input, its value will automatically update the variable value1 or value2. If you do an evaluation of the variable with Chrome Dev Tools, you can see the change. Let's say you're displaying the value that was inputted in a <span> tag or something, you can use a text bind to display the value. So value is a write-to binding, text is a read-from binding.
The click binding used on the button links to a function within our app.js view model that needs to be executed. This is usually the use case for the click binding. If your function requires parameters, you can do the following:
data-bind="click: function(parameter1 [optional]) { specificFunctionName(parameter1 [could be another observable variable] ...)}"Every binding we use in the HTML/CSS that we want to link to the KnockoutJS view model, we always include the data-bind attribute on the HTML element. Then, within the attribute, we specify the specific binding type, as well as the function or the variable name tied to it.
Fin.