Author Archives: Gonzalo Ayuso

Generating push notifications with Pushbullet and Silex

Sometimes I need to send push notifications to mobile apps (Android or IOS). It’s not difficult. Maybe it’s a bit nightmare the first times, but when you understand the process, it’s straightforward. Last days I discover a cool service called PushBullet. It allows us to install one client in our Android/IOS or even desktop computer, and send push notifications between them.

Pushbullet also has a good API, and it allows us to automate our push notifications. I’ve play a little bit with the API and my Raspberry Pi – home server. It’s really simple to integrate the API with our Silex backend and send push notifications to our registered devices.

I’ve created one small service provider to enclose the API. The idea is to use one Silex application like this

use Silex\Application;
use PushSilex\Silex\Provider\PushbulletServiceProvider;

$app = new Application(['debug' => true]);

$myToken = include(__DIR__ . '/../conf/token.php');

$app->register(new PushbulletServiceProvider($myToken));

$app->get("/", function () {
    return "Usage: GET /note/{title}/{body}";
});

$app->get("/note/{title}/{body}", function (Application $app, $title, $body) {
    return $app->json($app['pushbullet.note']($title, $body));
});

$app->run();

As we can see we’re using one service providers called PushbulletServiceProvider. This service provides us ‘pushbullet.note’ and allows to send push notifications. We only need to configure our Service Provider with our Pushbulled’s token and that’s all.

<?php
namespace PushSilex\Silex\Provider;
use Silex\ServiceProviderInterface;
use Silex\Application;
class PushbulletServiceProvider implements ServiceProviderInterface
{
    private $accessToken;
    const URI = 'https://api.pushbullet.com/v2/pushes';
    const NOTE = 'note';
    public function __construct($accessToken)
    {
        $this->accessToken = $accessToken;
    }
    public function register(Application $app)
    {
        $app['pushbullet.note'] = $app->protect(function ($title, $body) {
            return $this->push(self::NOTE, $title, $body);
        });
    }
    private function push($type, $title, $body)
    {
        $data = [
            'type'  => $type,
            'title' => $title,
            'body'  => $body,
        ];
        $ch = curl_init();
        curl_setopt_array($ch, [
            CURLOPT_URL            => self::URI,
            CURLOPT_HTTPHEADER     => ['Content-Type' => 'application/json'],
            CURLOPT_CUSTOMREQUEST  => 'POST',
            CURLOPT_POSTFIELDS     => $data,
            CURLOPT_HTTPAUTH       => CURLAUTH_BASIC,
            CURLOPT_USERPWD        => $this->accessToken . ':',
            CURLOPT_RETURNTRANSFER => true
        ]);
        $out = curl_exec($ch);
        curl_close($ch);

        return json_decode($out);
    }
    public function boot(Application $app)
    {
    }
}

Normally I use Guzzle to handle HTTP clients, but in this example I’ve created a raw curl connection.

You can see the project in my github account

i18n AngularJS provider

There’s more than one way to perform i18n translations within our AngularJS projects. IMHO the best one is https://angular-translate.github.io/, but today I’m going to show you how I’m doing translations in my small AngularJS projects (normally Ionic projects).

I’ve packaged my custom solution and I also create one bower package ready to use via bower command line:

bower install ng-i8n --save

First we add our provider

<script src='lib/ng-i8n/dist/i8n.min.js'></script>

And now we add our new module (‘gonzalo123.i18n’) to our AngularJS project

angular.module('G', ['ionic', 'ngCordova', 'gonzalo123.i18n'])

Now we’re ready to initialise our provider with the default language and translation data

    .config(function (i18nProvider, Conf) {
        i18nProvider.init(Conf.defaultLang, Conf.lang);
    })

I like to use constants to store default lang and translation table, but it isn’t necessary. We can just pass the default language and Lang object to our provider

    .constant('Conf', {
        defaultLang: 'es',
        lang: {
            HI: {
                en: 'Hello',
                es: 'Hola'
            }
        }
    })

And that’s all. We can translate key in templates (the project also provides a filter):

<h1 class="title">{{ 'HI' | i18n }}</h1>

And also inside our controllers

    .controller('HomeController', function ($scope, i18n) {
        $scope.hi = i18n.traslate('HI');
    })

If we need to change user language, we only need to trigger ‘use’ function:

    .controller('HomeController', function ($scope, i18n) {
        $scope.changeLang = function(lang) {
            i18n.use(lang);
        };
    })

Here we can see the code of our provider:

(function () {
    "use strict";

    angular.module('gonzalo123.i8n', [])
        .provider('i18n', function () {
            var myLang = {},
                userLang = 'en',
                translate;

            translate = function (key) {
                if (myLang.hasOwnProperty(key)) {
                    return myLang[key][userLang] || key;
                } else {
                    return key;
                }
            };

            this.$get = function () {
                return {
                    use: this.use,
                    translate: translate
                };
            };

            this.use = function (lang) {
                userLang = lang;
            };

            this.init = function (lang, conf) {
                userLang = lang;
                myLang = conf;
            };
        })

        .filter('i18n', ['i18n', function (i18n) {
            var i18nFilter = function (key) {
                return i18n.translate(key);
            };

            i8nFilter.$stateful = true;

            return i18nFilter;
        }])
    ;
})();

Anyway the project is in my github account

Microservice container with Guzzle

This days I’m reading about Microservices. The idea is great. Instead of building a monolithic script using one language/framowork. We create isolated services and we build our application using those services (speaking HTTP between services and application).

That’s means we’ll have several microservices and we need to use them, and maybe sometimes change one service with another one. In this post I want to build one small container to handle those microservices. Similar idea than Dependency Injection Containers.

As we’re going to speak HTTP, we need a HTTP client. We can build one using curl, but in PHP world we have Guzzle, a great HTTP client library. In fact Guzzle has something similar than the idea of this post: Guzzle services, but I want something more siple.

Imagine we have different services:
One Silex service (PHP + Silex)

use Silex\Application;

$app = new Application();

$app->get('/hello/{username}', function($username) {
    return "Hello {$username} from silex service";
});

$app->run();

Another PHP service. This one using Slim framework

use Slim\Slim;

$app = new Slim();

$app->get('/hello/:username', function ($username) {
    echo "Hello {$username} from slim service";
});

$app->run();

And finally one Python service using Flask framework

from flask import Flask, jsonify
app = Flask(__name__)

@app.route('/hello/<username>')
def show_user_profile(username):
    return "Hello %s from flask service" % username

if __name__ == "__main__":
    app.run(debug=True, host='0.0.0.0', port=5000)

Now, with our simple container we can use one service or another

use Symfony\Component\Config\FileLocator;
use MSIC\Loader\YamlFileLoader;
use MSIC\Container;

$container = new Container();

$ymlLoader = new YamlFileLoader($container, new FileLocator(__DIR__));
$ymlLoader->load('container.yml');

echo $container->getService('flaskServer')->get('/hello/Gonzalo')->getBody() . "\n";
echo $container->getService('silexServer')->get('/hello/Gonzalo')->getBody() . "\n";
echo $container->getService('slimServer')->get('/hello/Gonzalo')->getBody() . "\n";

And that’s all. You can see the project in my github account.

PHP Dumper using Websockets

Another crazy idea. I want to dump my backend output in the browser’s console. There’re several PHP dumpers. For example Raul Fraile’s LadyBug. There’re also libraries to do exactly what I want to do, such as Chrome Logger. But I wanted to use Websockets and dump values in real time, without waiting to the end of backend script. Why? The answer is simple: Because I wanted to it :)

I’ve written several post about Websockets, Silex, PHP. In this case I’ll use a similar approach than the previous posts. First I’ve created a simple Webscocket server with socket.io. This server also starts a Express server to handle internal messages from the Silex Backend

var CONF = {
        IO: {HOST: '0.0.0.0', PORT: 8888},
        EXPRESS: {HOST: '0.0.0.0', PORT: 26300}
    },
    express = require('express'),
    expressApp = express(),
    server = require('http').Server(expressApp),
    io = require('socket.io')(server, {origins: 'localhost:*'})
    ;

expressApp.get('/:type/:session/:message', function (req, res) {
    console.log(req.params);
    var session = req.params.session,
        type = req.params.type,
        message = req.params.message;

    io.sockets.emit('dumper.' + session, {title: type, data: JSON.parse(message)});
    res.json('OK');
});

io.sockets.on('connection', function (socket) {
    console.log("Socket connected!");
});

expressApp.listen(CONF.EXPRESS.PORT, CONF.EXPRESS.HOST, function () {
    console.log('Express started');
});

server.listen(CONF.IO.PORT, CONF.IO.HOST, function () {
    console.log('IO started');
});

Now we create a simple Service provider to connect our Silex Backend to our Express server (and send the dumper’s messages using the websocket connection)

<?php

namespace Dumper\Silex\Provider;

use Silex\Application;
use Silex\ServiceProviderInterface;
use Dumper\Dumper;
use Silex\Provider\SessionServiceProvider;
use GuzzleHttp\Client;

class DumperServiceProvider implements ServiceProviderInterface
{
    private $wsConnector;
    private $client;

    public function __construct(Client $client, $wsConnector)
    {
        $this->client = $client;
        $this->wsConnector = $wsConnector;
    }

    public function register(Application $app)
    {
        $app->register(new SessionServiceProvider());

        $app['dumper'] = function () use ($app) {
            return new Dumper($this->client, $this->wsConnector, $app['session']->get('uid'));
        };

        $app['dumper.init'] = $app->protect(function ($uid) use ($app) {
            $app['session']->set('uid', $uid);
        });

        $app['dumper.uid'] = function () use ($app) {
            return $app['session']->get('uid');
        };
    }

    public function boot(Application $app)
    {
    }
}

Finally our Silex Application looks like that:

include __DIR__ . '/../vendor/autoload.php';

use Silex\Application;
use Silex\Provider\TwigServiceProvider;
use Dumper\Silex\Provider\DumperServiceProvider;
use GuzzleHttp\Client;

$app = new Application([
    'debug' => true
]);

$app->register(new DumperServiceProvider(new Client(), 'http://192.168.1.104:26300'));

$app->register(new TwigServiceProvider(), [
    'twig.path' => __DIR__ . '/../views',
]);

$app->get("/", function (Application $app) {
    $uid = uniqid();

    $app['dumper.init']($uid);

    return $app['twig']->render('index.twig', [
        'uid' => $uid
    ]);
});

$app->get('/api/hello', function (Application $app) {
    $app['dumper']->error("Hello world1");
    $app['dumper']->info([1,2,3]);

    return $app->json('OK');
});


$app->run();

In the client side we have one index.html. I’ve created Twig template to pass uid to the dumper object (the websocket channel to listen to), but we also can fetch this uid from the backend with one ajax call.

<!DOCTYPE html>
<html>
<head lang="en">
    <meta charset="UTF-8">
    <title>Dumper example</title>
</head>
<body>

<a href="#" onclick="api('hello')">hello</a>

<!-- We use jQuery just for the demo. Library doesn't need jQuery -->
<script src="assets/jquery/dist/jquery.min.js"></script>
<!-- We load the library -->
<script src="js/dumper.js"></script>

<script>
    dumper.startSocketIo('{{ uid }}', '//localhost:8888');
    function api(name) {
        // we perform a remote api ajax call that triggers websockets
        $.getJSON('/api/' + name, function (data) {
            // Doing nothing. We only call the api to test php dumper
        });
    }
</script>
</body>
</html>

I use jQuery to handle ajax request and to connect to the websocket dumper object (it doesn’t deppend on jQuery, only depend on socket.io)

var dumper = (function () {
    var socket, sessionUid, socketUri, init;

    init = function () {
        if (typeof(io) === 'undefined') {
            setTimeout(init, 100);
        } else {
            socket = io(socketUri);

            socket.on('dumper.' + sessionUid, function (data) {
                console.group('Dumper:', data.title);
                switch (data.title) {
                    case 'emergency':
                    case 'alert':
                    case 'critical':
                    case 'error':
                        console.error(data.data);
                        break;
                    case 'warning':
                        console.warn(data.data);
                        break;
                    case 'notice':
                    case 'info':
                    //case 'debug':
                        console.info(data.data);
                        break;
                    default:
                        console.log(data.data);
                }
                console.groupEnd();
            });
        }
    };

    return {
        startSocketIo: function (uid, uri) {
            var script = document.createElement('script');
            var node = document.getElementsByTagName('script')[0];

            sessionUid = uid;
            socketUri = uri;
            script.src = socketUri + '/socket.io/socket.io.js';
            node.parentNode.insertBefore(script, node);

            init();
        }
    };
})();

Source code is available in my github account

Building TCP server daemond with PHP and Ratchet

In my daily work I normally play a lot with TCP servers, clients and things like that. I like to use Linux’s xinet.d daemon to handle the TCP ports.

I’ve also written something about it. This approach works fine. You don’t need to open any port. Xinet.d opens the ports and invoke the PHP scripts. The problem appears when we call intensively our xinet.d server. It creates one PHP instance per request. It isn’t a problem with one request in, for example, 3 seconds, but if we need to handle 10 requests per second our server load will grow. The solution: a dedicated server.

With PHP we can create dedicated servers using, for example, Ratchet. I want to create a library using Ratchet to open TCP ports and register callbacks to those ports to handle the requests (Reactor pattern). Do you know Silex? Of course you know. This library borrows the idea of Silex (register callbacks to routes) to the TCP world.

Let me show examples:

Example 1:

use React\EventLoop\Factory as LoopFactory;
use G\Pxi\Pxinetd;

$loop = LoopFactory::create();
$service = new Pxinetd('0.0.0.0');

$service->on(8080, function ($data) {
    echo $data;
});

$service->register($loop);
$loop->run();

That’s the simplest example. A TCP echo server. We open 8080 port to all interfaces (0.0.0.0) and we return a simple input echo.

We can start different ports also:

use G\Pxi\Pxinetd;
use React\EventLoop\Factory as LoopFactory;

$loop = LoopFactory::create();
$service = new Pxinetd('0.0.0.0');

$service->on(8080, function ($data) {
    echo $data;
});

$service->on(8888, function ($data) {
    echo $data;
});

$service->register($loop);
$loop->run();

Example 2:
We can also work with the connection

use G\Pxi\Pxinetd;
use G\Pxi\Connection;
use React\EventLoop\Factory as LoopFactory;

$loop = LoopFactory::create();
$service = new Pxinetd('0.0.0.0');

$service->on(8080, function ($data) {
    echo $data;
});

$service->on(8088, function ($data, Connection $conn) {
    var_dump($conn->getRemoteAddress());
    echo $data;
    $conn->send("....");
    $conn->close();
});

$service->register($loop);
$loop->run();

Example 3:
I’m a big fan of YAML configurations, so we can load configurations from a YAML file, of course:

conf3.yml:

ports:
  9999:
    class: Services\Reader1
// Services/Reader1.php
use G\Pxi\Connection;
use G\Pxi\MessageIface;

class Reader1 implements MessageIface
{
    public function onMessage($data, Connection $conn)
    {
        echo $data . $conn->getRemoteAddress();
    }
}
use G\Pxi\Pxinetd;
use G\Pxi\YamlFileLoader;
use Symfony\Component\Config\FileLocator;
use React\EventLoop\Factory as LoopFactory;

$loop = LoopFactory::create();
$service = new Pxinetd('0.0.0.0');

$loader = new YamlFileLoader($service, new FileLocator(__DIR__ ));
$loader->load('conf3.yml');

$service->on(8080, function ($data) {
    echo "$data";
});

$service->register($loop);
$loop->run();

Example 4:
We’re using symfony/config and symfony/yaml components, so we can use hierarchy within our yaml files:

use G\Pxi\Pxinetd;
use G\Pxi\YamlFileLoader;
use Symfony\Component\Config\FileLocator;
use React\EventLoop\Factory as LoopFactory;

$loop = LoopFactory::create();
$service = new Pxinetd('0.0.0.0');

$loader = new YamlFileLoader($service, new FileLocator(__DIR__));
$loader->load('conf4.yml');

$service->on(8080, function ($data) {
    echo "$data";
});

$service->register($loop);
$loop->run();

config4.yml:

imports:
  - { resource: conf4_2.yml }
ports:
  9999:
    class: Services\Reader1

config4_2.yml

ports:
  7777:
    class: Services\Reader1

Example 5:
And finally one bonus. This script is single thread. That means if one process takes too much time it will block to the rest of the processes. We can implemente threads, but I try to avoid them like the plague. I prefer to create a Silex app (behind a http server) and perform http requests to “emulate” threads in a simply way.

use G\Pxi\Pxinetd;
use G\Pxi\YamlFileLoader;
use Symfony\Component\Config\FileLocator;
use React\EventLoop\Factory as LoopFactory;

$loop = LoopFactory::create();
$service = new Pxinetd('0.0.0.0');

$loader = new YamlFileLoader($service, new FileLocator(__DIR__ ));
$loader->load('conf5.yml');

$service->on(8080, function ($data) {
    echo "$data";
});

$service->register($loop);
$loop->run();

conf5.yml

ports:
  9999:
    class: Services\Reader1
  9991:
    url: http://localhost:8899/onMessage/{data}
  9992:
    url: http://localhost:8899/simulateError/{data}

And now our Silex server running at 8899 port:

include __DIR__ . "/../../vendor/autoload.php";

use Silex\Application;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;

$app = new Application();

$app->get('/onMessage/{data}', function ($data) {
    return "OK" . "'{$data}'";
});

$app->get('/simulateError/{data}', function ($data) {
    throw new NotFoundHttpException();
});

$app->run();

And that’s all. What do you think? You can see the whole library in my github account.

Handling private states within AngularJS applications

One typical task when we work with AngularJs application is login, and private states. We can create different states in our application. Something like this:

    .config(function ($stateProvider, $urlRouterProvider) {
        $stateProvider
            .state('state1', {
                url: '/state1',
                templateUrl: templates/state1.html,
                controller: 'State1Controller'
            })
            .state('state2', {
                url: '/state2',
                templateUrl: templates/state2.html,
                controller: 'State2Controller'
            })
        $urlRouterProvider.otherwise('/state1');
    })

One way to create private states is using $stateChangeStart event. We can mark our private states with state parameters:

    .state('privateState1', {
            url: '/privateState1',
            templateUrl: templates/privateState1.html,
            controller: 'PrivateState1Controller',
            data: {
                isPublic: false
            }
        })

And then we can check out this parameters within $stateChangeStart event, doing one thing or another depending on token is present or not

    .run(function ($rootScope) {
        $rootScope.$on("$stateChangeStart", function (event, toState) {
            if (toState.data && toState.data.isPublic) {
                // do something here with localstorage and auth token
            }
        });
    })

This method works, but last days, reading one project of Aaron K Saunders at github, I just realised that there’s another method. We can listen to $stateChangeError. Let me show you how can we do it.

The idea is to use resolve in our private states. With resolve we can inject objects to our state’s controllers, for example user information. This method is triggered before call to the controller, so that’s a good place to check if token is present. If it isn’t, then we can raise an error. This error will trigger $stateChangeError event, and here we can redirect the user to login state.

It sounds good, but we need to write resolve parameter in every private states, and that’s bored. Especially when all states are private except login state. To by-pass this problem we can use abstract states. The idea is simple, we define one abstract state with “resolve” and then we create our private states under this abstract state.

Here we can see one example: login state isn’t private, but state1 and state2 are private, indeed.

    .config(function ($stateProvider, $urlRouterProvider) {
        .state('login', {
            url: '/login',
            templateUrl: 'templates/login.html',
            controller: 'LoginController'
        })
        .state('private', {
            url: "/private",
            abstract: true,
            template: '<ui-view/>',
            resolve: {
                user: function (UserService) {
                    return UserService.init();
                }
            }
        })
        .state('private.state1', {
            url: '/state1',
            templateUrl: 'templates/state1.html',
            controller: 'State1Controller'
        })
    
        .state('private.state2', {
            url: '/privateState2',
            templateUrl: 'templates/state2.html',
            controller: 'State2Controller'
        });
    
        $urlRouterProvider.otherwise('/private/privateState1');
    })

Our UserService is a AngularJS service. This service provides three methods: init (the method that raises an error if token isn’t present), login (to perform login and validate credentials), and logout (to remove token from localstorage and redirects to login state)

    .service('UserService', function ($q, $state) {
        var user = undefined;

        var UserService = {
            init: function () {
                var deferred = $q.defer();

                // do something here to get user from localstorage

                setTimeout(function () {
                    if (user) {
                        deferred.resolve(user);
                    } else {
                        deferred.reject({error: "noUser"});
                    }
                }, 100);

                return deferred.promise;
            },

            login: function (userName, password) {
                // validate user and password here
            },

            logout: function () {
                // remove token from localstorage
                user = undefined;
                $state.go('login', {});
            }
        };

        return UserService
    })

And finally the magic in $stateChangeError

    .run(function ($rootScope, $state) {
        $rootScope.$on('$stateChangeError',
            function (event, toState, toParams, fromState, fromParams, error) {
                if (error && error.error === "noUser") {
                    $state.go('login', {});
                }
            });
    })

And that’s all. IMHO this solution is cleaner than $stateChangeStart method. What do you think?

WARNING!
Before publishing this post I realize that this technique doesn’t work 100% correctly. Maybe is my implementation but I tried to use it with an ionic application and it doesn’t work with android. Something kinda weird. It works with web applications, it works with IOS, but it doesn’t work with Android. It looks like a bug (not sure about it). Blank screen instead of showing the template (but controller is loaded). We can see this anomalous situation using “ionic serve -l” (IOS ok and Android Not Ok)

To bypass this problem I tried a workaround. instead of using abstract states I create normal states, but to avoid to write again and again the resolve function to mark private states, I create a privateState provider

    .provider('privateState', function () {
        this.$get = function () {
            return {};
        };
    
        this.get = function(obj) {
            return angular.extend({
                resolve: {
                    user: function (UserService) {
                        return UserService.init();
                    }
                }
            }, obj);
        }
    })

Now I can easily create private states without writing ‘resolve’ function.

    .config(function ($stateProvider, $urlRouterProvider, privateStateProvider) {
        $urlRouterProvider.otherwise('/home');

        $stateProvider
            .state('home', privateStateProvider.get({
                url: '/home',
                templateUrl: 'templates/home.html',
                controller: 'HomeController'
            }))
        ;
    })

Building a AngularJS provider for hello.js library

This days I’ve been playing with hello.js. Hello is a A client-side Javascript SDK for authenticating with OAuth2 web services. It’s pretty straightforward to use and well explained at documentation. I want to use it within AngularJS projects. OK, I can include the library and use the global variable “hello”, but it isn’t cool. I want to create a reusable module and available with Bower. Let’s start.

Imagine one simple AngularJS application

(function () {
    angular.module('G', [])
        .config(function ($stateProvider, $urlRouterProvider) {
            $urlRouterProvider.otherwise("/");
            $stateProvider
                .state('login', {
                    url: "/",
                    templateUrl: "partials/home.html",
                    controller: "LoginController"
                })
                .state('home', {
                    url: "/login",
                    template: "partials/home.html"
                });
        })

        .controller('LoginController', function ($scope) {
            $scope.login = function () {
            };
        })
})();

Now we can include our references within our bower.json file

"dependencies": {
    "hello": "~1.4.1",
    "ng-hello": "*"
  }

and append those references to our index.html

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no, width=device-width">
    <title>G</title>

    <script type="text/javascript" src="assets/hello/dist/hello.all.js"></script>
    <script type="text/javascript" src="assets/ng-hello/dist/ng-hello.js"></script>
    <script src="js/app.js"></script>
</head>
<body ng-app="G">
<div ui-view></div>

</body>
</html>

Our ng-hello is just a service provider that wraps hello.js

(function (hello) {
    angular.module('ngHello', [])
        .provider('hello', function () {
            this.$get = function () {
                return hello;
            };

            this.init = function (services, options) {
                hello.init(services, options);
            };
        });
})(hello);

That’s means that we configure the service in config callback and in our run callback we can set up events

(function () {
    angular.module('G', ['ngHello'])
        .config(function ($stateProvider, $urlRouterProvider, helloProvider) {
            helloProvider.init({
                twitter: 'myTwitterToken'
            });

            $urlRouterProvider.otherwise("/");
            $stateProvider
                .state('login', {
                    url: "/",
                    templateUrl: "partials/home.html",
                    controller: "LoginController"
                })
                .state('home', {
                    url: "/login",
                    template: "partials/home.html"
                });
        })

        .run(function ($ionicPlatform, $log, hello) {
            hello.on("auth.login", function (r) {
                $log.log(r.authResponse);
            });
        });
})();

And finally we can perform a twitter login within our controller

(function () {
    angular.module('G')
        .controller('LoginController', function ($scope, hello) {
            $scope.login = function () {
                hello('twitter').login();
            };
        })
    ;
})();

And that’s all. You can see the whole library in my github account here

Handling AngularJs POST requests with a Silex Backend

This days I working a lot with AngularJs applications (who doesn’t?). Normally my backend is a Silex application. It’s pretty straightforward to build a REST api with Silex. But when we play with an AngularJs client we need to face with a a problem. POST requests “doesn’t” work. That’s not 100% true. They work, indeed, but they speak different languages.

Silex assumes our POST requests are encoded as application/x-www-form-urlencoded, but angular encodes POST requests as application/json. That’s not a problem. It isn’t mandatory to use one encoder or another.

For example

name: Gonzalo
surname: Ayuso

If we use application/x-www-form-urlencoded, it’s encoded to:
name=Gonzalo&surname=Ayuso

And if we use application/json, it’s encoded to:
{ "name" : "Gonzalo", "surname" : "Ayuso" }

It’s the same but it isn’t.

Imagine this Silex example.

use Silex\Application;
use Symfony\Component\HttpFoundation\Request;

$app = new Application();

$app->post("/post", function (Application $app, Request $request) {
    return $app->json([
        'status' => true,
        'name'   => $request->get('name')
    ]);
});

This example works with application/x-www-form-urlencoded but it doesn’t work with application/json. We cannot use Symfony\Component\HttpFoundation\Request parameter’s bag as usual. We can get the raw request body with:

$request->getContent();

Our content in a application/json encoded Request is a JSON, so we can use json_decode to access to those parameters.

If we read the Silex documentation we can see how to handle those requests

http://silex.sensiolabs.org/doc/cookbook/json_request_body.html

In this post we’re going to enclose this code within a service provider. OK, that’s not really a service provider (it doesn’t provide any service). It just change the request (when we get application/json) without copy and paste the same code within each project.

use Silex\Application;
use G\AngularPostRequestServiceProvider;
use Symfony\Component\HttpFoundation\Request;

$app = new Application();
$app->register(new AngularPostRequestServiceProvider());

$app->post("/post", function (Application $app, Request $request) {
    return $app->json([
        'status' => true,
        'name'   => $request->get('name')
    ]);
});

The service provider is very simple

namespace G;

use Silex\Application;
use Symfony\Component\HttpFoundation\Request;
use Pimple\ServiceProviderInterface;
use Pimple\Container;

class AngularPostRequestServiceProvider implements ServiceProviderInterface
{
    public function register(Container $app)
    {
        $app->before(function (Request $request) {
            if ($this->isRequestTransformable($request)) {
                $transformedRequest = $this->transformContent($request->getContent());
                $request->request->replace($transformedRequest);
            }
        });
    }

    public function boot(Application $app)
    {
    }

    private function transformContent($content)
    {
        return json_decode($content, true);
    }

    private function isRequestTransformable(Request $request)
    {
        return 0 === strpos($request->headers->get('Content-Type'), 'application/json');
    }
}

You can see the whole code in my github account and also in packagist

Using OpenUI5 table and Angularjs

Last days I’ve been playing with OpenUI5. OpenUI5 is a web toolkit that SAP people has released as an open source project. I’ve read several good reviews about this framework, and because of that I started to hack a little bit with it. OpenUI5 came with a very complete set of controls. In this small example I want to use the “table” control. It’s just a datagrid. This days I playing a lot with Angular.js so I wanted to use together OpenUI5’s table control and Angularjs.

I’m not quite sure if it’s a good decision to use together both frameworks. In fact we don’t need Angular.js to create web applications using OpenUI5. OpenUI5 uses internally jQuery, but I wanted to hack a little bit and create one Angularjs directive to enclose one OpenUI5 datagrid.

First of all, we create one index.html. It’s just a boilerplate with angular + ui-router + ui-bootstrap. We also start our OpenUi5 environment with de default theme and including the table library

<!doctype html>
<html ng-app="G">
<head>
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <link rel="stylesheet" href="assets/bootstrap/dist/css/bootstrap.min.css">

    <script src="assets/angular/angular.js"></script>
    <script src="assets/angular-ui-router/release/angular-ui-router.js"></script>
    <script src="assets/angular-bootstrap/ui-bootstrap-tpls.js"></script>

    <script id='sap-ui-bootstrap' type='text/javascript'
            src="https://openui5.hana.ondemand.com/resources/sap-ui-core.js"
            data-sap-ui-theme='sap_bluecrystal'
            data-sap-ui-libs='sap.ui.commons, sap.ui.table'></script>

    <script src="js/ngOpenUI5.js"></script>

    <script src="js/app.js"></script>
    <link rel="stylesheet" href="css/app.css">
</head>
<body class="ng-cloak">

<div class="container">

    <div class="starter-template">
        <div ui-view></div>
    </div>
</div>

<script src="assets/html5shiv/dist/html5shiv.js"></script>
<script src="assets/respond/dest/respond.src.js"></script>

</body>
</html>

Then we create a directive enclosing the OpenUI5 needed code within a Angular module

(function () {
    'use strict';

    angular.module('ng.openui5', [])
        .directive('openui5Table', function () {

            function renderColumns(columns, oTable) {
                for (var i = 0; i <= columns.length; i++) {
                    oTable.addColumn(new sap.ui.table.Column(columns[i]));
                }
            }

            var link = function (scope, element) {

                var oData = scope.model.data,
                    oTable = new sap.ui.table.Table(scope.model.conf),
                    oModel = new sap.ui.model.json.JSONModel();

                oModel.setData({modelData: oData});
                renderColumns(scope.model.columns, oTable);

                oTable.setModel(oModel);
                oTable.bindRows("/modelData");
                oTable.sort(oTable.getColumns()[0]);

                oTable.placeAt(element);

                scope.$watch('model.data', function (data) {
                    if (data) {
                        oModel.setData({modelData: data});
                        oModel.refresh();
                    }
                }, true);

            };

            return {
                restrict: 'E',
                scope: {model: '=ngModel'},
                link: link
            };
        });
}());

And now we can create a simple Angular.js using the ng.openui5 module. In this application we configure the table and fetch the data from an externar API server

(function () {
    'use strict';

    angular.module('G', ['ui.bootstrap', 'ui.router', 'ng.openui5'])

        .value('config', {
            apiUrl: '/api'
        })

        .config(function ($stateProvider, $urlRouterProvider) {
            $urlRouterProvider.otherwise("/");
            $stateProvider
                .state('home', {
                    url: "/",
                    templateUrl: "partials/home.html",
                    controller: 'HomeController'
                });
        })

        .controller('HomeController', function ($scope, $http, $log, config) {
            $scope.refresh = function () {
                $http.get(config.apiUrl + '/gridData').success(function (data) {
                    $scope.datagrid.data = data;
                });
            };

            $scope.datagrid = {
                conf: {
                    title: "Table example",
                    navigationMode: sap.ui.table.NavigationMode.Paginator
                },
                columns: [
                    {
                        label: new sap.ui.commons.Label({text: "Last Name"}),
                        template: new sap.ui.commons.TextView().bindProperty("text", "lastName"),
                        sortProperty: "lastName",
                        filterProperty: "lastName",
                        width: "200px"
                    }, {
                        label: new sap.ui.commons.Label({text: "First Name"}),
                        template: new sap.ui.commons.TextField().bindProperty("value", "name"),
                        sortProperty: "name",
                        filterProperty: "name",
                        width: "100px"
                    }, {
                        label: new sap.ui.commons.Label({text: "Checked"}),
                        template: new sap.ui.commons.CheckBox().bindProperty("checked", "checked"),
                        sortProperty: "checked",
                        filterProperty: "checked",
                        width: "75px",
                        hAlign: "Center"
                    }, {
                        label: new sap.ui.commons.Label({text: "Web Site"}),
                        template: new sap.ui.commons.Link().bindProperty("text", "linkText").bindProperty("href", "href"),
                        sortProperty: "linkText",
                        filterProperty: "linkText"
                    }, {
                        label: new sap.ui.commons.Label({text: "Image"}),
                        template: new sap.ui.commons.Image().bindProperty("src", "src"),
                        width: "75px",
                        hAlign: "Center"
                    }, {
                        label: new sap.ui.commons.Label({text: "Gender"}),
                        template: new sap.ui.commons.ComboBox({
                            items: [
                                new sap.ui.core.ListItem({text: "female"}),
                                new sap.ui.core.ListItem({text: "male"})
                            ]
                        }).bindProperty("value", "gender"),
                        sortProperty: "gender",
                        filterProperty: "gender"
                    }, {
                        label: new sap.ui.commons.Label({text: "Rating"}),
                        template: new sap.ui.commons.RatingIndicator().bindProperty("value", "rating"),
                        sortProperty: "rating",
                        filterProperty: "rating"
                    }

                ]
            };
        })
    ;
}());

The API server is a simple Silex server

<?php
include __DIR__ . '/../../vendor/autoload.php';
use Silex\Application;

$app = new Application();
$app->get("/", function (Application $app) {

$app->get('gridData', function (Application $app) {
    return $app->json([
        ['lastName' => uniqid(), 'name' => "Al", 'checked' => true, 'linkText' => "www.sap.com", 'href' => "http://www.sap.com", 'gender' => "male", 'rating' => 4, 'src' => "images/person1.gif"],
        ['lastName' => "Friese", 'name' => "Andy", 'checked' => true, 'linkText' => "www.sap.com", 'href' => "http://www.sap.com", 'gender' => "male", 'rating' => 2, 'src' => "images/person1.gif"],
        ['lastName' => "Mann", 'name' => "Anita", 'checked' => false, 'linkText' => "www.sap.com", 'href' => "http://www.sap.com", 'gender' => "female", 'rating' => 3, 'src' => "images/person1.gif"],
        ['lastName' => "Schutt", 'name' => "Doris", 'checked' => true, 'linkText' => "www.sap.com", 'href' => "http://www.sap.com", 'gender' => "female", 'rating' => 4, 'src' => "images/person1.gif"],
        ['lastName' => "Open", 'name' => "Doris", 'checked' => true, 'linkText' => "www.sap.com", 'href' => "http://www.sap.com", 'gender' => "female", 'rating' => 2, 'src' => "images/person1.gif"],
        ['lastName' => "Dewit", 'name' => "Kenya", 'checked' => false, 'linkText' => "www.sap.com", 'href' => "http://www.sap.com", 'gender' => "female", 'rating' => 3, 'src' => "images/person1.gif"],
        ['lastName' => "Zar", 'name' => "Lou", 'checked' => true, 'linkText' => "www.sap.com", 'href' => "http://www.sap.com", 'gender' => "male", 'rating' => 1, 'src' => "images/person1.gif"],
        ['lastName' => "Burr", 'name' => "Tim", 'checked' => true, 'linkText' => "www.sap.com", 'href' => "http://www.sap.com", 'gender' => "male", 'rating' => 2, 'src' => "images/person1.gif"],
        ['lastName' => "Hughes", 'name' => "Tish", 'checked' => true, 'linkText' => "www.sap.com", 'href' => "http://www.sap.com", 'gender' => "male", 'rating' => 5, 'src' => "images/person1.gif"],
        ['lastName' => "Lester", 'name' => "Mo", 'checked' => true, 'linkText' => "www.sap.com", 'href' => "http://www.sap.com", 'gender' => "male", 'rating' => 3, 'src' => "images/person1.gif"],
        ['lastName' => "Case", 'name' => "Justin", 'checked' => false, 'linkText' => "www.sap.com", 'href' => "http://www.sap.com", 'gender' => "male", 'rating' => 3, 'src' => "images/person1.gif"],
        ['lastName' => "Time", 'name' => "Justin", 'checked' => true, 'linkText' => "www.sap.com", 'href' => "http://www.sap.com", 'gender' => "male", 'rating' => 4, 'src' => "images/person1.gif"],
        ['lastName' => "Barr", 'name' => "Gaye", 'checked' => true, 'linkText' => "www.sap.com", 'href' => "http://www.sap.com", 'gender' => "male", 'rating' => 2, 'src' => "images/person1.gif"],
        ['lastName' => "Poole", 'name' => "Gene", 'checked' => true, 'linkText' => "www.sap.com", 'href' => "http://www.sap.com", 'gender' => "male", 'rating' => 1, 'src' => "images/person1.gif"],
        ['lastName' => "Ander", 'name' => "Corey", 'checked' => false, 'linkText' => "www.sap.com", 'href' => "http://www.sap.com", 'gender' => "male", 'rating' => 5, 'src' => "images/person1.gif"],
        ['lastName' => "Early", 'name' => "Brighton", 'checked' => true, 'linkText' => "www.sap.com", 'href' => "http://www.sap.com", 'gender' => "male", 'rating' => 3, 'src' => "images/person1.gif"],
        ['lastName' => "Noring", 'name' => "Constance", 'checked' => true, 'linkText' => "www.sap.com", 'href' => "http://www.sap.com", 'gender' => "female", 'rating' => 4, 'src' => "images/person1.gif"],
        ['lastName' => "Haas", 'name' => "Jack", 'checked' => true, 'linkText' => "www.sap.com", 'href' => "http://www.sap.com", 'gender' => "male", 'rating' => 2, 'src' => "images/person1.gif"],
        ['lastName' => "Tress", 'name' => "Matt", 'checked' => true, 'linkText' => "www.sap.com", 'href' => "http://www.sap.com", 'gender' => "male", 'rating' => 4, 'src' => "images/person1.gif"],
        ['lastName' => "Turner", 'name' => "Paige", 'checked' => true, 'linkText' => "www.sap.com", 'href' => "http://www.sap.com", 'gender' => "female", 'rating' => 3, 'src' => "images/person1.gif"]
    ]);
});
$app->run();

And basically that’s all. You can see the whole project within my github account.

working example

working example

Why did this year has passed so swiftly? My 2014 Retrospective.

Today an original post. Maybe I’m the only one doing this, I know. 2014 is close to finish and I want to review how it went the year. Let’s start.

The bad parts:

  • My book about SOLID principles (in Spanish) isn’t released yet. It’s almost finished. It only needs a few reviews, but because one thing or another it looks like it isn’t be released this year. Lesson learned: Those kind of side projects must have a release date. If they haven’t, another side projects can grab our attention and they can be frozen.
  • No new languages learned this year. There was a good chance with Swift. A new language, but it didn’t attract my attention. Erlang books are still in my desk and also my aim to improve my Java skills didn’t success. I found nothing where apply my Java learning.

The good parts:

  • Finally I can say JavaScript is a first class language within my personal software stack. Various projects with JS this year and I feel very comfortable writing JavaScript code. That’s also the year of Angular.js (for me and probably a lot of people).
  • This year has been the year of mobile development for me. I’ve been involved with several projects using Cordova/Phonegap framework. I the beginning to install Cordova environment, compile, deploy the application into the device was something “heroic” but now it turns into trivial operations. I still remember my beginning with jQuery Mobile. Horrible. Then I started using Angular.js and Topcoat. Much better, but still problems when switching between Android and IOs. Finally I re-discover Ionic framework. Incredible project. Hybrid applications with angular.js with very complete toolkit. This year has been crowed by push notifications, camera plugins, barcode scanners, token based authorisations, Websockets and things like that. Now hybrid applications with Phonegap/Cordova live in my comfort zone along with Silex, Angular, PHP… (that’s means I need to find other places outside it)
  • The last part of the year I’ve been working a lot with automation tools: Bower and Grunt mainly. I also started to work with JavaScript testing with Karma and Jasmine
  • This year I’ve been a proud speaker at DeSymfony Day in Barcelona. On incredible weekend. Meeting with colleagues, speaker dinner, great conversations, and tourism in a great city. Definitely the most beautiful room for a conference that I ever been
  • Katayunos The coding dojo where we play with TDD and Pair Programming is still alive. Maybe not as continuous as I’d like, but we still meet together 20-25 people one Saturday morning to improve our programming skill, from time to time
  • My personal blog is still alive too. It’s close to be 5 years old (OK, technically speaking 6, but first year it wasn’t a serious one). More than 20k views per month and sometimes close to 30k (Hey, thank you for reading!)

And that’s all. It was a good year. Hopefully it will be worse than 2015 :)

See you!

Follow

Get every new post delivered to your Inbox.

Join 1,085 other followers