Codementor Events

Laravel and Highcharts graph

Published Nov 02, 2021Last updated Apr 30, 2022
Laravel and Highcharts graph

Hello developers, In this article I am will show you how to display your data with highcharts graph. I am using Laravel/PHP for this.
I will use this graph:https://www.highcharts.com/demo/line-labels/grid-light
to show active and trial users of our database.
Lets start...
step1:
first let's get highchart library and include it in layouts/app.blade.php file of Laravel project.

 <script src="https://code.highcharts.com/highcharts.js"></script>

js.png

step2:
Let's make a controller and view file to show the graph.
To make controller I typed this command in terminal

php artisan make:controller ChartController

and make a view file inside
resources/views folder
let's give name as

graph.blade.php

and now will make a Route in web.php
This is my route

Route::get('/chart', [App\Http\Controllers\Chartcontroller::class, 'index']);

Now the last step is to make a index method in ChartController and return a view as below
ChartController.php

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class ChartController extends Controller
{
    public function index()
    {
        return view('graph');
    }
}

my graph.blade.php looks like this

@extends('layouts.app')
@section('content')
    <div class="container">
        <div class="row justify-content-center">
            <div class="col-md-12">

                <div class="card">
                    <div class="card-header">Active and Trial Users
                       
                    </div>
                    <div class="card-body">
                        graph
                    </div>
                </div>
            </div>
        </div>
    </div>

@endsection




and this is view page looks like: A simple page with graph
as a message
graph1.png

Step3:
Visit this link https://www.highcharts.com/demo/line-labels/grid-light and get all the code and paste it in your graph.blade.php with <script>tag as show below:

Make sure you have <div id="container></div>
to load graph in this div
graph.blade.php

@extends('layouts.app')
@section('content')
    <div class="container">
        <div class="row justify-content-center">
            <div class="col-md-12">

                <div class="card">
                    <div class="card-header">Active and Trial Users

                    </div>
                    <div class="card-body">
                       <div id="container>
                       //here we will load the chart
                       </div>
                    </div>
                </div>
            </div>
        </div>
    </div>
<script>
//highchart code here
</script>
@endsection



Finally our graph.blade.php should looks like this:
Here I have changed the graph title to Active and Trail users, title of y-axis to Users , name of series to Active and Trial and aasigned whole chart to variable const chart as below.

@extends('layouts.app')
@section('content')
    <div class="container">
        <div class="row justify-content-center">
            <div class="col-md-12">

                <div class="card">
                    <div class="card-header">Active and Trial Users

                    </div>
                    <div class="card-body">
                        <div id="container"></div>
                    </div>
                </div>
            </div>
        </div>
    </div>
    <script>
      const chart = Highcharts.chart('container', {
            chart: {
                type: 'line'
            },
            title: {
                text: 'Active and Trail users'
            },
            subtitle: {
                text: ''
            },
            xAxis: {
                categories: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep',
                    'Oct', 'Nov', 'Dec'
                ]
            },
            yAxis: {
                title: {
                    text: 'Users'
                }
            },
            plotOptions: {
                line: {
                    dataLabels: {
                        enabled: true
                    },
                    enableMouseTracking: false
                }
            },
            series: [{
                name: 'Active',
                data: [7.0, 6.9, 9.5, 14.5, 18.4, 21.5, 25.2, 26.5, 23.3, 18.3, 13.9,
                    9.6
                ]
            }, {
                name: 'Trial',
                data: [3.9, 4.2, 5.7, 8.5, 11.9, 15.2, 17.0, 16.6, 14.2, 10.3, 6.6, 4.8]
            }]
        });

    </script>
@endsection


Now if you view this graph in browser, it should look like this
graph2.png

Step4:
Now it's time to setup our database structure.I have all the users informations in userstable
My users table has id ,name,email,password,status,plan,createad_at,updated_at
Your table must have status and plan. Here status is the users status which can be either plan or trial/null. and plan is user premium plan such as "business plan","enterprises plan" etc.
so now we can easily determine whether user has active account ot trial account.
Active account if users status is active and user has not trail/null plan and
Trial account if user has trail/null plan

Step5:
In step4 I showed you users table. Now we will wrtite the logic to determine if the user is Active or Trail based on step4 scenario.

We will make scope in User.php file. If you are not familiar with scope then please check the laravel documentation. Laravel scopes
I will make two scopes. One is to get Active users and other one is to get Trial users

   
    /**
     * get all paid users
     */
    public function scopeAllPremiumUsers($query)
    {
        $premiumUser = $query->where('status', 'active')->where(function ($query) {
            $query->where([
                ['plan', '!=', 'trial'],
                ['plan', '!=', null]
            ]);
        });
        return $premiumUser;
    }
     /**
     * get all Trail users
     */
    public function scopeAllTrailUsers($query)
    {
        return $query->where('plan', 'trial')->orWhere('plan', null);
    }


Step6:
Now we will make two more routes in web.php
one route will return all active users and the other route will return all the trrial users

Route::get('/chart', [App\Http\Controllers\Chartcontroller::class, 'index']);
//New route
Route::get('/chart/active/users', [App\Http\Controllers\Chartcontroller::class, 'activeUsers']);
Route::get('/chart/trial/users', [App\Http\Controllers\Chartcontroller::class, 'trialusers']);

Step7:
Now we will work on our controller and we will implement the scopes we have defined in our User.php

Now here I want to hardcode date. Later we will make the date dynamic.
I want to show active and trail users of date2021-01-01 year because I have some active and trial users in this date.

Before that I can get trial users as
User::allTrailUser()->get();
and active users as
User::allPremiumUser()->get();

allTrailUser() and allPremiumUser() are the two methods which we have in User.php

These are the steps I am going to do now to get Active and Trial users in required format

    /**
     * get trial users (user with plan trial or null)
     * group by created_at date
     * count number of user based on month
     * start from index 1 to 12 for 12 month,fill with 0 values of particluar key(month)that doesn't exist in collection
     * sort arrays
     * convert collection to array
     */

I am grouping the users and counting number of users. In some case we may not have particular month of date in our database. for example for year 2021 . For example If don't have record for month March. So in this case I still have to show users of 12 months,not 11 months. So for march I will set number of users 0 and month index as 3(jan is 1,feb is 2,march 3,april 4.....Dec 12)
Which can be done by this

            union(array_fill(1, 12, 0))
            sortKeys()

Check Laravel collection

This are my two methods:

public function activeUsers()
 {
     $activeUser = User::allPremiumUser()->orderBy('created_at')->whereYear('created_at','2021')->get()
         ->groupBy(function ($date) {
             return $date->created_at->month;
         })
         ->map(function ($group) {
             return $group->count();
         })
         ->union(array_fill(1, 12, 0))
         ->sortKeys()
         ->toArray();
     return (array_values($activeUser));
 }

 public function trialUsers()
 {
     $trialUser = User::allTrailUser()->orderBy('created_at')->whereYear('created_at','2021')->get()
         ->groupBy(function ($date) {
             return $date->created_at->month;
         })
         ->map(function ($group) {
             return $group->count();
         })
         ->union(array_fill(1, 12, 0))
         ->sortKeys()
         ->toArray();
     return (array_values($trialUser));
 }
}



Now lets call this two methods in Index method of same controller

public function index()
   {
       $trialUserForChart = $this->trialUsers();
       $activeUserForChart = $this->activeUsers();
       return view('graph',compact('trialUserForChart','activeUserForChart'));
   }
   

Now our ChartController.php looks like this


<?php

namespace App\Http\Controllers;

use App\Models\User;
use Illuminate\Http\Request;

class ChartController extends Controller
{
    public function index()
    {
        $trialUserForChart = $this->trialUsers();
        $activeUserForChart = $this->activeUsers();
        return view('graph',compact('trialUserForChart','activeUserForChart'));
    }
    public function activeUsers()
    {
        $activeUser = User::allPremiumUser()->orderBy('created_at')->whereYear('created_at','2021')->get()
            ->groupBy(function ($date) {
                return $date->created_at->month;
            })
            ->map(function ($group) {
                return $group->count();
            })
            ->union(array_fill(1, 12, 0))
            ->sortKeys()
            ->toArray();
        return (array_values($activeUser));
    }

    public function trialUsers()
    {
        $trialUser = User::allTrailUser()->orderBy('created_at')->whereYear('created_at','2021')->get()
            ->groupBy(function ($date) {
                return $date->created_at->month;
            })
            ->map(function ($group) {
                return $group->count();
            })
            ->union(array_fill(1, 12, 0))
            ->sortKeys()
            ->toArray();
        return (array_values($trialUser));
    }
}


If you visit each route /chart/active/users or /chart/trial/users
it returns the arrays of 12 data for 12 months as shown in the picture. The image below shows trial users data.
trial.png

So as you see, the first record is of Jan, second is Feb and so on. O means we don't have record for that month. You may have differnet data.

Step8:
Now lets pass index method varibale $activeUserForChart and $trialUserForChart to graph.blade.php and convert it into JSON with json_encode

 json_encode($activeUserForChart);
 json_encode($trialUserForChart);

Now our graph.balde.php looks like below

@extends('layouts.app')
@section('content')
    <div class="container">
        <div class="row justify-content-center">
            <div class="col-md-12">

                <div class="card">
                    <div class="card-header">Active and Trial Users
                       
                    </div>
                    <div class="card-body">
                        <div id="container"></div>
                    </div>
                </div>
            </div>
        </div>
    </div>
    <script>
     const chart = Highcharts.chart('container', {
            chart: {
                type: 'line'
            },
            title: {
                text: 'Active and Trail users'
            },
            subtitle: {
                text: ''
            },
            xAxis: {
                categories: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep',
                    'Oct', 'Nov', 'Dec'
                ]
            },
            yAxis: {
                title: {
                    text: 'Users'
                }
            },
            plotOptions: {
                line: {
                    dataLabels: {
                        enabled: true
                    },
                    enableMouseTracking: false
                }
            },
            series: [{
                name: 'Active',
                data:  <?php echo json_encode($activeUserForChart); ?>
            }, {
                name: 'Trial',
                data:  <?php echo json_encode($trialUserForChart); ?>
            }]
        });

    </script>
@endsection

Based on this data my graph looks like below
finalgraph.png

Step9:
From this step we will make our chart dynamic. We will remove the hardcoded year and we will send the year from dropdown list and refresh the chart with the particular year data.
Let's make a dropdown in graph.blade.php . I want to show year from 2013 to cuurent year.

<select name="year" id="year" class="float-right border-5">
     @foreach (range(date('Y'), 2013) as $year)
      <option value="{{ $year }}">{{ $year }}</option>
      @endforeach
</select>

Now will make end point which will return the active and trial users.
in web.php let's register new route

Route::get('/chart/active-trial', [App\Http\Controllers\Chartcontroller::class, 'activeTrail']);

In Chartcontroller we will make a method that will return active and trial users for given year.
Lets assume we are reciving year form ajax request and we receive year in our method as request()->year

public function activeTrail()
    {
        $year = request()->year;
        return [
            'active'=>$this->activeUsers($year),
            'trial'=> $this->trialUsers($year)
        ];
    }

data.png

So now out activeUsers() and trialUsers() accepts year as parameter. So we have to modify our methods accordingly.
Our final ChartController looks as below.
Chartcontroller.php

<?php

namespace App\Http\Controllers;

use App\Models\User;
use Illuminate\Http\Request;

class ChartController extends Controller
{
  
    public function index()
    {
        $year = date('Y');
        $trialUserForChart = $this->trialUsers($year);
        $activeUserForChart = $this->activeUsers($year);
        return view('graph',compact('trialUserForChart','activeUserForChart'));
    }
    public function activeUsers($year)
    {
      
        $activeUser = User::allPremiumUser()->orderBy('created_at')->whereYear('created_at',$year)->get()
            ->groupBy(function ($date) {
                return $date->created_at->month;
            })
            ->map(function ($group) {
                return $group->count();
            })
            ->union(array_fill(1, 12, 0))
            ->sortKeys()
            ->toArray();
        return (array_values($activeUser));
    }

    public function trialUsers($year)
    {
    
        $trialUser = User::allTrailUser()->orderBy('created_at')->whereYear('created_at',$year)->get()
            ->groupBy(function ($date) {
                return $date->created_at->month;
            })
            ->map(function ($group) {
                return $group->count();
            })
            ->union(array_fill(1, 12, 0))
            ->sortKeys()
            ->toArray();
        return (array_values($trialUser));
    }
    public function activeTrail()
    {
        $year = request()->year;
        return [
            'active'=>$this->activeUsers($year),
            'trial'=> $this->trialUsers($year)
        ];
    }
}

In index() method I have $year = date('Y'); so when page loads we wil show the chart with recent year data

step10:
lets include ajax. Make sure you have jQuery library in your project. When user change the year from dropdown list we will send this year to our endpoint and grap the data of that particular year.

$(document).on('change', '#name', function() {

      var value = this.value;
      $.ajax({
          url: "/chart/active-trial",
          type: "GET",
          data: {
              year: value,
          },
          success: function(response) {
              
              chart.series[0].update({
                  data: response.active

              });
              chart.series[1].update({
                  data: response.trial
              });

          },
      });
  });

I have included this in graph.balde.php

@extends('layouts.app')
@section('content')
    <div class="container">
        <div class="row justify-content-center">
            <div class="col-md-12">

                <div class="card">
                    <select name="select" id="select" class="float-right border-5">
                        @foreach (range(date('Y'), 2013) as $year)
                            <option value="{{ $year }}">{{ $year }}</option>
                        @endforeach
                    </select>
                    <div class="card-header">Active and Trial Users
                       
                    </div>
                    <div class="card-body">
                        <div id="container"></div>
                    </div>
                </div>
            </div>
        </div>
    </div>
    <script>
      const chart=  Highcharts.chart('container', {
            chart: {
                type: 'line'
            },
            title: {
                text: 'Active and Trail users'
            },
            subtitle: {
                text: ''
            },
            xAxis: {
                categories: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep',
                    'Oct', 'Nov', 'Dec'
                ]
            },
            yAxis: {
                title: {
                    text: 'Users'
                }
            },
            plotOptions: {
                line: {
                    dataLabels: {
                        enabled: true
                    },
                    enableMouseTracking: false
                }
            },
            series: [{
                name: 'Active',
                data:  <?php echo json_encode($activeUserForChart); ?>
            }, {
                name: 'Trial',
                data:  <?php echo json_encode($trialUserForChart); ?>
            }]
        });

   
   
        $(document).on('change', '#name', function() {

        var value = this.value;
        $.ajax({
            url: "/chart/active-trial",
            type: "GET",
            data: {
                year: value,
            },
            success: function(response) {
                
                chart.series[0].update({
                    data: response.active

                });
                chart.series[1].update({
                    data: response.trial
                });

            },
        });
    });
    </script>
@endsection

Now you should able to display active trial users in graph for different date
year.png

Discover and read more posts from Ranjeet Karki
get started