Tuesday, June 6, 2017

POST Requests and MongoDB Using PHP 7

Hi there! Captain Chaos is back with a brand new bag. A friend of mine wanted me to focus on "bread and butter" rather than Google arcana, so I took a detour into PHP 7. One nasty shock was learning Composer was unavoidable. That meant learning a bunch of new commands, new magic words, and configuration files. You can find installation instructions at https://getcomposer.org/doc/00-intro.md .

I decided to start with what I hoped was something simple... a POST form with nothing but text fields and a submit button, plus the equivalent of "SELECT *" without a WHERE clause. I like to avoid SQL and strings that should be checked by a compiler (and aren't) whenever possible, so I went with trusty-rusty MongoDB. I soon found out that installing a driver was just the beginning. So, I bit the bullet and installed Composer. After some googling and error, I learned the following spell:

composer require mongodb/mongodb

That conjured up a "composer.json" file and a whole directory of stuff. Fair enough. I started with a fairly simple-minded index.php...

<html>
    <head>
        <meta charset="UTF-8">
        <title>PHP Mongo Example</title>
    </head>
    <body>
        <form action="verify.php" method="post">
            <div>
                <label for="first_name">First name: </label>
                <input type="text" id="first_name" name="first_name" required>
            </div>
            <div>
                <label for="last_name">Last name:  </label>
                <input type="text" id="last_name" name="last_name" required>
            </div>
            <input type="submit" value="Continue">
        </form>
    </body>
</html>    

That gave me a functioning form, so so far so good. Next, I wrote a Name class (in the "name.php" file) with a function that will convert a Name to a string using just typecasting...

<?php

/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
namespace php2;

/**
 * Description of Name
 *
 * @author child
 */
class Name {
    public $firstName = "";
    public $lastName = "";
 
    function __construct(string $first, string $last) {
        $this->firstName = $first;
        $this->lastName = $last;
    }
 
    function __toString() {
        return "$this->firstName $this->lastName";
    }
}

Notice that I used "__construct" rather than the class's name for the constructor. That seems to be a permanent fixture of PHP, but might come as a surprise to some. I don't know how old "__toString" is, but at least for PHP 7.0 it does the job. The gotcha here is a simple "include" statement won't access a user-defined class when using Composer. That meant casting this spell:

composer dump-autoload -o

I'll tell you now I use Windows, and that version of Composer installs an actual program, along with the "composer" command. You might need to use "php composer.phar" instead.

Lastly came the fun part, the "business logic". At first, I blindly used the old chestnut

$firstName = $_POST['first_name'];
$lastName = $_POST['last_name'];

Not so fast! That approach doesn't work in PHP 7. Instead, I learned the following from my IDE:

 $submitted = !is_null($_POST);
        if ($submitted) {
            $firstName = filter_input(INPUT_POST, 'first_name');
            $lastName = filter_input(INPUT_POST, 'last_name');

A lot more verbose, but it got the job done. Plus, instead of the old "include" statements, I needed:

        require 'vendor/autoload.php';
        use php2\Name;
        use MongoDB\Client;

Not as simpleminded, but more in line with other object-oriented languages. With the bullocks out of the way, things were fairly straightforward:

        $name = new Name($firstName, $lastName);
        echo 'Submitted ' . (string) $name . "<br />";
        $client = new Client();
        $collection = $client->php2->Names;
        $collection->insertOne(['first_name' => $name->firstName, 'last_name' => $name->lastName]);
        $cursor = $collection->find();
        echo '<ul>';
        foreach ($cursor as $document) {
            $foundName = new Name((string) $document['first_name'], (string) $document['last_name']);
            echo '<li>' . (string) $foundName . '</li>';
        }
        echo '</ul>';
        } else {
            echo 'Submission failed! <br />';
         
        }

For those familiar with MongoDB, this should be familiar; "php2" is the database and "Names" is the collection. For those unfamiliar with NoSQL, a collection is similar to a database table, except there's no schema. Pretty much anything goes. A "document" is like a table row, but it isn't constrained to any particular structure. Typically, a collection contains objects (documents) of the same class. You're not hallucinating; PHP 7 has actual data types. One nice thing I learned is I didn't need to specify the namespace in a "new" statement as long as there are no class name conflicts.

Well, that's all for now! This sorcerer's apprentice learned how to clean a floor. I hope you found this rambling useful. Cheerio!

No comments:

Post a Comment