Get Even More Visitors To Your Blog, Upgrade To A Business Listing >>

PHP Developer's Revelation Or How To Write Simple Web-Application In Javascript Only

Original:http://m1x0n.blogspot.com/2014/03/php-developers-revelation-or-how-to.html

To write this article I needed a lot of inspiration, so I want to thank everyone I owe my inspiration.  I was inspired by @boo1ean who showed me some Backbone's magic and due to who I started perceiving  js from another perspective. Also thanks to @msemenistyi who proposed to look deeper in Node.js. And also thanks to Okean Elzy for the great music atmosphere that accompanied the whole process.

So what is the audience of this article? I think it'll be helpful for people who started learning server side programming using Node.js like me :), for people who got acquainted at least a bit with Node and NPM and for others who partially use js. I'm not a JS-guru but the challenge is accepted.

Let's start. Make sure you have the latest node and nmp installed. We're going to write simple as door TODO application using next js technologies:

· Express.js - web application framework for Node. (It's like Zend for PHP).

·  Sequelize - ORM for Node environment (Well known analogs for PHP are Eloquent and Doctrine).

·  PostgreSQL - as storage engine.

To be honest we will modify for our needs turorial from Sequelize website. So what is the architecture of our app like? We will delegate it to manipulate TODO instances via CRUD. That's why we need to create database in Postgres and create there a table in order to store described instances. If you haven't yet installed postgresql server,please, install it. There is an example below:

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

-- Database: todo

-- DROP DATABASE todo;

CREATEDATABASEtodo

WITHOWNER = postgres

ENCODING = 'UTF8'

TABLESPACE = pg_default

LC_COLLATE = 'en_US.UTF-8'

LC_CTYPE = 'en_US.UTF-8'

CONNECTIONLIMIT = -1;

GRANTCONNECT, TEMPORARYONDATABASEtodo TOpublic;

GRANTALLONDATABASEtodo TOpostgres;

GRANTALLONDATABASEtodo TOmichael;

--

--

-- Please, use your granted user instead of michael

--

--

-- Table: notes

-- DROP TABLE notes;

CREATETABLEnotes

(

id serial NOTNULL,

text charactervarying(255),

CONSTRAINTnotes_pkey PRIMARYKEY(id)

)

WITH(

OIDS=FALSE

);

ALTERTABLEnotes

OWNER TOmichael;

If you are new to PostgreSQL you can take a look at this tutorial. Here serial is analog to MySQL's autoincrement and varying char is the same as VARCHAR(?).
And some forewords about UI. Our app will have next UI:

Here we will submit new items from sibling input via 'Add' button and connected input to it. When new item appears it appends to list as read-only input which will be activated by double clicking on them.
Now we can switch to writing an application. First of all let's create app folder and install express.js via npm. Then ask express to create skeleton app structure which we're going to modify later. For doing this you need to run following commands in your command line:

?

1

2

3

4

5

6

7

8

9

mkdirexpress-sequelize-postgres-todo

cdexpress-sequelize-postgres-todo

npm installexpress

# This will create skeleton app in specified earlier folder

node_modules/.bin/express. -f

npm install

mkdirdb models

After that we will receive following project structure:

You're able to run Express skeleton app using node app.js and see result by entering localhost: 3000 in your browser's address bar.         
In order to install PostgresSQL db connector and Sequelize, please, run following commands:

?

1

2

3

4

5

npm install--save pg

npm install--save sequelize

npm install--save sequelize-postgres

# Install lodash for some cool stuff

npm install--save lodash

--save option instructs NPM to include the package inside of the dependencies section of your package.json automatically.

Let's write main app file app.js. Here we require all necessary dependency modules, create express app and define server variables like port, template render engine etc.:

?

1

2

3

4

5

6

7

8

9

10

11

12

13

varexpress = require('express'),

routes  = require('./routes'),

todo    = require('./routes/todo'),

http    = require('http'),

path    = require('path'),

db      = require('./models');

varapp = express();

// all environments

app.set('port', process.env.PORT || 3000);

app.set('views', path.join(__dirname, 'views'));

app.set('view engine', 'jade');

Then map routes to controller’s actions. Express has special methods for handling HTTP requests. The similar routing way you can find in PHP framework Laravel.

?

1

2

app.get('/todos', todo.list);

app.post('/todos/add', todo.add);

And finally make db connection and create server.

?

1

2

3

4

5

6

7

8

9

10

11

12

13

/**

* Put sync({force: true}) if you want to truncate data in tables

* and will use migrations to up your database

*/

db.sequelize.sync().complete(function(error) {

if(error) {

throwerror;

} else{

http.createServer(app).listen(app.get('port'), function() {

console.log('Express server listening on port '+ app.get('port'));

});

}

});

Next step: we create models loader models/index.js. The purpose of this file is to collect all models modules in db variable in order to provide access to them in other modules (e.g controllers aka routes) of our application. Actually I forked this module from tutorial and left it "as is". But there is one important thing like db connection. It's not so complicated. We just provide db credentials and specify dialect. FYI Sequelize is able to work against MySQL, SQlite and MariaDB too. Below is an example of db connection:

?

1

2

3

4

5

...

varsequelize = newSequelize('todo', 'michael', 'root', {

dialect: 'postgres'

});

...

Our next point is Todo model definition via Sequalize interface.

?

1

2

3

4

5

6

7

8

9

10

11

module.exports = function(sequelize, DataTypes) {

varTodo = sequelize.define('Todo', {

text: DataTypes.STRING

}, {

paranoid:   false,

timestamps: false,

tableName: 'notes'

});

returnTodo;

};

Due to Sequelize ability of creation db schema via migrations on first app run let's disable some options like paranoid mode which allows creating additional timestamp fields (createdAt, updatedAt). Also we are able to specify actual table name. As for DataTypes we will use only STRING in our simple example. The other types you can find at Sequalize documentation page. Now we are closer to controllers (routes) checkpoint. In our app we will have two of them:

· index.js - which handles our index page. On this page we will render main layout and all todo items;

· todo.js - which methods reflect our actions on todo items.

Controller index.js will helps us retrieve all TODO items and render them via Node template engine - Jade

Sequelize provides different kinds of finders like find, findAll, findOrCreate.

?

1

2

3

4

5

6

7

8

9

vardb = require("../models");

exports.index = function(req, res) {

db.Todo.findAll().success(function(todos) {

res.render('index', {

todos: todos

});

});

};

Template index.jade is the main content block for layout.jade. In index.jade we're going to render TODO item list, new TODO input and action button. In layout.jade we define our common html page markup, add stylesheets, specify additional js libraries like jQuery and do other necessary things.  On application running via node app.js all jade templates will be compiled in html ones. Examples of layout.jade and index.jade are placed below:

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

// layout.jade

doctype html

html

head

title= title

link(rel='stylesheet', href='/stylesheets/style.css')

script(type='text/javascript', src='/javascripts/jquery-1.11.0.min.js')

script(type='text/javascript', src='/javascripts/todo-client.js')

body

block content

hr

div(class="created-by") Created by m1x0n

// index.jade

extends layout

block content

h2 TODO list:

div

span

input(name='todo'id="todo-input"value=''placeholder="Add new TODO item here")

span

a(href="javascript:void(0)"class="add-btn") Add

div(class="todo-list")

each todo intodos

div

span

input(name='todo'id="todo-item-#{todo.id}"class="todo-item-input"value='#{todo.text}'readonly="true")

span

a(href="javascript:void(0)"class="edit-btn"data-id="#{todo.id}") Update

span

a(href="javascript:void(0)"class="delete-btn"data-id="#{todo.id}") Delete

Usually passed template's variables have simple access to them and jade is not an exception. Unfortunately it's not a jade tutorial. For more information, please, read Jade's reference.
Let's see todo.js controller.

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

vardb = require("../models");

exports.list = function(req, res) {

db.Todo.findAll().success(function(todos) {

res.json(todos);

});

};

exports.add = function(req, res) {

db.Todo.create({text: req.body.text}).success(function(todo) {

res.render('todo-item', {item: todo});

});

};

exports.edit = function(req, res) {

db.Todo.find({where: {'id': req.body.id}, limit: 1}).success(function(todo) {

todo.updateAttributes({text: req.body.text}).success(function(updated) {

res.json(updated);

});

});

};

exports.remove = function(req, res) {

db.Todo.find({where: {'id': req.body.id}, limit: 1}).success(function(todo) {

todo.destroy().success(function(destroyed) {

res.json({id: req.body.id, deleted: true});

});

});

};

The main point here is request handlers. For each of them we do some manipulations with database. In order to create new install of Todo we call Sequelize's create method and specified attribute text which we can retrieve from requests body. When successful we will render html row (partial view) with new item. This row located in separate template views/todo-item.jade and looks like following:

?

1

2

3

4

5

6

7

div

span

input(name='todo'id="todo-item-#{item.id}"class="todo-item-input"value='#{item.text}'readonly="true")

span

a(href="javascript:void(0)"class="edit-btn"data-id="#{item.id}") Update

span

a(href="javascript:void(0)"class="delete-btn"data-id="#{item.id}") Delete

To edit model properties Sequelize has updateAttributes method and in order to delete there is a drop one. As a result of all, edit and remove todo controller's methods we will return json data.
Now it's time to write client side application. In this part of this article we're going to create Todo client module which helps to connect our UI with server's logic. This file lives in /public/javascript/todo-client.js.
Our TODO application will be single page. That's why we will perform all of our requests via AJAX. Firstly let's map our requests with server's urls:

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

varendpoint = "/todos/";

varactions = {

add:    {

type:     "POST",

url:      endpoint + "add",

dataType: "html"

},

edit:   {

type:     "PUT",

url:      endpoint + "edit",

dataType: "json"

},

remove: {

type:     "DELETE",

url:      endpoint + "delete",

dataType: "json"

}

};

Secondly bind our UI events to special handlers which will send XmlHttpRequest to the server.

?

1

2

3

4

5

6

7

8

9

10

varbindEvents = function() {

$(ui.addButton).on('click', addItem);

bindItemEvents();

};

varbindItemEvents = function() {

$(ui.editButton).on('click', editItem);

$(ui.deleteButton).on('click', deleteItem);

$(ui.todoItemInput).on('dblclick', enableItemEdit);

};

Let's describe addItem.

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

varui = {

todoInput:     "#todo-input",

todoItem:      "#todo-item-",

todoItemInput: ".todo-item-input",

todoItemBlock: ".todo-item-block",

todoList:      ".todo-list",

addButton:     ".add-btn",

editButton:    ".edit-btn",

deleteButton:  ".delete-btn"

};

varaddItem = function() {

if(dummyValidate(ui.todoInput)) {

$.ajax({

url:      actions.add.url,

type:     actions.add.type,

dataType: actions.add.dataType,

data:     {

text: $(ui.todoInput).val()

},

success:  function(response) {

renderItem(response);

},

error:    function(xhr, status, error) {

console.log(error);

}

});

}

};

varrenderItem = function(item) {

$(ui.todoList).append($(item));

clearTodoInput();

bindItemEvents();

};

varclearTodoInput = function() {

$(ui.todoInput).val('');

};

Here in UI variable we collected all necessary selectors. When user clicks on 'Add' button addItem() will be called. In this method we run dummy validation which checks if user entered something in input. If so we send this data to server where todo.add() will be applied and will return item template on success. We're going to render this template via renderItem(). 
Our TodoClient starts when document is ready and bind its events. For newly added items we need to rebind them. Other handlers work at the same way.  I've added some custom stylesheets to the latest revision. And now it looks like this:

 

Last version of this project you can find on Github.

In conclusion as a PHP-developer I'd like to say that building apps in js is very fun and interesting. It was great experience and I'm happy to share theis knowledge with others. Thanks to everyon who read this article to the end. I hope you enjoyed it.

 

P.S. There are some links which I think will be useful:

1. Stoyan Stefanov. JavaScript for PHP Developers

2. Interactive Node.js lessons with nodeschool.io

3. Addy Osmani. Learning JavaScript Design Patterns



This post first appeared on Blog - Offshore Custom Software Development Compan, please read the originial post: here

Share the post

PHP Developer's Revelation Or How To Write Simple Web-Application In Javascript Only

×

Subscribe to Blog - Offshore Custom Software Development Compan

Get updates delivered right to your inbox!

Thank you for your subscription

×