Posted on 3 Comments

Create A Point Of Sales System With Vue/Laravel + Stripe

Point Of Sales In The Palm Of Your Hand

In today’s tutorial I will be creating a point of sales system utilizing Vue and Laravel with Stripe being our payment processor. The program will allow a stripe account holder to take payments and if on mobile will allow them to scan the card via the device’s camera. It will utilize Laravel Passport for secure API calls and Stripe to handle the payments.

Installing The Dependencies

The app uses two dependencies as of now and those are Stipe and Laravel Passport install them using composer
 

composer require laravel/passportstripe/stripe-php

Now run the migrations (I’m using Laravel 5.5 so the packages are auto-discovered)

php artisan migrate

Now open your app/User.php model and edit the following

<?php
namespace App;
use Laravel\Passport\HasApiTokens;
use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;
class User extends Authenticatable
{
    use HasApiTokens, Notifiable;
}

Next register the Passport routes in your AuthServiceProvider

<?php
namespace App\Providers;
use Laravel\Passport\Passport;
use Illuminate\Support\Facades\Gate;
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
class AuthServiceProvider extends ServiceProvider
{
    /**
     * The policy mappings for the application.
     *
     * @var array
     */
    protected $policies = [
        'App\Model' => 'App\Policies\ModelPolicy',
    ];
    /**
     * Register any authentication / authorization services.
     *
     * @return void
     */
    public function boot()
    {
        $this->registerPolicies();
        Passport::routes();
    }
}

Register the api driver in config/auth.php

'guards' => [
    'web' => [
        'driver' => 'session',
        'provider' => 'users',
    ],
    'api' => [
        'driver' => 'passport',
        'provider' => 'users',
    ],
],

Lastly set the web middleware group

'web' => [
    // Other middleware...
    \Laravel\Passport\Http\Middleware\CreateFreshApiToken::class,
],

Controller

This application only needs one external controller

php artisan make:controller StripeController

This controller will only contain 2 methods __construct() and charge(). The __construct method will set the StripeApiKey and the charge method actually makes the charge

<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class StripeController extends Controller
{
 //
 public function __construct(){
 \Stripe\Stripe::setApiKey(env('STRIPE_SECRET'));
 }
public function charge(Request $request){
 try {
 // Use Stripe's library to make requests...
 $token = \Stripe\Token::create(array(
 "card" => array(
 "number" => $request->card['card_number'],
 "exp_month" => $request->card['expiry_month'],
 "exp_year" => $request->card['expiry_year'],
 "cvc" => $request->card['cvv']
 )
 ));
 \Stripe\Charge::create(array(
 "amount" => $request->amount * 100,
 "currency" => "usd",
 "source" => $token, // obtained with Stripe.js
 "description" => $request->description,
 "receipt_email" => $request->email
 ));
 return response()->json([
 'success' => true
 ]);
 } catch(\Stripe\Error\Card $e) {
 // Since it's a decline, \Stripe\Error\Card will be caught
 return response()->json($e->getJsonBody());
 } catch (\Stripe\Error\RateLimit $e) {
 // Too many requests made to the API too quickly
 return response()->json($e->getJsonBody());
 } catch (\Stripe\Error\InvalidRequest $e) {
 // Invalid parameters were supplied to Stripe's API
 return response()->json($e->getJsonBody());
 } catch (\Stripe\Error\Authentication $e) {
 // Authentication with Stripe's API failed
 // (maybe you changed API keys recently)
 return response()->json($e->getJsonBody());
 } catch (\Stripe\Error\ApiConnection $e) {
 // Network communication with Stripe failed
 return response()->json($e->getJsonBody());
 } catch (\Stripe\Error\Base $e) {
 // Display a very generic error to the user, and maybe send
 // yourself an email
 return response()->json($e->getJsonBody());
 } catch (Exception $e) {
 // Something else happened, completely unrelated to Stripe
 return response()->json($e->getJsonBody());
 }
 }
}

The controller is now finished let’s create the API routes

API Routes

Open the routes/api.php file and add the following routes

<?php
use Illuminate\Http\Request;
/*
|--------------------------------------------------------------------------
| API Routes
|--------------------------------------------------------------------------
|
| Here is where you can register API routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| is assigned the "api" middleware group. Enjoy building your API!
|
*/
Route::middleware('auth:api')->get('/user', function (Request $request) {
 return $request->user();
});
Route::middleware('auth:api')->post('/charge','StripeController@charge');

The backend is now complete now for the front end.

Vue Component

Get rid of the example component and create a new one called ChargeComponent and add the following content

<template>
 <div class="container">
 <div class="row">
 <div class="col-md-8 col-md-offset-2">
 <div class="panel panel-default">
 <div class="panel-heading">Make A Charge</div>
<div class="panel-body">
 <fieldset>
 <div class="form-group">
 <label class="col-sm-3 control-label" for="amount">Amount</label>
 <div class="col-sm-9">
 <input type="number" class="form-control" id="amount" placeholder="Amount To Charge" v-model="amount">
 </div>
 </div>
 <div class="form-group">
 <label class="col-sm-3 control-label" for="email">Email</label>
 <div class="col-sm-9">
 <input type="email" class="form-control" id="email" placeholder="Email Receipt" v-model="email">
 </div>
 </div>
 <div class="form-group">
 <label class="col-sm-3 control-label" for="description">Description</label>
 <div class="col-sm-9">
 <input type="text" class="form-control" id="description" placeholder="Credit Card Description" v-model="description">
 </div>
 </div>
 <div class="form-group">
 <label class="col-sm-3 control-label" for="card-number">Card Number</label>
 <div class="col-sm-9">
 <input type="text" class="form-control" name="card-number" id="card-number" placeholder="Debit/Credit Card Number" autocomplete="cc-number" v-model="card.card_number">
 </div>
 </div>
 <div class="form-group">
 <label class="col-sm-3 control-label" for="expiry-month">Expiration Date</label>
 <div class="col-sm-9">
 <div class="row">
 <div class="col-xs-3">
 <select class="form-control col-sm-2" name="expiry-month" id="expiry-month" autocomplete="cc-exp-month" v-model="card.expiry_month">
 <option>Month</option>
 <option value="01">Jan (01)</option>
 <option value="02">Feb (02)</option>
 <option value="03">Mar (03)</option>
 <option value="04">Apr (04)</option>
 <option value="05">May (05)</option>
 <option value="06">June (06)</option>
 <option value="07">July (07)</option>
 <option value="08">Aug (08)</option>
 <option value="09">Sep (09)</option>
 <option value="10">Oct (10)</option>
 <option value="11">Nov (11)</option>
 <option value="12">Dec (12)</option>
 </select>
 </div>
 <div class="col-xs-3">
 <select class="form-control" name="expiry-year" autocomplete="cc-exp-year" v-model="card.expiry_year">
 <option value="17">2017</option>
 <option value="18">2018</option>
 <option value="19">2019</option>
 <option value="20">2020</option>
 <option value="21">2021</option>
 <option value="22">2022</option>
 <option value="23">2023</option>
 </select>
 </div>
 </div>
 </div>
 </div>
 <div class="form-group">
 <label class="col-sm-3 control-label" for="cvv">Card CVV</label>
 <div class="col-sm-3">
 <input type="text" class="form-control" name="cvv" id="cvv" placeholder="Security Code" autocomplete="cvc" v-model="card.cvv">
 </div>
 </div>
 <div class="form-group">
 <div class="col-sm-offset-3 col-sm-9">
 <button type="button" class="btn btn-success" v-on:click="createCharge">Pay Now</button>
 </div>
 </div>
 </fieldset>
 </div>
 </div>
 </div>
 </div>
 </div>
</template>
<script>
 export default {
 mounted() {
 console.log('Component mounted.')
 },
 data(){
 return{
 card: {
 card_number: null,
 expiry_year: null,
 expiry_month: null,
 cvv: null
 },
 amount: 0,
 email: null,
 description: null
 }
 },
 methods: {
 createCharge: function(){
 axios.post('/api/charge',{card: this.card, amount: this.amount, description: this.description})
 .then(function(data){
 alert('Success!')
 }).catch(function(error){
 alert(error.message);
 });
 }
 }
 }
</script>

Edit the app.js file to match the following

/**
 * First we will load all of this project's JavaScript dependencies which
 * includes Vue and other libraries. It is a great starting point when
 * building robust, powerful web applications using Vue and Laravel.
 */
require('./bootstrap');
window.Vue = require('vue');
/**
 * Next, we will create a fresh Vue application instance and attach it to
 * the page. Then, you may begin adding components to this application
 * or customize the JavaScript scaffolding to fit your unique needs.
 */
Vue.component('charge-component', require('./components/ChargeComponent.vue'));
const app = new Vue({
 el: '#app'
 });

now install the npm dependencies and run mix

npm install && npm run dev

Enjoy your new app! You can fork the source code here! Be sure to like/subscribe/share and if you want to show your support please check out the shop!

Posted on 2 Comments

Automating Your DMs With Nodejs

In The Last Tutorial

We created a Laravel/Vue app that allows us to automate and schedule our Twitter posts. Now we are going to add a Node.js microservice that will send an auto DM to those who follow you on Twitter. This adds a personal touch when you gain new followers and makes people more apt to pay attention to your tweets. If you don’t have the app from the last tutorial, you can download it here. Otherwise open up your terminal to the project root and let’s get started.
 

Node Script

This node script will use the twit npm module to create a Twitter stream listening to our own account. When the follow event is fired we will grab the source id of the user that sent us a follow request, after which we will send a DM to that user id with a welcome message. Let’s start by adding the twit dependency and the dotenv dependency.

npm install twit dotenv --save

Now create a new file called twitter.js and fill with the following:

require('dotenv').config()
var Twit = require('twit')
var T = new Twit({
 consumer_key: process.env.TWITTER_CONSUMER_KEY,
 consumer_secret: process.env.TWITTER_CONSUMER_SECRET,
 access_token: process.env.TWITTER_ACCESS_TOKEN,
 access_token_secret: process.env.TWITTER_ACCESS_TOKEN_SECRET,
 timeout_ms: 60*1000, // optional HTTP request timeout to apply to all requests.
})var stream = T.stream('user');
stream.on('connected', function(response){
 console.log('Connected!')
});
stream.on('follow',function(data){
 console.log(data.source.id);
 T.post('direct_messages/events/new',{
 "event": {
 "type": "message_create",
 "message_create": {
 "target": {
 "recipient_id": data.source.id
 },
 "message_data": {
 "text": "Thanks for the follow! Be sure to check out my blog https://jyroneparker.com",
 }
 }
 }
})});

The dotenv dependency allows us to use the same .env file to grab our Twitter app credentials that we used in our Laravel app. As you can see the stream fires on different events we just choose the follow and the connected events. Lastly we need to run the script make sure you have supervisor or some other program running it as a daemon.

node twitter.js

If you want to extend the functionality read up on the Twitter stream documentation here. Otherwise add some auto like functionality to your bot.

Posted on 7 Comments

How To Automate Your Twitter Posts With Laravel and Vue

Twitter Marketing Is Huge

Well for my platform it is anyway, whenever I create a new app or website Twitter is the first social network that I turn to. It’s fast, the analytics are on point, and it’s easy to use, my only issue is that I am busy coding and whatnot to Tweet as much as I would like or need to in order to optimize my reach. This leaves me with two options:

  1. Pay someone to manage my account for me
  2. Learn the Twitter API and write my own automation program to manage it myself

The idea of paying another person to manage my social media just made my stomach and my wallet hurt so the OBVIOUS choice is to use the API. Now let me tell you thisn  API is DENSE there is a lot you can do with it (click here for the docs) so I will only focus on one aspect in this tutorial, sending posts. The first basic application that I wrote was a simple post scheduler, it allows me to create as many posts as I want, set a date-time to post at, then a Laravel job executes at the desired time, then deletes the entry from the database. This basic application has saved me tremendous time because now I can just schedule all my important tweets weeks or months early!
 

Registering Your Twitter App

Before you can do any actual coding you need to register your app on Twitter, head over to the developer portal and create your app. The home page will show a list of your Twitter apps, if this is your first time then you won’t have any apps, click on create new app.
Fill out all the mandatory fields then proceed you should see a screen that shows you your Consume Key and Consumer Secret. Copy those down and also create some access tokens. These access tokens are for writing Twitter apps that only interact with your OWN account. Copy down the access token and the access token secret and that’s all you have to do. Now let’s code!
 

App Components

 

The main page will be a Vue.js app that allows you to add new tweets, and an area below to edit and delete them. In a traditional SPA manner the app will interact with an API for all it’s calls. The backend will consist of one model (Tweet), one controller (TweetController), and one Job ProcessTweet, pretty simple stuff let’s begin.  Start by creating your Laravel application.

laravel new tweeter && cd tweeter && php artisan make:auth

This app only has one composer dependency add it

composer require thujohn/twitter

In the config/app.php make sure to add the following provider and alias

'providers' => [
    Thujohn\Twitter\TwitterServiceProvider::class,
]
'aliases' => [
    'Twitter' => Thujohn\Twitter\Facades\Twitter::class,
]

Lastly run the following command to import the twitter config/ttwitter.php file and modify inserting your Twitter app credentials

php artisan vendor:publish --provider="Thujohn\Twitter\TwitterServiceProvider"

Tweet Model

Now create the Tweet model along with it’s migration

php artisan make:model -m Tweet

The Tweet model only has two attributes a string that holds the content that is limited to 140 characters and a timestamp for when you want the tweet to be published.

<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateTweetsTable extends Migration
{
 /**
 * Run the migrations.
 *
 * @return void
 */
 public function up()
 {
 Schema::create('tweets', function (Blueprint $table) {
 $table->increments('id');
 $table->string('content',140);
 $table->timestamp('publish_timestamp');
 $table->timestamps();
 });
 }/**
 * Reverse the migrations.
 *
 * @return void
 */
 public function down()
 {
 Schema::dropIfExists('tweets');
 }
}

The Tweet model is pretty bare bones

<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Tweet extends Model
{
 //
 public $fillable = [
 'content',
 'publish_timestamp'
 ];
}

Go ahead and run your migrations we are done with models, now to make our RESTful controller.

RESTful Controller and Routes

Create a resource controller for our Tweet model

php artisan make:controller --resource TweetController

The controller is basic I purposely left out validation and such as a homework exercise for you, but the gist is basic CRUD operations that return JSON

<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Tweet;
use App\Jobs\ProcessTweet;
class TweetController extends Controller
{
 /**
 * Display a listing of the resource.
 *
 * @return \Illuminate\Http\Response
 */
 public function index()
 {
 //
 return response()->json(Tweet::all());
 }
/**
 * Show the form for creating a new resource.
 *
 * @return \Illuminate\Http\Response
 */
 public function create()
 {
 //
 }
/**
 * Store a newly created resource in storage.
 *
 * @param \Illuminate\Http\Request $request
 * @return \Illuminate\Http\Response
 */
 public function store(Request $request)
 {
 //create the tweet
 $tweet = Tweet::Create([
 'content' => $request->content,
 'publish_timestamp' => \Carbon\Carbon::parse($request->publish_timestamp)
 ]);
 //Add tweet to the queue
 ProcessTweet::dispatch($tweet)->delay(\Carbon\Carbon::parse($request->publish_timestamp,'America/New_York')->diffInSeconds(\Carbon\Carbon::now('America/New_York')));
//return json
 return response()->json($tweet);
 }/**
 * Display the specified resource.
 *
 * @param int $id
 * @return \Illuminate\Http\Response
 */
 public function show($id)
 {
 //
 return response()->json(Tweet::findOrFail($id));
 }/**
 * Show the form for editing the specified resource.
 *
 * @param int $id
 * @return \Illuminate\Http\Response
 */
 public function edit($id)
 {
 //
 }/**
 * Update the specified resource in storage.
 *
 * @param \Illuminate\Http\Request $request
 * @param int $id
 * @return \Illuminate\Http\Response
 */
 public function update(Request $request, $id)
 {
 //
 $tweet = Tweet::findOrFail($id);
 $tweet->fill([
 'content' => $request->content,
 'publish_timestamp' => \Carbon\Carbon::parse($request->publish_timestamp)
 ]);
 //return json
 return response()->json($tweet);
 }
/**
 * Remove the specified resource from storage.
 *
 * @param int $id
 * @return \Illuminate\Http\Response
 */
 public function destroy($id)
 {
 //
 Tweet::destroy($id);
 }
}

Notice the ProcessTweet job? We will implement that next but for now open up the routes/api.php file and add the following resource route

<?php
use Illuminate\Http\Request;
/*
|--------------------------------------------------------------------------
| API Routes
|--------------------------------------------------------------------------
|
| Here is where you can register API routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| is assigned the "api" middleware group. Enjoy building your API!
|
*/
Route::middleware('auth:api')->get('/user', function (Request $request) {
 return $request->user();
});
Route::resource('/tweet','TweetController');

Now our Vue application (patience it’s coming) can interact with our backend. Now let’s add our ProcessTweet job and we will be finished on the backend.

ProcessTweet Job

In order to schedule the tweets to post at any given time you must utilize Laravel Jobs this will allow you to schedule your events to fire at a certain point in time. If you aren’t familiar with jobs and queues please look at my previous post because I will not be covering setting up queues in this tutorial, otherwise create a new job

php artisan make:job ProcessTweet

In the handle method we simply call the Twitter API and send the tweet then delete it from the database

<?php
namespace App\Jobs;
use Illuminate\Bus\Queueable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Twitter;
class ProcessTweet implements ShouldQueue
{
 use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
 public $tweet;
 /** * Create a new job instance.
 *
 * @return void
 */
 public function __construct(\App\Tweet $tweet)
 {
 //
 $this->tweet = $tweet;
 }
/**
 * Execute the job.
 *
 * @return void
 */
 public function handle()
 {
 //post the tweet
 Twitter::postTweet(['status' => $this->tweet->content, 'format' => 'json']);
 //delete the tweet from database
 \App\Tweet::destroy($this->tweet->id);
 }
}

Your backend is now functioning run your queue worker and test around with it or continue to creating the Vue application.

Getting The Front End Together

We need to add vue-resource to call our api so let’s add that first

npm install --save vue-resource

Also update the resources/js/app.js file

/**
 * First we will load all of this project's JavaScript dependencies which
 * includes Vue and other libraries. It is a great starting point when
 * building robust, powerful web applications using Vue and Laravel.
 */
require('./bootstrap');
window.Vue = require('vue');
const resource = require('vue-resource');
Vue.use(resource);
/**
 * Next, we will create a fresh Vue application instance and attach it to
 * the page. Then, you may begin adding components to this application
 * or customize the JavaScript scaffolding to fit your unique needs.
 */
Vue.component('tweet-component', require('./components/TweetComponent.vue'));
const app = new Vue({
 el: '#app'
});

As you can see the example-component has been replaced with the tweet-component rename your ExampleComponent.vue to TweetComponent.vue and update it with the following contents

<template>
 <div class="container">
 <div class="row">
 <div class="col-md-8 col-md-offset-2">
 <div class="panel panel-default">
 <div class="panel-heading">Tweet Scheduler</div>
<div class="panel-body">
 <div class="form-group">
 <input class="form-control" placeholder="Content" v-model="newTweet.content">
 </div>
 <div class="form-group">
 <input class="form-control" type="datetime-local" placeholder="Description" v-model="newTweet.publish_timestamp">
 </div>
<div class="form-group">
 <button class="btn btn-success" v-on:click="addTweet(newTweet)">Add Tweet</button>
 </div>
 <ul v-if="ready" class="list-group">
 <li v-for="post in tweets" class="list-group-item clearfix">
 {{post.content}}
 <span class="pull-right button-group">
 <button class=" btn btn-default" v-on:click="openEditTweet(post)">Edit</button>
 <button class=" btn btn-danger" v-on:click="deleteTweet(post)">Delete</button>
 </span>
 </li>
 </ul>
</div>
 </div>
 </div>
 </div>
 <!-- Modal -->
<div id="editModal" class="modal fade" role="dialog">
 <div class="modal-dialog">
<!-- Modal content-->
 <div class="modal-content">
 <div class="modal-header">
 <button type="button" class="close" data-dismiss="modal">&times;</button>
 <h4 class="modal-title">{{selectedTweet.content}}</h4>
 </div>
 <div class="modal-body">
 <div class="form-group">
 <input class="form-control" placeholder="Content" v-model="selectedTweet.content">
 </div>
 <div class="form-group">
 <input class="form-control" type="datetime-local" placeholder="Publish At" v-model="selectedTweet.publish_timestamp">
 </div>
 <div class="form-group">
 <button class="btn btn-info" v-on:click="editTweet(selectedTweet)">Edit Tweet</button>
 </div>
 </div>
 <div class="modal-footer">
 <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
 </div>
 </div>
</div>
 </div>
 </div>
</template>
<script>
export default {
 mounted() {
 console.log('Component mounted.')
 },
 data(){
 return {
 tweets: {},
 newTweet:{
 'content': '',
 'publish_timestamp': ''
 },
 selectedTweet:{
 'content': '',
 'publish_timestamp': ''
 },
 ready: false
 }
 },
 created(){
 this.$http.get('/api/tweet').then(function(data){
 this.tweets = data.data;
 this.ready = true;
 });
 },
 methods: {
 addTweet: function(tweet){
 this.$http.post('/api/tweet',tweet).then(function(data){
 this.tweets.unshift({content:tweet.content,publish_timestamp:tweet.publish_timestamp});
 });
 },
 editTweet: function(tweet){
 this.$http.put('/api/tweet/'+tweet.id,this.selectedTweet).then(function(data){
 let index = this.tweets.indexOf(tweet);
 this.tweets[index] = tweet;
 alert('Updated!');
 $("#editModal").modal('hide');
 });
 },
 openEditTweet: function(tweet){
 console.log(tweet);
 this.selectedTweet = tweet;
 $("#editModal").modal({show: true});
},
 deleteTweet: function(tweet){
 this.$http.delete('/api/tweet/'+tweet.id).then(function(data){
 let index = this.tweets.indexOf(tweet)
 this.tweets.splice(index, 1);
 });
}
}
}
</script>

Open up your resources/assets/sass/app.scss file and add the following fade transition

.fade-enter-active, .fade-leave-active {
 transition: opacity .5s
}
.fade-enter, .fade-leave-to /* .fade-leave-active below version 2.1.8 */ {
 opacity: 0
}

Run your npm build script as well as your queue worker and start tweeting! You can get the full source code here, and as always please subscribe to my blog via email or push notifications, share, and leave your comment below! If you would like to add a real-time nodejs microservice to your app then read here!

Posted on 1 Comment

Check Out My New Web Application Anon Video Chat

Anon Video Chat

Anon Video Chat is my latest web application, the premise is simple, anonymous video chat using webRTC for secure low latency browser to browser connections. This app assigns you a random channel ID everytime you load the page (you have the option on saving the current ID to your device to use permanently) you can call other channel IDs and if they answer a webRTC connection is set up between your two browsers and the live feeds start. In the future I plan on adding features like file and location sharing and possibly Bitcoin sending/receiving. No data is ever stored on my server making all actions anonymous. I decided to write this for a few reasons

  • I wanted to sharpen my Vue.js skills
  • I wanted to better understand webRTC
  • I need to implement P2P chat in other applications
  • I didn’t want to pay Twilio to do something I could do myself

Why Did I Choose To Release It?

The reason I chose to release Anon Video Chat to the public is because I believe in the philosophy of an open and free internet. Other video applications like Skype collect tons of metadata on you and in turn sell that data on you for a profit. In cases like these you are not the consumer but the product. Not only is this creepy but it affects the performance of the application by providing overhead with no benefit to the end user resulting in laggy performance.

anon video chat screenshot
Screenshot of me and my friend in France using Anon Video Chat

Current Limitations Of Anon Video Chat

Until iOS11 is released later in the fall, Anon Video Chat will not work on iOS devices (blame them not me they decided not to implement webRTC until now!). Other than that it should work on Chrome, Firefox and Opera. If you notice that it doesn’t please email me and let me know jyrone.parker@gmail.com.
Needless to say I wrote this app in a few hours, you can tell by it’s current lack of features but as I stated above I will update it periodically. If the demand calls for it I will add whatever features you guys leave below in the comments. Also if you are interested in contributing to the project, drop your GitHub username in the comment section below and I will add you to the private repo!

Posted on 47 Comments

Implementing Web Push Using Laravel 5.4 Notifications

Web Push Is Awesome

No seriously! It’s a pivotal moment in web development. You see web push is a W3C protocol that allows websites to communicate with a user’s browser in the background, using this web developers can now do things such as: background push notifications, offline sync, and background analytics just to name few. The web push api follows the protocol and consist of 4 main stages:

  1. User Agent installs service worker
  2. App asks permission to show notifications
  3. Once permission is granted, subscribe to push
  4. Save subscription details on backend
  5. Send notification via Laravel notification

All the code can be seen on my Github and the live demo can be seen here!

Components Of Web Push

Implementing web push requires a true full-stack approach, on the back-end we have to:

  • Implement the WebPush notification channel
  • Set up VAPID keys (more details later)
  • Configure the user model to manage subscriptions
  • Fire the notifications

On the front end we must:

  • Create and install a service worker, the majority of our logic will be contained here and is responsible to handling push notifications
  • Give user prompt to accept permissions
  • Give user ability to send notification to self

I will break it down into back-end and front-end implementations as to not confuse you; let’s start with the back-end.
 

Creating The Backend

Create a blank Laravel application and run the following composer command to download the web push notification channel.

composer require laravel-notification-channels/webpush

Afterwards register in your config/app.php


// config/app.php 'providers' => [ 
... 
NotificationChannelsWebPushWebPushServiceProvider::class,
 ],

Next open up your user model and add the following.

<?php
namespace App;
use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;
use NotificationChannels\WebPush\HasPushSubscriptions;
class User extends Authenticatable
{
    use Notifiable, HasPushSubscriptions;
    /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    protected $fillable = [
        'name', 'email', 'password',
    ];
    /**
     * The attributes that should be hidden for arrays.
     *
     * @var array
     */
    protected $hidden = [
        'password', 'remember_token',
    ];
}

This added HasPushSubscriptions trait allows the user model to receive push notifications.
Next publish the migration with:

php artisan vendor:publish --provider="NotificationChannels\WebPush\WebPushServiceProvider" --tag="migrations"

Run the migrate command to create the necessary table:

php artisan migrate

You can also publish the config file with:

php artisan vendor:publish --provider="NotificationChannels\WebPush\WebPushServiceProvider" --tag="config"

Generate the VAPID keys with (required for browser authentication) with:

php artisan webpush:vapid

This command will set VAPID_PUBLIC_KEY and VAPID_PRIVATE_KEYin your .env file. VAPID is a web push protocol that is needed if we want to send push notifications. Basically it voluntarily identifies itself to a push notification server. If you want to read the specification you can here. Create the default auth routes

php artisan make:auth

Next let’s create the notification being used

php artisan make:notification GenericNotification

Open up the file and replace with the following contents

<?php
namespace App\Notifications;
use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Notification;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\MailMessage;
use NotificationChannels\WebPush\WebPushMessage;
use NotificationChannels\WebPush\WebPushChannel;
class GenericNotification extends Notification
{
    use Queueable;
    public $title, $body;
    /**
     * Create a new notification instance.
     *
     * @return void
     */
    public function __construct($title, $body)
    {
        //
        $this->title = $title;
        $this->body = $body;
    }
    /**
     * Get the notification's delivery channels.
     *
     * @param  mixed  $notifiable
     * @return array
     */
    public function via($notifiable)
    {
        return [WebPushChannel::class];
    }
    public function toWebPush($notifiable, $notification)
    {
      $time = \Carbon\Carbon::now();
        return WebPushMessage::create()
            // ->id($notification->id)
            ->title($this->title)
            ->icon(url('/push.png'))
            ->body($this->body);
            //->action('View account', 'view_account');
    }
}

Next open up routes/api.php and fill out the API routes

<?php
use Illuminate\Http\Request;
/*
|--------------------------------------------------------------------------
| API Routes
|--------------------------------------------------------------------------
|
| Here is where you can register API routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| is assigned the "api" middleware group. Enjoy building your API!
|
*/
Route::middleware('auth:api')->get('/user', function (Request $request) {
    return $request->user();
});
Route::post('/save-subscription/{id}',function($id, Request $request){
  $user = \App\User::findOrFail($id);
  $user->updatePushSubscription($request->input('endpoint'), $request->input('keys.p256dh'), $request->input('keys.auth'));
  $user->notify(new \App\Notifications\GenericNotification("Welcome To WebPush", "You will now get all of our push notifications"));
  return response()->json([
    'success' => true
  ]);
});
Route::post('/send-notification/{id}', function($id, Request $request){
  $user = \App\User::findOrFail($id);
  $user->notify(new \App\Notifications\GenericNotification($request->title, $request->body));
  return response()->json([
    'success' => true
  ]);
});

That’s it for the back end let’s move to the front end!

Setting Up The Service Worker

In order for the web application to handle notifications in the background, service workers must be implemented. A service worker is a javascript file that runs on a separate thread in the background (multi-threading FTW). These files must be installed by the front end application before they take effect. Open a new file public/js/service-worker.js and let’s implement a push listener.

self.addEventListener('push', function(event) {
  if (event.data) {
    var data = event.data.json();
    self.registration.showNotification(data.title,{
      body: data.body,
      icon: data.icon
    });
    console.log('This push event has data: ', event.data.text());
  } else {
    console.log('This push event has no data.');
  }
});

Now open up resources/views/home.blade.php and fill in the following

@extends('layouts.app')
@section('content')
<div class="container">
    <div class="row">
        <div class="col-md-8 col-md-offset-2">
            <div class="panel panel-default">
                <div class="panel-heading">Dashboard</div>
                <div class="panel-body">
                    <button class="btn btn-info" id="enable-notifications" onclick="enableNotifications()"> Enable Push Notifications </button>
                    <div class="form-group">
                    <input class="form-control" id="title" placeholder="Notification Title">
                    </div>
                    <div class="form-group">
                    <textarea id="body" class="form-control" placeholder="Notification body"></textarea>
                    </div>
                    <div class="form-group">
                    <button class="btn" onclick="sendNotification()">Send Notification</button>
                  </div>
                </div>
            </div>
        </div>
    </div>
</div>
<script>
function sendNotification(){
  var data = new FormData();
data.append('title', document.getElementById('title').value);
data.append('body', document.getElementById('body').value);
var xhr = new XMLHttpRequest();
xhr.open('POST', "{{url('/api/send-notification/'.auth()->user()->id)}}", true);
xhr.onload = function () {
    // do something to response
    console.log(this.responseText);
};
xhr.send(data);
}
var _registration = null;
function registerServiceWorker() {
  return navigator.serviceWorker.register('js/service-worker.js')
  .then(function(registration) {
    console.log('Service worker successfully registered.');
    _registration = registration;
    return registration;
  })
  .catch(function(err) {
    console.error('Unable to register service worker.', err);
  });
}
function askPermission() {
  return new Promise(function(resolve, reject) {
    const permissionResult = Notification.requestPermission(function(result) {
      resolve(result);
    });
    if (permissionResult) {
      permissionResult.then(resolve, reject);
    }
  })
  .then(function(permissionResult) {
    if (permissionResult !== 'granted') {
      throw new Error('We weren\'t granted permission.');
    }
    else{
      subscribeUserToPush();
    }
  });
}
function urlBase64ToUint8Array(base64String) {
  const padding = '='.repeat((4 - base64String.length % 4) % 4);
  const base64 = (base64String + padding)
    .replace(/\-/g, '+')
    .replace(/_/g, '/');
  const rawData = window.atob(base64);
  const outputArray = new Uint8Array(rawData.length);
  for (let i = 0; i < rawData.length; ++i) {
    outputArray[i] = rawData.charCodeAt(i);
  }
  return outputArray;
}
function getSWRegistration(){
  var promise = new Promise(function(resolve, reject) {
  // do a thing, possibly async, then…
  if (_registration != null) {
    resolve(_registration);
  }
  else {
    reject(Error("It broke"));
  }
  });
  return promise;
}
function subscribeUserToPush() {
  getSWRegistration()
  .then(function(registration) {
    console.log(registration);
    const subscribeOptions = {
      userVisibleOnly: true,
      applicationServerKey: urlBase64ToUint8Array(
        "{{env('VAPID_PUBLIC_KEY')}}"
      )
    };
    return registration.pushManager.subscribe(subscribeOptions);
  })
  .then(function(pushSubscription) {
    console.log('Received PushSubscription: ', JSON.stringify(pushSubscription));
    sendSubscriptionToBackEnd(pushSubscription);
    return pushSubscription;
  });
}
function sendSubscriptionToBackEnd(subscription) {
  return fetch('/api/save-subscription/{{Auth::user()->id}}', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json'
    },
    body: JSON.stringify(subscription)
  })
  .then(function(response) {
    if (!response.ok) {
      throw new Error('Bad status code from server.');
    }
    return response.json();
  })
  .then(function(responseData) {
    if (!(responseData.data && responseData.data.success)) {
      throw new Error('Bad response from server.');
    }
  });
}
function enableNotifications(){
  //register service worker
  //check permission for notification/ask
  askPermission();
}
registerServiceWorker();
</script>
@endsection

You must run this from https:// or localhost else it will not work! Otherwise press the enable notifications button then send yourself a test! If you want the whole source code you can fork it here. If you enjoyed my content please like/subscribe/share! If you want another cool project learn how to automate your Twitter account!

Posted on Leave a comment

Jyrone Parker Live – Buidling Our First PHP Application (Fibonacci)

[youtube width=”100%” height=”100%” autoplay=”false”]https://www.youtube.com/watch?v=-77pgzODEQI[/youtube]

What Better Assignment Than Fibonacci?

Since I am finished with the beginning into to PHP videos I found it imperative to do a stream putting everything together and showing you how easy it is to write an app. In today’s live stream I create a Fibonacci app, for those who are not familiar with the Fibonacci sequence there is a great wiki here. In this application I demonstrate variables, control flow logic, exception handling, and function design. All my code can be seen on my Github here or you can view it below in text or on video

<?php
$input = '';
$running = true;
while($running){
try{
getInput($input);
echo calculateFib($input) . "\n";
}
catch(Exception $e){
echo "Message: {$e->getMessage()}";
}
}
function getInput(&$input){
$input = readline("Please input your number or q to quit: ");
};
function calculateFib($input){
if(intval($input) < 0){
throw new Exception("Input must be greater than zero! \n");
}
elseif($input === 'q' || $input === 'Q'){
exit();
}
if(intval($input) === 0 || intval($input) === 1){
return 1;
}
else{
return calculateFib($input-1) + calculateFib($input - 2);
}
};
?>

 

Posted on 4 Comments

Writing A Real Time Location Service – Routes and Controller Logic

Routes and Controllers

If you are following along from the previous tutorial then you should have the following page implemented on the front end:

Home screen
Your screen should look like this

In this part of the tutorial I will show you how to implement the routes and the back-end controller logic. The application has two main controllers that I will be implementing:

  • ActivationController for registering and activating new devices
  • LocationController for storing and broadcasting GPS locations

but before we get into that let’s first create our routes, open up the routes/web.php file and replace the contents with this:


<?php
/*
|--------------------------------------------------------------------------
| Web Routes
|--------------------------------------------------------------------------
|
| Here is where you can register web routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| contains the "web" middleware group. Now create something great!
|
*/
Route::get('/', function () {
return view('welcome');
});
Auth::routes();
Route::get('/home', 'HomeController@index');
/* Commands */
Route::group(['prefix' => 'command','middleware' => 'auth'], function(){
Route::get('start-gps', 'LocationController@startGps');
Route::get('stop-gps','LocationController@stopGps');
});
/* Activation */
Route::group(['prefix' => 'activation','middleware' => 'auth'], function(){
Route::get('/','ActivationController@getView');
Route::post('/','ActivationController@activateDevice');
Route::post('/register','ActivationController@registerDevice');
});ro

 
and open up routes/api.php and replace the contents with this:


<?php
use Illuminate\Http\Request;
/*
|--------------------------------------------------------------------------
| API Routes
|--------------------------------------------------------------------------
|
| Here is where you can register API routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| is assigned the "api" middleware group. Enjoy building your API!
|
*/
Route::resource('location','LocationController')

 
You may be wondering why I am editing two separate route files and the reason being is becasue the routes/api.php file is what the Android application will be interacting with and it’s just good practice to keep API code separate from main code. As you can see there are two route groups one for activation and one for commands. In the commands group I have a start-gps and a stop-gps route. These are going to do what their name implies and instruct the Android device to either start sending GPS data to the server, or to stop it. Next in the activation group there is a get route / to return the activation view, and a post route  / to activate the device, lastly there is a post route to register a new device. Basically the Android device will call register when the app opens for the first time, this will generate a code that the user has to enter at the activation screen to give the web app permission to start collecting data anonymously. The routes/api.php file has a resourceful route called location that will call the CRUD functions on the LocationController, although right now I am only implementing the store() function (gotta give you guys SOME homework). If you run php artisan route:list in your terminal you should see the following routes:

 +--------+-----------+------------------------------+------------------+------------------------------------------------------------------------+------------+ | Domain | Method | URI | Name | Action | Middleware | +--------+-----------+------------------------------+------------------+------------------------------------------------------------------------+------------+ | | GET|HEAD | / | | Closure | web | | | POST | activation | | App\Http\Controllers\ActivationController@activateDevice | web,auth | | | GET|HEAD | activation | | App\Http\Controllers\ActivationController@getView | web,auth | | | POST | activation/register | | App\Http\Controllers\ActivationController@registerDevice | web,auth | | | GET|HEAD | api/location | location.index | App\Http\Controllers\LocationController@index | api | | | POST | api/location | location.store | App\Http\Controllers\LocationController@store | api | | | GET|HEAD | api/location/create | location.create | App\Http\Controllers\LocationController@create | api | | | PUT|PATCH | api/location/{location} | location.update | App\Http\Controllers\LocationController@update | api | | | GET|HEAD | api/location/{location} | location.show | App\Http\Controllers\LocationController@show | api | | | DELETE | api/location/{location} | location.destroy | App\Http\Controllers\LocationController@destroy | api | | | GET|HEAD | api/location/{location}/edit | location.edit | App\Http\Controllers\LocationController@edit | api | | | GET|HEAD | command/start-gps/{id} | | App\Http\Controllers\LocationController@startGps | web,auth | | | GET|HEAD | command/stop-gps/{id}| | App\Http\Controllers\LocationController@stopGps | web,auth | | | GET|HEAD | home | | App\Http\Controllers\HomeController@index | web,auth | | | GET|HEAD | login | login | App\Http\Controllers\Auth\LoginController@showLoginForm | web,guest | | | POST | login | | App\Http\Controllers\Auth\LoginController@login | web,guest | | | POST | logout | logout | App\Http\Controllers\Auth\LoginController@logout | web | | | POST | password/email | | App\Http\Controllers\Auth\ForgotPasswordController@sendResetLinkEmail | web,guest | | | POST | password/reset | | App\Http\Controllers\Auth\ResetPasswordController@reset | web,guest | | | GET|HEAD | password/reset | | App\Http\Controllers\Auth\ForgotPasswordController@showLinkRequestForm | web,guest | | | GET|HEAD | password/reset/{token} | | App\Http\Controllers\Auth\ResetPasswordController@showResetForm | web,guest | | | POST | register | | App\Http\Controllers\Auth\RegisterController@register | web,guest | | | GET|HEAD | register | register | App\Http\Controllers\Auth\RegisterController@showRegistrationForm | web,guest | +--------+-----------+------------------------------+------------------+------------------------------------------------------------------------+------------+

Implementing The Controller

If you haven’t already created the controllers, please do so with the following commands:
php artisan make:controller LocationController
php artisan make:controller ActivationController
Let’s start with the ActivationController, open up app/Http/Controllers/ActivationController.php. This controller only has three functions getView(), activateDevice(), and registerDevice(), I described their functions above so instead of repeating myself I will post the code:

<?php namespace App\Http\Controllers; use Illuminate\Http\Request; use App\ActivationCode; use App\Device; class ActivationController extends Controller {  //  public function getView(){  return view('activation');  } public function activateDevice(Request $request){  $code = ActivationCode::where('code', $request->code)->first();  if ($code != null){  $device = Device::create([  'user_id' => $request->user()->id,  'uuid' => $code->uuid  ]);  }  return redirect('/home');y  } public function registerDevice(Request $request){  $code = ActivationCode::Create([  'uuid' => $request->uuid,  'code' => $request->code  ]);  } }

You may be wondering where the uuid and code is coming from. This uuid and the code are generated on the Android device when the app is opened for the first time. This uuid is what maps the app to each device, while the code is shown to the user on the android device they then have to enter that code on the activation page to activate the device and start collecting data. The devices also listens on a Socket.IO channel that corresponds to their uuid so this is how we will talk to the Android devices. Next open up the LocationController and enter the following contents:

<?php namespace App\Http\Controllers; use Illuminate\Http\Request; use App\Location; use App\Device; class LocationController extends Controller {  // public function startGps($id){  $device = Device::findOrFail($id);  event(new \App\Events\SendCommand($device,'start-gps'));  } public function stopGps($id){  $device = Device::findOrFail($id);  event(new \App\Events\SendCommand($device,'stop-gps'));  }ns  public function store(Request $request){  $device = Device::where('uuid', $request->uuid)->first();  $location = Location::Create([  'long' => $request->long,  'lat' => $request->lat,  'device_id' => $device->id  ]);  event(new \App\Events\LocationCreated($location));  } }be i

All three of these functions are relying on Laravel events because they will be interacting with sockets, however we will actually be implementing the event logic in the next tutorial. Right now just understand that the sendCommand event will be responsible for telling the Android device to start or stop the GPS transponder and the LocationCreated event will tell the web browser the GPS coordinates in real time.
 

Conclusion

At this stage of the application you should have your front end logic complete and the controllers that power the back end. The last thing that has to be completed on the web end is the event and socket logic. If you haven’t already please subscribe not only to this blog but also my Youtube page (links to both on the right sidebar). Please leave any questions in the comment section below, and be sure to check the source code here.

Posted on Leave a comment

Jyrone Parker Live – Intro To Databases

[info]Check out the shop! [/info]

Intro To Databases

This live stream focuses on databases. A database is a container of imformation of sorts. It is what allows websites to be dynamic instead of static. Learn how to create a database, how to add tables to the database, how to update data in the database and how to delete data out of the database.
This stream is significant because it signifies the last beginner PHP live stream. Going forward we are creating PHP projects and building upon our knowledge set. Then I will go on and teach you guys some HTML/CSS/Javascript and move on to the Laravel framework.
As always if you haven’t yet subscribe to this blog using the widget on the right (bottom if on mobile) to get the latest updates from me.

Posted on Leave a comment

Jyrone Parker Live – Exceptions/Error Handling

[callaction url=”https://www.youtube.com/user/JPlaya01″ background_color=”#333333″ text_color=”#ffffff” button_text=”Go Now” button_background_color=”#e64429″]Subscribe To My Youtube Page[/callaction]
 

Exceptions and Error Handling

Continuing in the PHP tutorials I am showing you how to throw exceptions and handle errors in PHP. Often times when you are building software you have to account for when things go wrong. To ensure the best experience for your user, you want the application to gracefully handle those errors instead of crashing and leaving the user quizzically stumped. This is where exceptions come in. If you account for the cases where things might break and throw an exception then you can use the try/catch  control structure to catch said exceptions and handle them accordingly. A quick example is such:


<?php
function getRandomRange($min,$max){
if($min < 0){
throw new Exception("Min must be greater than 0");
}
if($max > 10000){
throw new Exception("Max must be less than 10000");
}
return rand($min,$max);
}
try{
/*
php exceptions.php $mix $max
*/
echo getRandomRange($argv[1],$argv[2]) . "\n";
}
catch(Exception $e){
echo $e->getMessage()."\n";
}
?>

Exceptions are part of the global PHP namespace and you can even create your own version on an exception that extends from the Exception class. If you didn’t get a chance to watch my live stream on the subject I talk about it more in depth, you can find it below
[youtube width=”100%” height=”100%” autoplay=”false”]https://www.youtube.com/embed/IkWK3qZ5Ix4[/youtube]

Posted on 3 Comments

Writing A Real Time Location Service – Views and Front End Logic

[callaction url=”https://www.youtube.com/user/JPlaya01″ background_color=”#333333″ text_color=”#ffffff” button_text=”Go Now” button_background_color=”#e64429″]Subscribe To My Youtube Page[/callaction]

Views

Thanks to the scaffolding done in the initial project setup, our login and registration views are already created, we just need to modify the home page and add a new page for device registration. Le’t create the activation view first, copy the home.blade.php file and name it activation.blade.php. This view will only have one form for taking in an activation code from an Android device replace the current contents with the following:


@extends('layouts.app')
@section('content')
<div class="container">
<div class="row">
<div class="col-md-8 col-md-offset-2">
<div class="panel panel-default">
<div class="panel-heading">Activate Device</div>
<div class="panel-body">
<form class="form-horizontal" role="form" method="POST" action="{{ url('/activate') }}">
{{ csrf_field() }}
<div class="form-group{{ $errors->has('name') ? ' has-error' : '' }}">
<label for="name" class="col-md-4 control-label">Code</label>
<div class="col-md-6">
<input id="name" type="text" class="form-control" name="code" value="{{ old('code') }}" required autofocus>
@if ($errors->has('name'))
<span class="help-block">
<strong>{{ $errors->first('activate') }}</strong>
</span>
@endif
</div>
</div>
<div class="form-group">
<div class="col-md-6 col-md-offset-4">
<button type="submit" class="btn btn-primary">
Activate
</button>
</div>
</div>
</form>.
</div>
</div>
</div>
</div>
</div>
@endsection

Don’t worry that the route hasn’t been created yet, we will get to that in a bit. In the mean time open up the home.blade.php file, this file is where the majority of the seen action is going to happen.  In this view the user will see a list of their devices with options to either start tracking GPS, stop tracking GPS, or delete the device. There is a Google map that will be used to show the position of the device and it will be updated in real time.


@extends('layouts.app')
@section('content')
<link rel="stylesheet" type="text/css" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css">
<style>
#map {
height: 400px;
width: 100%;
}
</style>
<script src="https://cdn.socket.io/socket.io-1.4.5.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.1/jquery.js"></script>
<div class="container">
<div class="row">
<div class="col-md-8 col-md-offset-2">
<div class="panel panel-default">
<div class="panel-heading">Dashboard</div>
<div class="panel-body">
<table class="table">
<tbody>
@foreach(auth()->user()->devices as $device)
<tr>
<td>{{$device->uuid}}</td>
<td>
<button data-id="{{$device->id}}" class="btn btn-sm btn-default action-start-gps" id="start-gps"><i class="fa fa-map-marker" aria-hidden="true"></i></button>
<button data-id="{{$device->id}}" class="btn btn-sm btn-warning action-stop-gps" id="stop-gps"><i class="fa fa-map-marker" aria-hidden="true"></i></button>
<form method="post" action="/device/{{$device->id}}">
<input type="hidden" name="_method" value="DELETE">
<input type="hidden" name="_token" value="{{ csrf_token() }}">
<button data-id="{{$device->id}}" type="submit" class="btn btn-sm btn-danger" id="delete"><i class="fa fa-close" aria-hidden="true"></i></button>
</form>
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-md-8 col-md-offset-2">
<div class="panel panel-default">
<div class="panel-heading">Map</div>
<div class="panel-body">
<div id="map"></div>
</div>
</div>
</div>
</div>
</div>
<script>
var startGps = document.getElementsByClassName("action-start-gps");
var stopGps = document.getElementsByClassName("action-stop-gps");
for (var i = 0; i < startGps.length; i++) {
startGps[i].addEventListener('click', function(){
$.get("http://gps.app/command/start-gps/" + $(this).data('id'), function(data, status){
}, false);
});
}
for (var i = 0; i < stopGps.length; i++) {
stopGps[i].addEventListener('click', function(){
$.get("http://gps.app/command/stop-gps/" + $(this).data('id'), function(data, status){
}, false);
});
}
function initMap(lat,long) {
var uluru = {lat: -25.363, lng: 131.044};
var map = new google.maps.Map(document.getElementById('map'), {
zoom: 20,
center: uluru,
});
var marker = new google.maps.Marker({
position: uluru,
map: map
});
var socket = io.connect('http://gps.app:6001');
socket.on('gps', function (data) {
var center = {lat:Number(data.data.gps.lat),lng:Number(data.data.gps.long)};
@foreach(auth()->user()->devices as $device)
socket.on({{$device->id}}, function(data){
console.log(data);
});
@endforeach
marker.setPosition(center);
// using global variable:
map.panTo(center);
})
}
</script>
<script async defer
src="https://maps.googleapis.com/maps/api/js?key=AIzaSyBCDNt1biVyfA8h-eCZyZ69CKS6NNBCeEQ&callback=initMap">
</script>
@endsection

Home screen
Your screen should look like this

This is where the majority of the magic happens in the home view there are two divs. In the first div all of the associate devices will be shown in a tabular format with options to start GPS tracking, stop GPS tracking, and deleting the associated device. In the second div there is a Google map that will be used to visualize our devices (in order to use Google map services you must register for a key). The javascript code calls the routes needed to start and stop GPS tracking, as well as the Socket.IO code needed to update the Google map in real time ( for those who don’t know Socket.IO please refer here). Let’s take a closer look at the Socket.IO code and understand what is going on. When new data comes through the websocket channel, the marker’s latitude and longitude are updated and the map is recentered all without a page reload. With this bagged up the next step is to set up the routes and controller logic!