Realtime App Using Laravel + NodeJs + AngularJS + Redis
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 the 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
<?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 the 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 the redis-cli monitor
Now navigate to / route and you will see all users
Output:
Thanks
KodeInfo
No Comments
Leave a comment Cancel