Charts in Laravel 5.1 with Morris.js

Access all tutorials in sprocket icon.

August 22, 2015 from author Bill Keck.

Charts in Laravel 5.1

Charts are great way to spice up UI and the Morris.js Chart is fairly easy to implement, so that is what we are going to use for our lesson.

This tutorial builds on the following tutorials:

You don’t need to use a Morris.js chart with our Widget model, but it does make it easy to follow along for the data formatting part.

Part 1. The Chart

We’re going to build a chart for our index page in our Widget model, which is just a generic model that I’m using in my tutorials. We have a Widget model, a WidgetController and corresponding views, including an index view and a search results view named resultsindex.

This means we can have a chart for all records, which are displayed on the index view and also a chart for specific search results, which are displayed on the resultsindex view.

We’re going to start with the views and work our way backwards. Let’s look at our views/widget/index view:



@extends('layouts.master')

@section('title')

    <title>The Widget Page </title>

   @endsection

@section('css')

    <link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/morris.js/0.5.1/morris.css">

@endsection

@section('content')

   {!! Breadcrumb::withLinks(['Home' => '/', 'Widgets' => '/widget']) !!}

   @include('widget.noresults')

    <br>

   @include('widget.searchform')

   @if(isset($results))

       @include('widget.searchresults')

       @else

        <h1>Widget Stats </h1>

        // insert a div id with a value of chart

        <hr>

       @include('widget.allrecords')

       @endif

@endsection

@section('scripts')

    //cdnjs.cloudflare.com/ajax/libs/raphael/2.1.0/raphael-min.js
    //cdnjs.cloudflare.com/ajax/libs/morris.js/0.5.1/morris.min.js

    

       function ConfirmDelete()
       {
           var x = confirm("Are you sure you want to delete?");
           if (x)
               return true;
           else
               return false;
       }

    
    
       var data =  ;

       Morris.Line({
           element: 'chart',
           data: data,
           xkey: 'year',
           ykeys: ['count'],
           labels: ['widgets created']
       });
    

@endsection

Note: Once again wordpress is chomping my code. So you have to insert a div id with a value of chart where I have indicated // insert a div id with a value of chart.

It’s also missing the opening and closing script tags within the @section(‘scripts’), so you will have to add those as well.

I have created a gist with the full code for reference and convenience to copy.

Also note that if you are not using the Bootstrapper package, then chop out:

{!! Breadcrumb::withLinks([‘Home’ => ‘/’, ‘Widgets’ => ‘/widget’]) !!}

Ok, let’s look at the parts that are specific to the Morris Chart implementation. We’ll start with pulling in the css through CDN:



@section('css')

    <link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/morris.js/0.5.1/morris.css">

@endsection

Since we have an @yield(‘css’) on our Master page, the contents inside the @section(‘css’) tags will be injected, so we have our css.

Next we have within our @section(‘content’), our h1 tag and chart div:



 <h1>Widget Stats </h1>

        // insert a div id with a value of chart

        <hr>

Note: Once again wordpress is chomping my code. So you have to insert a div id with a value of chart where I have indicated // insert a div id with a value of chart.

Here again is the gist with the full code for reference and convenience to copy.

I use a simple <hr> to separate from the rest of the content, so nothing fancy about that. Our chart div is where the actual chart will go. You can see I’m specifying height.

Next we have our @section(‘scripts’), which is where we will bring in the morris.js files via CDN:



@section('scripts')

    //cdnjs.cloudflare.com/ajax/libs/raphael/2.1.0/raphael-min.js
    //cdnjs.cloudflare.com/ajax/libs/morris.js/0.5.1/morris.min.js

Then we have a script for our alert on the delete button, which has nothing to do with chart so I’m going to skip it. That brings us to the script for the chart:



    
       var data =  ;

       Morris.Line({
           element: 'chart',
           data: data,
           xkey: 'year',
           ykeys: ['count'],
           labels: ['widgets created']
       });
    

@endsection

Don’t forget to add the opening and closing script tags.

You can see we are setting var data the result of json encoded $chartData. Before we encode it, it looks like this:



array:4 [▼
  0 => array:2 [▼
    "year" => "2012"
    "count" => "3"
  ]
  1 => array:2 [▼
    "year" => "2013"
    "count" => "1"
  ]
  2 => array:2 [▼
    "year" => "2014"
    "count" => "3"
  ]
  3 => array:2 [▼
    "year" => "2015"
    "count" => "45"
  ]
]

This is what it looks like after json encoding:



[{"year":"2012","count":"3"},
{"year":"2013","count":"1"},
{"year":"2014","count":"3"},
{"year":"2015","count":"46"}]

So obviously our $chartData is returning year and the count of widgets created within that year. You make a chart from any data you like, you just need to format it correctly in json format.

Next we have the config:



 Morris.Line({
           element: 'chart',
           data: data,
           xkey: 'year',
           ykeys: ['count'],
           labels: ['widgets created']
       });

Morris.Line will display the results as a line chart, while Morris.Bar will display it as bar chart. We are assigning the results to the chart element, which in our implementation is a div id.

Next we are using the data variable that we created as the source of the data. Then we assign the year key to the xkey and the count to ykeys. We designate the labels to say widgets created.

And that’s pretty much all you need on the morris.js side of the equation, which makes this so easy to work with. For those of you who know how to do the queries on your model to produce the $chartData results, you probably don’t need to continue.

One thing to note however is that the Morris.Line implementation, as opposed the .Bar implementation, requires all data to be of type string. Since I’m not a javascript wizard, I did the conversion before encoding $chartData. You can handle that however you wish.

The implementation resultsindex view is exactly the same as for index, so just repeat the steps from above, if you are following along with the Widget model and views. Since it is the same, you can also extract it out to a partial, but I will leave that up to you to implement.

Part 2. Building a Chart Helper

So the second part of this tutorial deals with extracting the $chartData from the model. This got a little complicated because of the formatting issues, where the year has to be a string, which made it bloat the controller.

Because of that, I extracted it out to a helper class. Basically, I wanted to be able to get the chart data with a single line in the controller:



$chartData = $chart->chartDataFromAllRecords();

To be able to use $chart, which is an instance of our WidgetChart class, I inject the instance into the method like so:



public function index(WidgetChart $chart)
{

One of the really cool features in Laravel 5.1 is that it will instantiate the instance for you, as long as you have included the use statement:



use App\Charthelpers\WidgetChart;

So there is no need to new up an instance of the class, Laravel does it for you.

For reference, I’m going to give you the entire index method from the WidgetController. This is so if you are following along from the earlier tutorials, you can see exactly how I use it:



public function index(WidgetChart $chart)
{

  $widgets = Widget::paginate(10);

   $count =  DB::table('widgets')->count();

  if (session()->has('message')){

      Session::flash('noResults', 'Sorry, we found 0 results');
   }

   $chartData = $chart->chartDataFromAllRecords();


   return view('widget.index', compact('widgets', 'count', 'chartData'));
}

So just 3 variables sent to the view, a paginated instance of Widget, the count of all records, and $chartData.

If you haven’t been following the other tutorials and this doesn’t make sense to you, it’s ok. The important part is how we are using our instance of WidgetChart to send along $chartData.

Ok, since we have not yet created the helper class, let’s do it now. Within your app folder, create a folder named CharHelpers. Inside the ChartHelpers folder, create a php file named ChartHelper.php, with the following contents:



 <?php
namespace App\ChartHelpers;

use App\Widget;
use DB;

class WidgetChart
{

   public function chartDataFromAllRecords()
   {

       $yearCounts = Widget::select(DB::raw('year(created_at) as year'),
           DB::raw('count(widget_name) as `count`'))
           ->groupBy('year')
           ->having('year', '>', '2011')->get();

       $chartData = $this->formatResultOf($yearCounts);

       return $chartData;

   }

   public function chartDataFrom($searchTerm)
   {

           $yearCounts = Widget::select(DB::raw('year(created_at) as year'),
               DB::raw('count(widget_name) as `count`'))
               ->where('widget_name', 'LIKE', '%'. $searchTerm .'%')
               ->groupBy('year')
               ->get();

           $chartData = $this->formatResultOf($yearCounts);

           return $chartData;

   }

   protected function convertToString($results)
   {
       foreach ($results as $key => $val)
       {

           foreach ($val as $k => $v)
           {


               if (is_numeric($v))
               {

                   $results[$key][$k] = (string) $v;

               }

           }
       }

       return $results;
   }

   /**
    * @param $yearCounts
    * @return mixed
    */
   private function formatResultOf($yearCounts)
   {
       $results = $yearCounts->toArray();

       $chartData = $this->convertToString($results);

       return $chartData;
   }


}

You can see our namespace declaration, use statements, and class declaration:



namespace App\ChartHelpers;

use App\Widget;
use DB;

class WidgetChart
{

First we have our chartDataFromAllRecords method:

public function chartDataFromAllRecords()
   {

       $yearCounts = Widget::select(DB::raw('year(created_at) as year'),
           DB::raw('count(widget_name) as `count`'))
           ->groupBy('year')
           ->having('year', '>', '2011')->get();

       $chartData = $this->formatResultOf($yearCounts);

       return $chartData;

   }

We call the select method on the Widget model and then we are using DB::raw, so we can use raw sql to get what we need. In this case, we want the created_at date returned in a year format, labeled as year, and we also want a count of widgets, and we group everything by year. We also set a condition of having year > 2011, which means any widgets before 2011 will not be included. I figured I would throw in an example of that as an extra tip. The results get assigned to $yearCounts.

Next we use our formatResultOf method to format $yearCounts and assign it to $chartData, which we then return. Two things have to happen, first we need the results in an array, second, we need the year to be a string. So inside the formatResultOf method:



private function formatResultOf($yearCounts)
{
   $results = $yearCounts->toArray();

   $chartData = $this->convertToString($results);

   return $chartData;
}

We format the $yearCounts object as an array and assign it to the variable $results. Then we feed $results into our convertToString method:



protected function convertToString($results)
{
   foreach ($results as $key => $val)
   {

       foreach ($val as $k => $v)
       {


           if (is_numeric($v))
           {

               $results[$key][$k] = (string) $v;

           }

       }
   }

   return $results;
}

Don’t you just love nested foreach loops? In this case, $val is an array, so to get to our year, we need to check if $v is_numeric, and if so, cast it to a string, using (string). I always get tripped up on the array syntax, but this is correct:



$results[$key][$k] = (string) $v;

So with each pass, it puts the value into the array as a string. Then we simply return the array as $results.

Getting back to our formatResultsOf method, which uses the convertToString method as follows:



$chartData = $this->convertToString($results);

We now have our $chartData in a format that can be converted to json, so we simply return it:



return $chartData;

So that covers what we need when we want to return all results from the model for the chart, but in our case, we also have a second method for returning the results of a search, which takes in $searchterm as a parameter:



public function chartDataFrom($searchTerm)
{

       $yearCounts = Widget::select(DB::raw('year(created_at) as year'),
           DB::raw('count(widget_name) as `count`'))
           ->where('widget_name', 'LIKE', '%'. $searchTerm .'%')
           ->groupBy('year')
           ->get();

       $chartData = $this->formatResultOf($yearCounts);

       return $chartData;

}

You can see the only difference is that we have the where clause:



->where('widget_name', 'LIKE', '%'. $searchTerm .'%')

If you are following along with the WidgetController, I have made a gist for the search method, so you can see how we have used the chartDataFrom method.

That concludes the WidgetChart class. You can see this ended up being more than what we would want in our controller, and formatting results is not the responsibility of the controller.

We could have gone even further by coding to contract instead of a concrete class. We could have set up a contracts folder within the ChartHelpers folder, and then within that made a ChartHelpersContract.

If you would like to go in that direction, check out my tutorial on how to setup a service provider, and implement a service provider for the ChartHelpersContract.

In my case, I didn’t feel it was necessary for my application, but you should decide what is right for you.

I hope you have enjoyed this tutorial and found it useful. Click on the sprocket icon at the top of the page to see all tutorials.

Please comment, share, and like if you can, thanks!

I don’t have a donate button, but If you would like to support my work, you can do so by buying one of my 99¢ books, I really appreciate it.

5 thoughts on “Charts in Laravel 5.1 with Morris.js

  1. Robert Santos says:

    Good day Bill

    Appreciate what you have been doing. I’m just curious from Yii to Laravel what datagrid are you using on Laravel 5.1?

    Thanks,
    Robert

    Like

  2. Robert Santos says:

    Hi Bill,

    sorry.. my mistake.

    I was asking for the widget. In Yii we use the GRIDVIEW but in Laravel what would you suggest as an alternative.

    Regards,
    Robert

    Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s