KodeInfo | Learning resources for web and mobile development

Realtime App Using Laravel + NodeJs + AngularJS + Redis

September 21st, 2014 12:31:16 by Imran Iqbal Comments(0) - Views(78668)

This is insane how we can combine Laravel,NodeJS,AngularJS and Redis to create an amazing product . For this tutorial i hope you know basics of angularjs , laravel and dont worry about redis , nodejs i will cover that in detail . Below are our steps for User Management Panel .

  • Laravel Installation and Configuration
  • Setting up Redis Server and NodeJS
  • Wrapping up with AngularJS
  • Final Step
 
 

 

What are we creating ?

Laravel Installation and Configuration

Install a fresh copy of laravel and configure your database . I am using mysql as my database and redis to publish/subscribe events .
Now lets create a listener which listens to our update user event . Create a new folder inside app and name it KodeInfo , create one more folder inside app/KodeInfo and name it Handlers 
Add this to autoload classmap as below and run php artisan dump-autoload

"app/KodeInfo",
"app/KodeInfo/Handlers"
php artisan dump-autoload

Now create a new class inside our app/KodeInfo and name it UserUpdatedEventHandler 

<?php

namespace KodeInfo\Handlers;

use Redis;
use Response;

class UserUpdatedEventHandler {

    CONST EVENT = 'users.update';
    CONST CHANNEL = 'users.update';

    public function handle($data)
    {
        $redis = Redis::connection();
        $redis->publish(self::CHANNEL, $data);

    }

We will create a listeners.php in app folder and will listen for event users.update . whenever users.update it will execute handle method of UserUpdatedEventHandler class which will publish our data to users.update channel . So that means we have to listen for our event users.update in  our angularjs code and act accordingly .

listeners.php

<?php

Event::listen(\KodeInfo\Handlers\UserUpdatedEventHandler::EVENT, '\KodeInfo\Handlers\UserUpdatedEventHandler');

Our UsersController will handle request from angularjs
 

UsersController.php

<?php

class UsersController extends BaseController {

    public function index(){
        return View::make('index',['rows'=>User::all(),'window'=>new \KodeInfo\JSHelper]);
    }

    public function addUser(){
        $user=new User();
        $user->name = Input::get('name');
        $user->email = Input::get('email');
        $user->save();
        Event::fire(\KodeInfo\Handlers\UserUpdatedEventHandler::EVENT, array(User::all()));
    }

    public function updateUser(){
        $user=User::find(Input::get('id'));
        $user->name = Input::get('name');
        $user->email = Input::get('email');
        $user->save();
        Event::fire(\KodeInfo\Handlers\UserUpdatedEventHandler::EVENT, array(User::all()));
    }

    public function deleteUser($user_id){
        User::find($user_id)->delete();
        Event::fire(\KodeInfo\Handlers\UserUpdatedEventHandler::EVENT, array(User::all()));
    }

    public function all(){
        return Response::json(User::all());
    }
} 

We are using same view files which we have used in our previous tutorial Laravel Admin Panel . So we are using Adminlte with following directory structure

I wont be putting all views here so please have a look at views on github . We will get back to our app and work on nodejs,redis,angularjs . Below is our index.blade.php

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Laravel Realtime App</title>
    <meta content='width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no' name='viewport'>

    {{HTML::style("/css/bootstrap.min.css")}}
    {{HTML::style("/css/font-awesome.min.css")}}
    {{HTML::style("/css/ionicons.min.css")}}
    {{HTML::style("/css/AdminLTE.css")}}

    <!--[if lt IE 9]>
    {{HTML::script("https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js")}}
    {{HTML::style("https://oss.maxcdn.com/libs/respond.js/1.3.0/respond.min.js")}}
    <![endif]-->

    <script>
        window.extras = window.extras || {{$window}};
    </script>

    @yield('styles')
    <base href="/"/>

</head>

<body class="skin-blue" ng-app="DemoApp">
<!-- header logo: style can be found in header.less -->

@include('layouts.header')

<div class="wrapper row-offcanvas row-offcanvas-left">
    <!-- Left side column. contains the logo and sidebar -->

    @include('layouts.navigation')

    <!-- Right side column. Contains the navbar and content of the page -->
    <aside class="right-side">

        <div class="col-md-12">
            <div ng-view></div>
        </div>


    </aside>
    <!-- /.right-side -->
</div>
<!-- ./wrapper -->

<!--Template Plugins-->
{{HTML::script("/js/jquery.2.0.3.js")}}
{{HTML::script("/js/jquery-ui-1.10.3.min.js")}}
{{HTML::script("/js/bootstrap.min.js")}}
{{HTML::script("js/plugins/iCheck/icheck.min.js")}}
{{HTML::script("js/AdminLTE/app.js")}}

<!--AngularJS-->
{{HTML::script("https://code.angularjs.org/1.2.13/angular.js")}}
{{HTML::script("//ajax.googleapis.com/ajax/libs/angularjs/1.2.9/angular-route.js")}}
{{HTML::script("//ajax.googleapis.com/ajax/libs/angularjs/1.0.3/angular-sanitize.js")}}
{{HTML::script("//cdnjs.cloudflare.com/ajax/libs/angular-ui-router/0.2.8/angular-ui-router.min.js")}}

<!--Socket.io-->
{{HTML::script("http://localhost:3000/socket.io/socket.io.js")}}
{{HTML::script("/angular/modules/angular-socket-io/socket.js")}}
{{HTML::script("/angular/app.js")}}

<!--Controllers and Services-->
{{HTML::script("/angular/services/users-service.js")}}

@yield('scripts')


</body>
</html>

In the above code we are getting some variables to angularjs so that it will be helpful . We can send csrf_token with each post request to be safe from csrf attach and so on . We are using KodeInfo/JSHelper to do so

<script>
        window.extras = window.extras || {{$window}};
</script>

We have defined a ng-view in which our list will load and have some angular scripts . Here the main part in below which we will get back soon after covering nodejs . I have seen many people get strucked in this step of how to load socket.io.js

{{HTML::script("http://localhost:3000/socket.io/socket.io.js")}}

We have also using https://github.com/btford/angular-socket-io library to easily use socket.io inside angularjs .

Setting up Redis Server and NodeJS

I was using windows at the time of writing this post so i have downloaded win64 executable from this link . It is also included with the source code .
Now download and install nodejs from nodejs website and windows users make sure you have set the path for nodejs installation folder [mine was C:\Program Files\nodejs]  . Now start command prompt/terminal and navigate to your laravel project/public directory , create a new folder nodejs and navigate to nodejs from cmd/terminal , now lets install our dependencies using below command .

npm install socket.io express redis

Now create a new file inside public/nodejs and name it server.js

var express =   require('express'),
    http =      require('http'),
    server =    http.createServer(app);

var app = express();

const redis =   require('redis');
const io =      require('socket.io');
const client =  redis.createClient();

server.listen(3000, 'localhost');
console.log("Listening.....");

io.listen(server).on('connection', function(client) {
    const redisClient = redis.createClient();

    redisClient.subscribe('users.update');

    console.log("Redis server running.....");

    redisClient.on("message", function(channel, message) {
        console.log(message);
        client.emit(channel, message);
    });

    client.on('disconnect', function() {
        redisClient.quit();
    });
});

In the above code our nodejs is listening on port 3000 . 
we have created a new redis client 

const redisClient = redis.createClient();

and subscribed to users.update event 

redisClient.subscribe('users.update');

    console.log("Redis server running.....");

    redisClient.on("message", function(channel, message) {
        console.log(message);
        client.emit(channel, message);
    });

Once the event is fired we will send message to all subscribers to that channel .
Now go back to our index.blade.php do you remember what i told about socket.io script . 
make sure the port which are we are using in server.js is same here . 

​Wrapping up with AngularJS

Our angularjs directory structure is below

In our app.js i have defined one route so you can expand it and do not have to declare routeprovider.

app.config(['$routeProvider',
    function($routeProvider) {
        $routeProvider.
            when('/', {
                templateUrl: '/partials/users/index.html',
                controller: 'UsersController'
            }).
            otherwise({
                redirectTo: '/'
        });
    }
]);

We have defined socket factory 

app.factory('socket', function ($rootScope) {
    var socket = io.connect('http://127.0.0.1:3000/');
    return {
        on: function (eventName, callback) {
            socket.on(eventName, function () {
                var args = arguments;
                $rootScope.$apply(function () {
                    callback.apply(socket, args);
                });
            });
        },
        emit: function (eventName, data, callback) {
            socket.emit(eventName, data, function () {
                var args = arguments;
                $rootScope.$apply(function () {
                    if (callback) {
                        callback.apply(socket, args);
                    }
                });
            })
        }
    };
});

Our userscontroller is basic it will load all users at startup and have methods to create , update or delete user . In our userscontroller we have listening for users.update event and get the new users set .

Final Step

Our final step is to run our redis server with admin rights , run our server.js using node which can be done like below in terminal / cmd

node server.js

I have also created a migration make sure to run 

php artisan migrate

You can also monitor redis request using redis-cli monitor
Now navigate to / route and you will see all users 

Output:


Thanks 
KodeInfo

 

Author

  • Imran Iqbal
    Imran Iqbal

    Imran is a web developer and consultant from India. He is the founder of KodeInfo, the PHP and Laravel Community . In the meantime he follows other projects, works as a freelance backend consultant for PHP applications and studies IT Engineering . He loves to learn new things, not only about PHP or development but everything.

Related

WHY USE A FRAMEWORK OVER PLAIN PHP

WHY USE A FRAMEWORK OVER PLAIN PHP
read more

GETTING STARTED WITH LARAVEL

GETTING STARTED WITH LARAVEL
read more

UNDERSTANDING LARAVEL STRUCTURE

UNDERSTANDING LARAVEL STRUCTURE
read more

UNDERSTANDING LARAVEL ROUTES

UNDERSTANDING LARAVEL ROUTES
read more

comments powered by Disqus