Developing web applications today is quite easy, you will find always a framework/library that does most of the work for you.

Some month ago, I heard about Apigility and lately I have been playing a little with it. So what’s Apigility?: Apigility is an API Builder, designed to simplify creating and maintaining useful, easy to consume, and well structured APIs.

For this tutorial I’m going to show you how to create a Todo-Api, using Apigility on the backend and Ember.js on the front end.

Installing Apigility

Installing Apigility is easy, you download it from www.apigility.org or installing using composer by running this cmd:

php composer.phar create-project –sdev zfcampus/zf-apigility-skeleton path/where/to/install

After that it is just to fire it up.

Remember you have to disable the opcode on Apache/PHP or the Apigility admin dashboard won’t work, when developing the API it is best to use the build in web server in PHP (>5.4.8+).


To fire it up, first open you cmd and go to the installation folder and run following cmd to put the application into development mode:

php public/index.php development enable

Then run the server:

php -S 0.0.0.0:8080 -t public public/index.php

Change the port if you are also using Apache or other http server on that machine.

Open the browser and go to your url, in my case im using apigility.empirio.local:8082, and if everything is installed correctly you will be transferred to zf-apigility-welcome module, click on «Get started» to continue to the admin panel.





Create a database adapter


On the left side you find a menu on the admin dashboard, click on «Database Adapters» then «Create New DB Adapter«, fill the field according to you setup, in my case Im going to be using pdo_mysql and name the adapter «Demo\Apigility\TodoApi».



Create a new API


After creating the db adapter now its time to continue with our api, click on the button «Create new API» on the top right corner and fill out the name of you api, in my case we are going to name it «TodoApi».

And that’s all, you have now created your first API.



Create a REST Service


The API need some configuration, otherwise its just a dumb and empty API, so go ahead and create a new REST service.

Click on your API on the top, and then «REST Services» and then «Create new REST Service», here you have 2 options:

  • Code-connected – Custom rest services, easier to manipulate the data, and/or for third party APIs
  • DB-connected – You get a lot functionality for free, more difficult to add custom manipulation.

We are going for the DB-connected, so select that and under «DB Adapter Name» select the db adapter we created earlier and as well fill the table name on your database, we are going to create the table after this.



Create the table


Create you database table, in our case we are going to name it «todos» and have following attributes:

todos_id (primary key),
name (string),
is_completed (bool)

Testing


Before starting on the front-end, we can test our api using a rest client, im using Advanced Rest Client on Google Chrome.

Open the REST client and send a GET request to the server/api (http://apigility.empirio.local:8082/todos), the server will return something like this:

{
    _links: {
        self: {
            href: http://apigility.empirio.local:8082/todos
        }
    }
    _embedded: {
        todos: [0]
    }
}

As you can see its just an JSON string.

Now try to do a POST request, set the header content-type to application/json and under payload put this JSON string:

{"name":"My first task", "is_completed":false}

In return, you will get a 201 created response from the server with the created task as JSON:

{
    todos_id: "1"
    name: "My first task"
    is_completed: "0"
    _links: {
        self: {
            href: "http://apigility.empirio.local:8082/todos/1"
        }
    }
}

And so, you can try to get a single task, delete one and play more if you want.

Ember.js


In you Apigility installation, you have a module named «Application», all it does is to redirect you to the zf-apigility-welcome module, so I’m going to use this for my front-end.

But we have to do some changes on the ApplicationController, we have to remove the code that redirects you to zf-apigility-welcome module, we do this by removing this line of code from the index action:

return $this->redirect()->toRoute('zf-apigility-welcome');

And then in my case im going to use asset manager to load my assets and static files. So create a folder named «asset» under «Application»-module then open the confige-file and put this on the top:

// ...
'asset_manager' => array(
    'resolver_configs' => array(
        'paths' => array(
            __DIR__ . '/../asset',
        ),
    ),
),
// ...

Now you can put all the javascript and css files under the folder we just created.

Downloading Ember.js


I’m going to use canary build of Ember.js and Ember Data, you can download them here: http://emberjs.com/builds/#/canary. I’m also going to use twitter bootstrap for styling.

Here it is how my folder structure looks like it:

// ...
Module/Application
    asset
        css
            boostrap.css
            boostrap.min.css
            app.css
        font
            // boostrap fonts
        javascript
            vendor
                boostrap.js
                ember.js
                ember-data.js
                handlebars-1.1.2.js
                jquery-1.10.2.js
            app.js
    view
        layout
            layout.html
// ...

And here is my layout:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Ember.js & Apigility TodoApi</title>
    <link rel="stylesheet" href="css/bootstrap.css">
    <link rel="stylesheet" href="css/app.css">
</head>
<body>
    <script type="text/x-handlebars" data-template-name="application">
        <div class="container">
            <h1>Ember.js & Apigility TodoApi</h1>

            {{outlet}}
        </div>
    </script>

    <script src="javascript/vendor/jquery-1.10.2.js"></script>
    <script src="javascript/vendor/handlebars-v1.3.0.js"></script>
    <script src="javascript/vendor/ember.js"></script>
    <script src="javascript/vendor/ember-data.js"></script>
    <script src="javascript/vendor/bootstrap.js"></script>
    <script src="javascript/app.js"></script>
</body>
</html>

If you open this on your browser you should just see the «Ember.js & Apigility TodoApi» if everything is okey.

Now open the app.js-file remove everything and just leave the definition of the application:

App = Ember.Application.create();

Then we need to define our resource, here its how we do that:

App.Router.map(function(){
    this.resource('todos', {path : '/'});
});

As you can see we just defined one resrouce named «todos».

Then we define an adapter and a store:

// ...
App.Adapter = DS.RESTAdapter.extend({
    namespace: ""
});

App.Store = DS.Store.extend({
    adapter: App.Adapter
});

The adapter extends DS.REST adapter and the store extends DS,Store and uses the adapter.

We then continue and create the model, called «todo», with a name attribute as string and an isCompleted attribute as Boolean.

// ...
App.Todo = DS.Model.extend({
    name: DS.attr('string'),
    isCompleted: DS.attr('boolean'),
});

So far so good but still nothing happens, its time to add the handlebar template for our «todos» resource that we defined earlier and then create a form with an input field.

// ...
<script type="text/x-handlebars" data-template-name="todos">
    <form {{action "add" on="submit"}}>
        {{view Ember.TextField valueBinding="todoName" placeholder="Add a new task" class="form-control"}}<br />
    </form>
</script>
// ...

The form has an action that will be trigged on submit, and the input field has an attribute bound to the controller.

We need now to create the Todos-route and define the model we are going to use, then add the action the will handle form submission.

// ...
App.TodosRoute = Ember.Route.extend({
    model : function(){
        return this.store.find('todo');
    },
    actions : {
        add : function(){
            var self = this;
            var todo = this.store.createRecord('todo');

            todo.set('name', this.get('controller').get('todoName'));
            todo.set('isCompleted', false);

            todo.save().then(function(){
                self.get('controller').set('todoName', '');
                console.log('Todo added ...');
            });
        }
    }
});

The first part is easy to understand, we define a model function that returns the «todo» model from the store.

On the add-function we first create a todo record from the store, set the name from our input field and then save it, if everything went well on the server we reset the value of the name field.

If the server we send the request supports the data on the format Ember.js sends it, you should be able to create a task now, unfortunately Apigility doesn’t do that and we need to do some extra changes.

First we need to create our own serializer that extends «ActiveModelSerializer» and define the extract function like this:

// ...
App.ApplicationSerializer = DS.ActiveModelSerializer.extend({
    primaryKey: 'todos_id',

    extract: function(store, type, payload, id, requestType) {
        this.extractMeta(store, type, payload);

        if(payload._embedded)
            payload = payload._embedded;

        if(requestType == 'updateRecord' || requestType == 'createRecord'){
            var data = {};
            data[type.typeKey] = payload;
            payload = data;
        }
        var specificExtract = "extract" + requestType.charAt(0).toUpperCase() + requestType.substr(1);
        return this[specificExtract](store, type, payload, id, requestType);
    }
});

All I did was copying the same function from ActiveModelSerializer and do some changes, we check if the payload has the key _embeded, if so use that as the payload. Then we check if the reqestType is updateRecord or createRecord, since the Apigility returns just the data we need to manipulate the payload and send it to in the right format to Ember.js.

Also the default primary key in Ember.js is «id» so if you have something else you can define it here.

The serializer is used when we need to change the default behavior when we get a response from the server.



Now if we reload the page and write something on the input field and hit enter we will get an 500 Internal Server Error, and the reason for this is that Apigility expects following format on the payload:

{"name":"Testing","is_completed":false}

But Ember.js by default sends following payload to the server:

{"todo":{"name":"Testing","is_completed":false}}

And we have to do something with this, so we manipulate the payload by extending (copying from the «RESTAdapter) the createRecord-function change the behavior on the adapter like this:

// ...
createRecord: function(store, type, record) {
    var data = {};
    var serializer = store.serializerFor(type.typeKey);
    serializer.serializeIntoHash(data, type, record, { includeId: true });
    return this.ajax(this.buildURL(type.typeKey), "POST", { data: data[type.typeKey] });
},
// ...

It’s a minimal change on the last line, from returning just the data array to data[type.typeKey].

If we now refreshes the page again and try to create a new task it will work now.

Now its time to display the tasks on our page, we loop trough the tasks on the controller from the template like this:

// ...
<ul class="list-unstyled">
    {{#each}}
        <li {{bind-attr class="isCompleted:text-muted isCompleted:done"}}>
            <button {{action delete this}} class="btn btn-danger btn-danger-round btn-xs">x</button>
            {{view Ember.Checkbox checkedBinding="isCompleted"}} {{name}}
        </li>
    {{/each}}
</ul>

The route provide the model to the controller and we just loop trough that, and for each tasks we create a delete button with a delete action and a checkbox bound to the isCompleted attribute.

Then we need to create an observer to observe the checkbox when a task is marked as complete, and here I think Ember.js really shines.

By just creating a function on our model and attach the observer to it like this:

// ...
changeObserver: function(){
    if(this.get('isDirty') == true && !this.get('isNew')){
        this.save().then(function(){
            console.log('Model changed and saved!');
        });
    }
}.observes('isCompleted')

If the model is changed but it is not a new model then we save the model. The Ember.js observer is attached to the changeObserver-function and passed the bounded attribute «isCompleted», so every time isCompleted is changed this function will be triggered.

Yet again the default payload from Ember.js is not supported in Apigility so we need to extend a new function named updateRecord on our adapter, like this:

// ...
updateRecord: function(store, type, record) {
    var data = {};
    var serializer = store.serializerFor(type.typeKey);
    serializer.serializeIntoHash(data, type, record);
    var id = Ember.get(record, 'id');
    return this.ajax(this.buildURL(type.typeKey, id), "PUT", { data: data[type.typeKey] });
}

Same thing here as in createRecord, on last line just changed data to data[type.typeKey].

Now you can refresh the page and try clicking on one checkbox and it works.

Now at very end we need to add support for deleting a task, you can do this in different ways, one way is by creating a function on TodosRoute under actions like this:

// ...
delete : function(model){
    if (confirm("Are you sure you want to delete the selected record ? Click OK to continue.")) {
        //deletes record from store
        model.deleteRecord();

        //persist change
        model.save().then(function(){
            console.log('Todo deleted');
        });
    }
}

First we delete the record from the store and then we persist the changes to the server.

Demo and source code


You can find a demo of this todo app here, and you will find the source code on GitHub.

And that’s all for now, on my next tutorial I will add the functionality for pagination. Hope you learn a thing or two and I appreciate your feedback.

Follow me on twitter @oxodesign