During our development of the LeapIn.it prototype, we have used a number of design patterns, libraries and API’s. This post will give an overview of the most crucial of these.
The server is responsible for creating a REST interface for database access. As mentioned in our post on architecture, we have chosen to use RedBeanPHP to communicate with the database, and Slim.php as a REST endpoint. We have also used tokens for security, PHPImageWorkshop for thumbnail generation and AlchemyAPI for sentiment analysis.
Database access through RedBean
RedBean is an Object-Relational Mapping (ORM), meaning that records in the database are available as objects. For example, the following PHP code may be used to change a person that exists in the database:
// Load the person with ID 209 from the database $person = R::load('person', 209); // Get their name $name = $person->name; // Change their name $person->name = 'Joel'; // Update the database R::store($person); |
Using RedBean results in simple and more maintainable code when accessing the database. It also provides security over direct SQL queries, as it prevents SQL injection.
REST interface through Slim.php
Slim provides a simple way to create a REST endpoint on the server, which we use to allow clients to interact with the database.
As an example, the following code allows a user to create a user. First, the client will sent a POST request to ‘/api/person’ with the username and password in the request body. All interactions with the database are done through RedBean (denoted by R). Using Slim.php, we listen for that response. In addition, we use middleware to ensure that all interactions are in JavaScript Object Notation (JSON) – this is done by adding the variable $requestJSON in the first line. The body of the function first checks if the username is already in use, and if not, creates a “person” with a username and a password (which is stored as a SHA1 hash for security). An avatar is then created by copying a random template (there are about 50 avatar templates in the database by default). The person is then stored in the database and exported as REST to the HTTP response.
287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 | $app->post('/person/', $requestJSON, function () use (&$app, &$params) { if (R::findOne('person', ' username = ? ', array($params->username))) { $app->render(401, [ 'msg' => 'Username already in use.' ]); } else { $person = R::dispense('person'); $person->username = $params->username; $person->password = sha1($params->password); $avatar = R::dup(R::findOne('avatar', ' ORDER BY RAND() LIMIT 1 ')); $avatar->bgcolor = randomColor(); $person->avatar = $avatar; $person->joined = time(); R::store($person); $app->render(201, [ 'response' => exportPerson($person) ]); } }); |
Security through tokens
Because the server and client will reside in different domains, it is not possible to use cookies for authentication. Instead, we have used tokens, which are unique random strings assigned to authenticated users, which they must provide whenever they make a request to the server. This is based on a model provided in this Stack Exchange discussion.
The token is sent as part of the URL. For example, to request a room, the token would be placed on then end: http://leapin.it/api/room/4?token=[user's token]
This is not actually safe over HTTP, as the token may be observed by third parties who see the URL. If a person were to steal a token, they would be able to authenticate themselves as the user by appending the token to the end of URL’s. This is known as session hijacking. To prevent this, encryption will need to be used, ideally through SSL.
Checking for tokens is done as middleware within Slim.php. The middleware is a function which validates that a token exists and identifies the user it belongs to:
$validateToken = function () use ($app) { $tokenKey = $app->request()->params('token'); error_log('checking token ' . $tokenKey); $token = R::findOne('token', ' `key` = ? ', array($tokenKey)); error_log('ok'); if ($token !== null) { error_log('found token with user', $token->person_id); $user = R::load('person', $token->person_id); } if (isset($user) && $user->id !== 0) { $app->user = $user; } else { error_log('no user'); $app->render(401, [ 'msg' => 'Unauthorized.' ]); } }; |
This can then be used when defining a request response, as shown below when fetching a user:
248 249 250 251 | $app->get('/person/:id/', $requestJSON, $validateToken, function ($id) use (&$app, &$params) { $person = R::load('person', intval($id)); $app->render(200, ['result' => exportPerson($person)]); }); |
Thumbnails using PHPImageWorkshop
When viewing posts on the cleint, hexagon thumbnails are displayed to form a honeycomb. These thumbnails are generated using PHPImageWorkshop. As an example, the following code loads a picture, crops it to the largest square possible, and then resizes it to 100×100 pixels. Finally, it save the file.
$layer = ImageWorkshop::initFromPath($file); $layer->cropMaximumInPixel(0, 0, 'MM'); $layer->resizeInPixel(100, 100) imagepng($layer->getResult(), 'thumb.png'); |
Sentiment analysis through AlchemyAPI
To form the honeycomb, all posts needed to be converted to thumbnails, including text. Simply displaying text in a small hexagon would not be effective – it would be too small and cramped to read. Instead, we wanted to display a preview of what the text may contain. We therefore form a preview comprising of the following:
For both keyword extraction and sentiment analysis, we use AlchemyAPI, which has been trained with vast amounts of data, such that it is ready to analyse data with diverse contexts. The sentiment is returned as a value between -1 and 1, which we use to adjust the hue of the hexagon, such that a negative mood is red, and a positive mood is green.
The client is the application which people will use to access LeapIn.it. We use HTML, CSS and JavaScript, and use Apache Cordova to package this as native Android and iPhone apps. Backbone is used to interface with the REST endpoint and Angular is used to generate DOM content.
Apache Cordova to create mobile apps
Cordova provides a wrapper for HTML, CSS and JavaScript, such that it may be packaged as a mobile app. It also exposes a hardware-level API which can be used within JavaScript.
The process of converting a web application to a Cordova application is minimal – it simply needs an app name and icon.
We use Apache Cordova to utilise the camera as a barcode scanner. The following code is responsible for that:
13 14 15 | cordova.plugins.barcodeScanner.scan(function (result) { scan(result.text); // find a room for this code }); |
Backbone used as a REST client
To interface with the REST endpoint on the server, Backbone.js is used. This provides an object mapper for models, allowing us to modify database records much like we would with RedBean on the server. As an example, the following will get and change a room title:
// Define the Rooms collection var Rooms = Backbone.Collection.extend({ url: 'http://leapin.it/api/rooms' }); // Instantiate the collection var rooms = new Rooms(); // Fetch all rooms rooms.fetch().then(function () { // Get room with ID 10 var myroom = people.get(10); // Get the title var title = myroom.get('title'); // Change the title myroom.save({ title: 'myroom' }).then(function () { console.log('SAVED!'); }); }); |
Angular used to generate DOM
Angular provides HTML markup to generate the DOM automatically when data has loaded. For example, the following will generate a list of rooms:
<ul class="rooms"> <li><a href="{{ room.url }}">{{ room.title }}</a></li> </ul> |
Other libraries used on the client
The use of existing design patterns, libraries and API’s has greatly reduced the complexity of development, allowing us to develop three prototypes in a short period of time.