Posted on Leave a comment

Setting Up A CI Laravel Pipeline Using Bitbucket

Continuous Integration

Is a must in today’s software architecture. Setting up a solid CI/CD pipeline will save you countless hours and money immediately and down the line.

There are plenty of tools that can accomplish your CI/CD goals from Jenkins, to CircleCI however in today’s tutorial I will be showing you how to accomplish this using Bitbucket pipelines.

Editing The YAML File

Bitbucket’s pipeline system uses yml files to spin up docker instances and run your scripts. This is an example file that runs composer, generates keys, runs migrations, installs passport and runs the tests. It also only runs when deploying to master branch and sets up a mysql instance and sets the environment keys.

# This is a sample build configuration for PHP.
# Check our guides at https://confluence.atlassian.com/x/e8YWN for more examples.
# Only use spaces to indent your .yml configuration.
# -----
# You can specify a custom docker image from Docker Hub as your build environment.
image: lorisleiva/laravel-docker
pipelines:
  branches:
    master:
      - step:
          caches:
            - composer
          script:
            - composer install --prefer-dist --no-ansi --no-interaction --no-progress --no-scripts
            - cp .env.example .env
            - php artisan key:generate
            - php artisan migrate
            - php artisan passport:install
            - vendor/bin/phpunit
          services:
            - mysql
      - step:
          name: Deploy to prod
          deployment: production
          # trigger: manual  # Uncomment to make this a manual deployment.
          script:
            - echo "Deploying to prod environment"
            - curl -X GET https://forge.laravel.com/servers/148653/sites/653797/deploy/http?token=4Q8e1kFPmFsHbxOBek7jcqikAvGVTbiX50tsPUPK
definitions:
    services:
      mysql:
        image: mysql:5.7
        variables:
          MYSQL_DATABASE: 'ben'
          MYSQL_RANDOM_ROOT_PASSWORD: 'yes'
          MYSQL_USER: 'homestead'
          MYSQL_PASSWORD: 'secret'

Why Is Continuous Integration Important?

I did an article about why continuous integration is important to the solo developer and I suggest everyone take a few moments and read that article. It outlines why having a solid CI/CD plan in your architecture, however the TL;DR is that is saves you time, money, effort and stress by spending the little bit of time up front to ensure you have tested and tried code before you ship to production.

Posted on Leave a comment

How To Create A Point Of Sales System With Laravel and Vue Pt. 2

Expanding Upon Our Point Of Sales App

In part one we created a point of sales system using Laravel and Vue (if you haven’t done part 1 here is the source code). It had very basic functionality but it got the job done, you could charge debit/credit cards. The basic design looked like this
Point of sales part 1 screenshot
In part two we will add email invoice functionality so that we can email our customers and they can pay without us having to know their account information. For this we will be using Laravel’s notification framework on the backend and utilizing the web payment request API with Vue.js on the front end.
 

The Invoice Model

In the command line we will create a new model to represent our invoices type in the following command to generate your model and migration

php artisan make:model -m Invoice

 
Once the model and migration are made, open up migration and edit accordingly:

<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateInvoicesTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('invoices', function (Blueprint $table) {
            $table->increments('id');
            $table->string('name');
            $table->longText('description');
            $table->integer('amount');
            $table->string('email');
            $table->string('charge_id');
            $table->boolean('is_paid')->default(false);
            $table->timestamps();
        });
    }
    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('invoices');
    }
}

As you can see there are only a few fields

  • name of person being invoiced
  • description of the invoice
  • amount to be invoiced
  • email address for invoice to be mailed to
  • stripe charge id
  • boolean indicating whether or not the invoice has been paid

Open up the Invoice.php model and add the following to the $fillable array

<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Notifications\Notifiable;
class Invoice extends Model
{
    use Notifiable;
    //
    protected $fillable = ['name','amount','email','description','charge_id'];
}

Next we need to create some api routes and a RESTful controller. Let’s start with the routes, open up routes/api.php and add the following routes

Route::middleware('auth:api')->resource('/invoice','InvoiceController');
Route::post('/invoice/pay/{id}','InvoiceController@payInvoice');

Open up routes/web.php and add the following

Route::get('/invoice/pay/{id}','InvoiceController@getPayInvoice');

Now create a new controller

php artisan make:controller --resource InvoiceController

Open up the controller and replace with the following

<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Invoice;
class InvoiceController extends Controller
{
  public function __construct(){
    \Stripe\Stripe::setApiKey(env('STRIPE_SECRET'));
  }
    /**
     * Display a listing of the resource.
     *
     * @return \Illuminate\Http\Response
     */
    public function index()
    {
        //
        $invoices = Invoice::where('is_paid',true)->get();
        return response()->json([
          'invoices' => $invoices
        ]);
    }
    /**
     * 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)
    {
        //
        $invoice = Invoice::Create($request->all());
        $invoice->notify(new \App\Notifications\InvoiceCreated());
        return response()->json([
          'invoice' => $invoice
        ]);
    }
    /**
     * Display the specified resource.
     *
     * @param  int  $id
     * @return \Illuminate\Http\Response
     */
    public function show($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)
    {
        //
        $invoice = Invoice::findOrFail($id);
        $invoice->fill($request->all())->save();
        return response()->json([
          'invoice' => $invoice
        ]);
    }
    /**
     * Remove the specified resource from storage.
     *
     * @param  int  $id
     * @return \Illuminate\Http\Response
     */
    public function destroy($id)
    {
        //
        return response()->json([
          'success' => Invoice::findOrFail($id)->delete()
        ]);
    }
    function getPayInvoice($id){
      return view('invoice')->with([
        'invoice' => Invoice::findOrFail($id)
      ]);
    }
    function payInvoice(Request $request,$id){
      $invoice = Invoice::findOrFail($id);
      try {
        // Use Stripe's library to make requests...
        $token = \Stripe\Token::create(array(
          "card" => array(
            "number" => $request->details['cardNumber'],
            "exp_month" => $request->details['expiryMonth'],
            "exp_year" => $request->details['expiryYear'],
            "cvc" => $request->details['cardSecurityCode']
          )
        ));
        \Stripe\Charge::create(array(
          "amount" => $invoice->amount,
          "currency" => "usd",
          "source" => $token, // obtained with Stripe.js
          "description" => $invoice->description,
          "receipt_email" => $invoice->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());
      }
    }
}

Most of the CRUD operations are simple, in the create method we are calling a notification on the invoice (we will create the notification next don’t worry), and we have the charge method which is really just copy and paste from the other charge method except the amount comes from the invoice.
The last thing needed on the back end is the notification. Let’s go ahead and create that.

php artisan make:notification InvoiceCreated

In the notification file we just send an email with a link to pay

<?php
namespace App\Notifications;
use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Notification;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\MailMessage;
class InvoiceCreated extends Notification
{
    use Queueable;
    /**
     * Create a new notification instance.
     *
     * @return void
     */
    public function __construct()
    {
        //
    }
    /**
     * Get the notification's delivery channels.
     *
     * @param  mixed  $notifiable
     * @return array
     */
    public function via($notifiable)
    {
        return ['mail'];
    }
    /**
     * Get the mail representation of the notification.
     *
     * @param  mixed  $notifiable
     * @return \Illuminate\Notifications\Messages\MailMessage
     */
    public function toMail($notifiable)
    {
        return (new MailMessage)
                    ->subject('You Have A New Invoice Due')
                    ->line('Pay NOW or suffer the consequences!')
                    ->action('Pay UP!', url('/invoice/pay/'.$notifiable->id))
                    ->line('Thank you for using our application!');
    }
    /**
     * Get the array representation of the notification.
     *
     * @param  mixed  $notifiable
     * @return array
     */
    public function toArray($notifiable)
    {
        return [
            //
        ];
    }
}

The Front End

We are going to create 2 components and a new page so people can pay their invoice. Create a new component in your vue application name it InvoiceComponent and replace with the following

<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 Invoice</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"  min="1.00" max="10000.00" id="amount" placeholder="Amount To Charge" v-model="amount">
                        </div>
                      </div>
                      <div class="form-group">
                      <label class="col-sm-3 control-label" for="name">Name</label>
                      <div class="col-sm-9">
                        <input type="text" class="form-control"  id="name" placeholder="Name To Charge" v-model="name">
                      </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">
                        <div class="col-sm-offset-3 col-sm-9">
                          <button type="button" class="btn btn-success" v-on:click="createInvoice">Create Invoice</button>
                        </div>
                      </div>
                        </fieldset>
                    </div>
                </div>
            </div>
        </div>
    </div>
</template>
<script>
    export default {
        mounted() {
            console.log('Component mounted.')
        },
        data(){
        return{
        name: null,
        amount: null,
        email: null,
        description: null,
        invoices: []
        }
        },
        created(){
          var that = this;
          axios.get('/api/invoice').then(function(data){
            that.invoices = data.data.invoices;
          });
        },
        methods: {
        createInvoice: function(){
        var that = this;
        axios.post('/api/invoice',{name: this.name, amount: this.amount * 100, description: this.description, email: this.email})
        .then(function(data){
          that.invoices.push(data.data.invoice);
        alert('Success!')
        }).catch(function(error){
        alert(error.message);
        });
        }
        }
    }
</script>

Drop that new component in your home.blade file and your page should look like this

Create another component call it InvoicePayController and place the following

<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 Invoice</div>
                    <div class="panel-body">
                        Your total amount due is {{invoice.amount / 100}}
                        <br>
                        {{invoice.description}}
                        <br>
                        <button v-on:click="payInvoice()" class="btn btn-primary">Pay</button>
                    </div>
                </div>
            </div>
        </div>
    </div>
</template>
<script>
    export default {
        mounted() {
            console.log('Component mounted.')
        },
        data(){
        return{
          invoice: null,
          paymentRequest: null
        }
        },
        created(){
          this.invoice = JSON.parse(this.invoiceObject);
        },
        methods: {
        payInvoice: function(){
          const supportedPaymentMethods = [
            {
              supportedMethods: 'basic-card',
            }
          ];
          const paymentDetails = {
            total: {
              label: this.invoice.description,
              amount:{
                currency: 'USD',
                value: this.invoice.amount/100
              }
            }
          };
          // Options isn't required.
          const options = {};
          this.paymentRequest = new PaymentRequest(
            supportedPaymentMethods,
            paymentDetails,
            options
          );
          var that = this;
          this.paymentRequest.show().then(function(data){
            axios.post('/api/invoice/pay/'+that.invoice.id,data).then(function(data){
              alert('Success');
              return paymentResponse.complete();
            })
          }).catch(function(err){
            console.log(err);
            return paymentResponse.complete();
          });
        },
      },
      props: ['invoiceObject']
        }
</script>

We are making use of the Payment Request API to gather all user card data to simplify the checkout process, you can read up more about it here. Create a new blade view file and call it Invoice.blade.php and put this new component there


@extends('layouts.app')
@section('content')
@endsection


Now your customers can pay invoices sent to them via email! Please don’t forget to subscribe to my Youtube Channel and like the video and post! Also don’t forget if you want to support this blog pre-orders for my new e-book are open now until the end of month!

Posted on 44 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 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

Writing A Real Time Location Service – Models and Migrations

[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]

Overview

In this programming series I am going to show you how to create a real time Laravel app that shows the location of an android device on a Google map in real time. This design can easily be applied to iOS and I will cover it at a later date. This project will be constructed of three parts:

  • Laravel app that handles registration/login, saves coordinates and timestamps
  • Node Application that talks to Laravel application and android devices
  • Android application that talks to node application

In this post I will create the Laravel + Node apps and I will write the Android app at a later post. The Laravel app is a run down version of my new application SocketDroid, an application that allows you to control android devices from a web panel. click a button and alert the Android device to send geolocation information back to the server and push it to the browser. The application will store the geolocation information in a database for later analysis if you so choose (in a later post I will expand upon the admin panel). The Laravel app will communicate with the Node app via Redis using the built-in Event system. From there the Node app will communicate with our devices via Socket.io. The Android device will listen on a websocket connection in a background service and listen for an event to start grabbing and posting GPS data, and another event to stop listening. Make sense so far? Good, let’s get started!
 

Scaffolding The Laravel Application

If you have followed any of my Laravel tutorials in the past, you know the drill, create a new application, and scaffold the authentication routes and views:


laravel new real-time-gps
cd real-time-gps
php artisan make:auth

Now we have a basic Laravel application with registration and login logic established. Let’s talk about database design for a moment shall we? This application is going to have 3 really important tables they are:

  • Users (Already made for us be default)
  • Devices
  • Locations
  • ActivationCodes

A user can have many devices, a device can have many locations and belongs to one user, and a location belong to one device, that is the basic relationship structure. Activation codes are for you guessed it activating devices to use the app. Go ahead and create the relations and models for devices and locations using the following commands:


php artisan make:model -m Device
php artisan make:model -m Location
php artisan make:model -m ActivationCode

Device Model

Open up the devices migration file and add the following content to the up() function:


Schema::create('devices', function (Blueprint $table) {
$table->increments('id');
$table->string('uuid');
$table->integer('user_id')->unsigned();
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
$table->timestamps();
});

The only things we need to know about devices are who they belong to and what channel they are listening on. We also want to make sure that when a user account is deleted from the database, that all of their devices are also removed from the database.  Now open up the Device model (app/Device.php), there are three things we need to do with this model, the first is tell it which parameters to accept when inserting into the database. The second thing is to tell the model about its relation to the User model, while the third is to tell the model about it’s relation to the Location model. The finished version should look like this:


<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Device extends Model
{
//
protected $guarded = [];
public function user(){
return $this->belongsTo('App\User');
}
public function locations(){
return $this->hasMany('App\Location');
}
}

The Device model and migration is now complete, now on to the Location model and migration.

Location Model

Open up the location migration file and in its up() function we are going to add the following:


Schema::create('locations', function (Blueprint $table) {
$table->increments('id');
$table->string('lat');
$table->string('long');
$table->integer('device_id')->unsigned();
$table->foreign('device_id')->references('id')->on('devices')->onDelete('cascade');
$table->timestamps();
});

Hopefully by now you understand what is going on, we are saving the lat/long acquired from the device, as well as its id. Using this information we can associate geolocations with independent devices. Next we will open up the Location(app/Location.php) model, tell it which properties to fill, and it’s relation to the Device model.


<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Location extends Model
{
//
public $guarded = [];
public function device(){
return $this->belongsTo('App\Device');
}
}

User Model

The only thing we need to do the user model is make it aware of it’s new device relation open up the User(app/User.php) model and add the following function:


public function devices(){
return $this->hasMany('App\Device');
}

ActivationCode Model

The activation code model is responsible for storing the activation code given from the Android device on app installation to register the device with your account. It will take in the UUID of the device and the activation code, open up the migration file and add the following:


public function up()
{
Schema::create('activation_codes', function (Blueprint $table) {
$table->increments('id');
$table->string('uuid');
$table->string('code');
$table->timestamps();
});
}

Now open up the ActivationCode(app/ActivationCode.php) model and add the following:


<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class AuthCode extends Model
{
//
public $guarded = [];
}

That’s all that’s needed you can now run your migrations!


php artisan migrate

In the next lesson we will be creating the views and front end logic needed to visualize the devices on a Google map!

Posted on Leave a comment

Get Your Daily Stripe Balance Using Laravel Task Scheduler

[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]

Laravel Task Scheduler

I create many apps that use the Uber business model for paying out contractors using Stripe, and I make money by using application fees. I like to get my daily account available and pending balances from Stripe. Using Laravel’s built in task scheduler this is trivial. Laravel provides a way to define all of your application’s scheduled jobs within Laravel itself, so that you only have to manage one cron tab * * * * * php /path/to/artisan schedule:run >> /dev/null 2>&1 it’s pretty awesome!

Install Dependencies

I will assume you have created a new Laravel install. I  need the Stripe PHP composer module for this to work. Go to the root of your application and type the following command composer require stripe/stripe-php now you have all the necessary components installed.

Define Schedule

All schedules are defined in schedule() method of the App\Console\Kernel located at app/console/kernel.php. I want to make a stripe call to retrieve my balance, both available and pending.

<?php
namespace App\Console;
use Mail;
use Carbon\Carbon;
use DB;
use Illuminate\Console\Scheduling\Schedule;
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
class Kernel extends ConsoleKernel
{
    /**
     * The Artisan commands provided by your application.
     *
     * @var array
     */
    protected $commands = [
        \App\Console\Commands\Inspire::class,
    ];
    /**
     * Define the application's command schedule.
     *
     * @param  \Illuminate\Console\Scheduling\Schedule  $schedule
     * @return void
     */
    protected function schedule(Schedule $schedule)
    {
        $schedule->call(function () {
        \Stripe\Stripe::setApiKey(env('STRIPE_SECRET'));
       $balance = \Stripe\Balance::retrieve();
       $emailText = "As of {Carbon::today()->toDayDateTimeString()}  your available balance is {$balance->available->amount} and your pending balance is {$balance->pending->amount}";
Mail::raw('Text to e-mail', function ($message) {
    //
    
 $message->from('financial@app.com', 'Your Application');
            $message->to(env('EMAIL_ADDRESS'),env('EMAIL_NAME'))->subject('Your Daily Stripe Balance!');
}); })->daily();  } }

With this piece of code I am now getting daily emails with the date, my available balance, and my pending balance. If you liked this please share and subscribe to my blog posts via email to the right!

Posted on Leave a comment

API Authentication In Laravel 5.2

[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]

Hours Saved!

Token based API authentication is something you are inevitably going to encounter if you plan on working with web/mobile apps (unless you are coding under a rock). The benefits are great, in fact here are six of them; However it can be a nightmare if you need to create your own token authentication server. As of Laravel 5.2, there is a new auth guard called conveniently api this guard allows us to check an api_token parameter against an api_token field on our users table in our database.

API Configuration

Almost everything is configured right out of the box, open up config/auth.php and you will see this block of code

  /*
 |--------------------------------------------------------------------------
 | Authentication Guards
 |--------------------------------------------------------------------------
 |
 | Next, you may define every authentication guard for your application.
 | Of course, a great default configuration has been defined for you
 | here which uses session storage and the Eloquent user provider.
 |
 | All authentication drivers have a user provider. This defines how the
 | users are actually retrieved out of your database or other storage
 | mechanisms used by this application to persist your user's data.
 |
 | Supported: "session", "token"
 |
 */
 'guards' => [
 'web' => [
 'driver' => 'session',
 'provider' => 'users',
 ],
 'api' => [
 'driver' => 'token',
 'provider' => 'users',
 ],
 ],

By default Laravel 5.2 uses the web guard, which is the traditional session based model. This model is great for a traditional web application but not suitable for API architecture. The next guard is the api this guard is suitable for routes that need to be consumed by mobile apps and the like. The reason why you would want to do this is because when you are making request from your client-side app, each request is mutually exclusive of the requests before and after it, thus each one requires authentication. In the traditional session based flow, user data is saved on the server and that session data is used in every request, not only is this approach not scalable, it isn’t efficient or as secure as one might think. API token authentication is faster, scalable, and more secure. The biggest benefit of all is that you can now use this token in other applications to access your own API if you so choose. If you would like to see an example leave a comment of what kind of app you would like to see this implemented in and I will write it, record it, and blog it!

Posted on Leave a comment

Learning Laravel – Installation

[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]

Getting Started Learning Laravel

So you have decided that learning Laravel is worth your time, GOOD FOR YOU! Now you have to go through the task of actually installing and configuring the software on your machine. If you use Laravel Homestead, then all of your system requirements are handled out of the box (I highly recommend this approach), if you choose not to use the VM, make sure your machine satisfies the following requirements:

  • PHP >= 5.5.9
  • OpenSSL PHP Extension
  • PDO PHP Extension
  • Mbstring PHP Extension
  • Tokenizer PHP Extension

Utilizing Composer

Laravel leverages Composer for all of it’s PHP dependency management. You may ask what is dependency management? Laravel utilizes a plethora of different components in order to make it function. It would be a nightmare if you as the developer is left in charge on managing all of these dependencies, with updates and patches and whatnot. This is where Composer comes in and makes your life pleasant. Composer keeps everything up-to-date, if a package requires other packages then Composer will handle all of the downloads. It’s like gem for Ruby or NPM for Node. If you don’t already have it installed it is quite easy, go to your command line and enter the following command.

curl -sS https://getcomposer.org/installer | php
mv composer.phar /usr/local/bin/composer

This will install composer globally so you can just type

composer <command>

if you run into any errors, then try running with the sudo command.
The first thing that we will do is install the Laravel installer with composer:

composer global require "laravel/installer"

Before you do anything else, make sure you put ~/.composer/vendor/bin in your PATH . Now you can start a new Laravel application by issuing the command

laravel new <app> 

doing so will create a new Laravel app creating a folder in the process where is your application’s name.