blogblogblogblog SyKoHPaTh

Web Development From Scratch, Day 8

After "Create", there is "Read". Technically we covered part of this yesterday with "get_all_users" in our model, but we need to actually use it - mainly for a login form. It's quite simple, really. Use SQL statements to Read from the database.

First, we'll have to actually create the login form. Nothing too fancy, just a simple "Username" and "Password" setup.

Normally, in an OOP environment, we'd use the same controller for this, but again, since that's a bit later, we're going to create a new controller, called "login.php". In order to keep things from getting messy, we need to rename our "users.php" to "register.php". Why didn't we do this earler? oops. Shoulda planned for that, right?

1) rename controller/users.php to controller/register.php

We need to slightly modify our index.php as well. Since we access our site by typing in /index.php, and we have that require our controller, how do we make it access our other controller? /index.php/register would be a rather nice way to do it, right?

There...sort of is...a way to do it without using an actual framework. We're going to have "ugly" URL's as a result, but there's ways around that, although this is actually beneficial from an SEO standpoint!

1) grab the full URL that was sent: http:///index.php/register
2) Parse that: [host], [path]/index.php/register
3) break that up: [0]=> [1]=>index.php [2]=>register
4) If [2] is set, call that "controller", else default to the "index" controller.

Code Sample:
  1. <?php
  2. require "../../includes/db_connect.php"; //modify to your needs
  3. $url = "http://" . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
  4. $urlParse = parse_url($url);
  5. $urlParts = explode('/', $urlParse['path']);
  6. if(isset($urlParts[3])){
  7. //check if file exists
  8. $controller = "controller/" . $urlParts[3] . ".php";
  9. if(!file_exists($controller)){
  10. die("Controller not found: " . $controller);
  11. }
  12. } else {
  13. $controller = "controller/index.php";
  14. }
  15. require $controller; //pass to controller
  16. ?>

* Your initial database require should not change, if it's working from last time, don't change it to match what I have!
* We use $_SERVER superglobals to craft the full URL. Assume we're hitting it from http (we should test for https, honestly)
* Parse_url gives us an array of the url...
* ...and we explode that array into the parts.
* I use the third position, since I'm hitting this from ([1]=> [2]=>index.php [3]=>register). If you're at http:///index.php, you'll be using position 2 to grab the controller name ([1]=>index.php, [2]=>/)
* Need to test if that file even exists! If not, error out.

Since we set a default controller to "index.php", we want this page to be the first page that pulls up, and not directly to our register page. So, let's whip up the following pages:

Code Sample:
  1. <?php
  2. if($urlParts[2] == "" || $urlParts[3] == ""){
  3. //index.php was not supplied, so modify links to reflect that
  4. $prefix = "index.php/";
  5. }
  6. require "view/index.php";
  7. ?>

Code Sample:
  1. <?php
  2. if(!isset($prefix)){ $prefix = ""; } //init
  3. ?><html>
  4. <head>
  5. <title>User Management System</title>
  6. </head>
  7. <body>
  8. <?php if($_SESSION["logged_in"]=="YES"): ?>
  9. Welcome Back, <?php echo $_SESSION['name']; ?>!
  10. Logout
  11. <?php else: ?>
  12. Login
  13. Register
  14. <?php endif; ?>
  15. <?php print_r($_SESSION); ?>
  16. </body>
  17. </html>

* Since this is a landing page which can be accessed through index.php via http:/// - we need to make sure the links are flexible to handle http:/// as well as http:///index.php - we do this by checking our url parts that were set in our index.php for the controller checker.
* We have a check to see if the user is logged in. If they are, show their name, and a link to logout. If they aren't, show a link to login.
* For testing/learning purposes, a print_r($_SESSION) displays all that was set once the user is logged in. It's an unneccessary line.

After making those changes, try them out. Go to index.php/test, as well as index.php/register - test should error, while register should pull up the registration form we made yesterday. Try index.php/ and the new index page we made should pull up.

One last thing, change the form location in view/user_form.php to point to the new location:
<form name="user_registration" action="register" method="post">

Now to move on to the login form! Not much to the view:

Code Sample:
  1. <html>
  2. <head>
  3. <title>User Login Form</title>
  4. </head>
  5. <body>
  6. <form name="user_login" action="login" method="post">
  7. Username
  8. <input type="text" name="username">
  9. Password
  10. <input type="password" name="password">
  11. Errors:
  12. <input type="submit" name="submit" value="Submit Form">
  13. </form>
  14. </body>
  15. </html>

Next our controller, a little bit more complicated:

Code Sample:
  1. <?php
  2. require "model/user_model.php";
  3. //check if logged in!
  4. if($_SESSION['logged_in'] == "YES"){
  5. require "view/index.php";
  6. exit;
  7. }
  8. //login user
  9. if(isset($_POST['submit'])){
  10. //setup array to match table fields=>form values
  11. $form = array(
  12. "username" => filter_var($_POST['username'], FILTER_SANITIZE_STRING),
  13. "password" => filter_var($_POST['password'], FILTER_SANITIZE_STRING)
  14. );
  15. //secure/encrypt the password
  16. $salt = "pepper";
  17. $form['password'] = md5($salt md5($form['password']));
  18. //check if user and password matches
  19. $errors = validate_user($websitetest_db, $form);
  20. if(is_array($errors)){
  21. //if so, cram that user into session
  22. $_SESSION['logged_in'] = "YES";
  23. $_SESSION['username'] = $errors['username'];
  24. $_SESSION['name'] = $errors['name'];
  25. require "view/index.php";
  26. exit;
  27. }
  28. }
  29. require "view/user_login.php";
  30. ?>

* This is VERY similar to the register.php controller. We still sanitize our fields, and then encrypt the password.
* We pass our associative array (containing the username and password) to the validate_user function, which resides in the model.
* We are passed back an array if the user is validated, and we set the fields according to what was passed back - in this case, only set the flag that the user is logged_in, as well as the username and their name.
* On completion, call the index view, which contains a detection for if the user is logged in.
* If we are not passed back an array, it's an error, so simply call the login view again, which displays the error.

We add the following function to the model:

Code Sample:
  1. ...
  2. function validate_user($db_conn, $dataArray){
  3. $sql = "SELECT * FROM users WHERE username='" . $dataArray['username'] . "'";
  4. $result = mysqli_query($db_conn, $sql);
  5. if(!$result){
  6. die('SQL Error: ' . mysqli_error($db_conn));
  7. }
  8. if(mysqli_num_rows($result) > 0){
  9. $row = mysqli_fetch_assoc($result);
  10. if($row['password']==$dataArray['password']){
  11. return $row; //pass user account back
  12. } else {
  13. return "Incorrect Password";
  14. }
  15. } else {
  16. return "Username not found";
  17. }
  18. }
  19. ...

* Ok, "finally" we deal with the "Read" part of CRUD.
* Using SQL, just pull all records that match the passed username. (Note that we haven't put in a duplicate username filter in our registration yet!)
* If there's more than 0 rows returned, it means that a matching record was found. Otherwise, 0 rows returned means there were no records that match the passed username.
* Compare the pulled password with the passed password - either it matches, or it doesn't.

After all this, we'll have the following in our $_SESSION:
Array ( [logged_in] => YES [username] => testAccount [name] => Test Account )

Now, if we try to login as "test" (Bob Bobkins!) we'll see that it doesn't work...because the password it's checking against is unencrypted. We'll deal with that one record in a later section (Delete!).

Our login system is almost complete. We just need to "logout". Painfully simple!

Code Sample:
  1. <?php
  2. //check if logged in!
  3. if($_SESSION['logged_in'] == "YES"){
  4. unset($_SESSION);
  5. }
  6. require "view/index.php";
  7. ?>

GOOD NESS. That was quite a bit for "Read", wasn't it? Well, a login system is the...I don't want to say "backbone"'s more like the "kidneys" of a user management system.

Next will be the user "Profile" once they are logged in. This will cover "Update" part of CRUD.

php, tutorial, crud, update