KodeInfo | Learning resources for web and mobile development

Generate Charts on Server Side using Highcharts

October 8th, 2014 18:37:34 in Routing , Views by Imran Iqbal Comments(0) - Views(15341)
Tags : highcharts, laravel highcharts, serverside highcharts, phantomjs highcharts, laravel tutorials, php laravel

Hi Friends ,

First do a fresh install of laravel and  i will show you how to generate charts using Highcharts at server side . Why and when you generate charts at server side .

  • you want to include your charts in emails or automated management reports
  • you want to have a consistency between graphs you present on your website and your backend produced reports
  • you want to show charts on your mobile device
 
 

 

We need to emulate browser environment on server to run Highcharts and we can do that using PhantomJs . PhantomJS, a headless WebKit with JavaScript API. The Phantom process takes our highcharts-convert.js script as an argument plus command line parameters. With the command line parameters we pass over the Highcharts configuration, the name of the output file and parameters for the graphical layout . 

$phantomjs highcharts-convert.js -infile $options -constr StockChart -outfile $file_name -scale 2.5 -width 800

Configuration

Our options file will be dynamic we will fetch data from server and write to this file , other options are below

-infile    The file to convert, the script have to find if this is a javascript file with a options object or a svg file.  It checks the input file for beginning with "<svg", "<?xml" or "<!doctype". Then it's a svg file, otherwise it's presumed to be an options file.


-constr  The constructor name. Can be one of Chart or StockChart. This depends on whether you want to generate Highstock or basic Highcharts.


-outfile  The file to output. Must be a filename with the extension .jpg, .png .pdf or .svg. 


-scale  To set the zoomFactor of the page rendered by PhantomJs. For example, if the chart.width option in the chart configuration is set to 600 and the scale is set to 2, the output raster image will have a pixel width of 1200. So this is a convenient way of increasing the resolution without decreasing the font size and line widths in the chart. This is ignored if the -width parameter is set.


-width  Set the exact pixel width of the exported image or pdf. This overrides the -scale parameter.


We will put some of our constants in config file . This file will have path for highcharts-convert.js , options file path , type of chart and output file path

<?php

return [

    'phantomjs' => public_path('charts/phantomjs'),

    'highcharts_convert' => public_path('charts/highcharts-convert.js'),

    'infile' => public_path('charts/images/options.json'),

    'constr' => 'StockChart',

    'outfile' => public_path('charts/images/test.png'),

];

If you are using windows then download phantomjs and give absolute path to phantomjs like below

<?php

return [

    'phantomjs' => 'D:\phantomjs\phantomjs',

    'highcharts_convert' => public_path('charts/highcharts-convert.js'),

    'infile' => public_path('charts/images/options.json'),

    'constr' => 'StockChart',

    'outfile' => public_path('charts/images/test.png'),

];

Now create a directory inside public folder as charts and copy all files from project on github to your project . Open public/charts/highcharts-convert.js and check if paths are correct for jquery , highcharts.js and highcharts-more.js .

Creating Command for Highcharts

Create a new command using artisan

php artisan command:make GenerateCharts

Change the command name in your new generated class to charts:generate and inject ChartsRepo which we are going to create in next step . Now when you execute php artisan charts:generate fire method will be executed so we will run our execute from ChartsRepo in fire method like below .

<?php

use Illuminate\Console\Command;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputArgument;

class GenerateCharts extends Command {

	/**
	 * The console command name.
	 *
	 * @var string
	 */
	protected $name = 'charts:generate';
	protected $chartsRepo ;

	/**
	 * The console command description.
	 *
	 * @var string
	 */
	protected $description = 'Generate Server Side Charts.';

	/**
	 * Create a new command instance.
	 *
	 * @return void
	 */
	public function __construct(KodeInfo\ChartsRepo $chartsRepo)
	{

        $this->chartsRepo = $chartsRepo;

		parent::__construct();
	}

	/**
	 * Execute the console command.
	 *
	 * @return mixed
	 */
	public function fire()
	{
        $this->chartsRepo->generate();
	}

}

Creating Repository to handle Command

Create a new folder inside app and name it KodeInfo , create a new class inside KodeInfo and name it ChartsRepo . We dont need to inject filesystem and config inside construct since we have only one method at present . In our generate method we are getting data from analytics table and creating a json string to write to options file .

$this->config = \Config::get('charts');

        $this->filesystem = new \Illuminate\Filesystem\Filesystem();

        //Get data and create options file
        $rows = DB::table("analytics")->get();

        $result = "";

        $start_fake_json = "{
            series: [{
                data: [";

        $result .= $start_fake_json;

        for($i = 0; $i < sizeof($rows); $i++) {

            $dd = Carbon::parse($rows[$i]->created_at);
            $rows[$i]->date = $dd->timestamp * 1000;

            if(is_null($rows[$i]->visits)){
                $result .= "[{$rows[$i]->date},null]";
            }else{
                $result .= "[{$rows[$i]->date},{$rows[$i]->visits}]";
            }

            if ($i != (sizeof($rows) - 1)) {
                $result .= ",";
            }
        }

        $end_fake_json = "],
                type: 'areaspline',
                color: '#3cf',
                threshold: null,
                fillColor:
                {
                   linearGradient: {
                        x1: 0,
                            y1: 0,
                            x2: 0,
                            y2: 1
                        },
                        stops: [
                            [0, '#33CCFF'],
                            [1, '#81DCFF']
                        ]
                   }
                }]
                ,xAxis:{
                    labels:{
                        enabled : false
                    }
                }
                ,yAxis:{
                    labels:{
                        style: {
	                        fontSize: '20px',
	                        fontWeight: 'bold'
                        }
                    }
                }
                ,navigator : {
				    enabled : false
			    },rangeSelector:{
                    enabled:false
                },scrollbar : {
                    enabled : false
                },credits: {
                    enabled: false
                }
            }";

        $result .= $end_fake_json;

        if (!empty ($result)) {
            \File::put($this->config['infile'],$result);
        }

I know i know it is not the clever way to create a json string but i will update on it . I have also added some additional properties like color , gradient to our chart . Now write the string to file which we gave in our config . Last step is to create the chart using our newly created options file .

 //Generate Charts now
$phantomjs = $this->config['phantomjs'];
$highcharts_convert = $this->config['highcharts_convert'];
$infile = $this->config['infile'];
$constr = $this->config['constr'];
$outfile = $this->config['outfile'];

$command = "$phantomjs $highcharts_convert -infile $infile -constr $constr -outfile $outfile -scale 2.5 -width 800";
system($command, $output);

We have everything ready but table and seeders are left . Lets create a new migration create_analytics_table and create four columns id,visits,created_at and updated_at

<?php

use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

class CreateAnalyticsTable extends Migration {

	/**
	 * Run the migrations.
	 *
	 * @return void
	 */
	public function up()
	{
		Schema::create('analytics',function($table){
            $table->increments('id');
            $table->integer('visits');
            $table->timestamps();
        });
	}

	/**
	 * Reverse the migrations.
	 *
	 * @return void
	 */
	public function down()
	{
		Schema::dropIfExists('analytics');
	}

}

Our seeder just inserts random visit values into the table 

<?php

class AnalyticsSeeder extends Seeder {

	/**
	 * Run the database seeds.
	 *
	 * @return void
	 */
	public function run()
	{
        $date = \Carbon\Carbon::now();

        for($i=0;$i<100;$i++) {
            $a = new Analytics();
            $a->visits = rand(1000,5000);
            $a->save();
            //Analytics::insert(['visits'=>rand(1000,5000)]);
            sleep(1);
        }
	}

}

Add your newly created AnalyticsSeeder to DatabaseSeeder

<?php

class DatabaseSeeder extends Seeder {

	/**
	 * Run the database seeds.
	 *
	 * @return void
	 */
	public function run()
	{
		Eloquent::unguard();

        $this->call('AnalyticsSeeder');

        $this->command->info('AnalyticsSeeder seeded!');
	}

}

Add your new command to your artisan file in start/artisan.php

Artisan::add(App::make("GenerateCharts"));

Now run our command

php artisan charts:generate

Output:

 

 

 

 

Author

  • Imran Iqbal
    Imran Iqbal

    Imran is a web developer and consultant from India. He is the founder of KodeInfo, the PHP and Laravel Community . In the meantime he follows other projects, works as a freelance backend consultant for PHP applications and studies IT Engineering . He loves to learn new things, not only about PHP or development but everything.

Related

WHY USE A FRAMEWORK OVER PLAIN PHP

WHY USE A FRAMEWORK OVER PLAIN PHP
read more

GETTING STARTED WITH LARAVEL

GETTING STARTED WITH LARAVEL
read more

UNDERSTANDING LARAVEL STRUCTURE

UNDERSTANDING LARAVEL STRUCTURE
read more

UNDERSTANDING LARAVEL ROUTES

UNDERSTANDING LARAVEL ROUTES
read more

comments powered by Disqus