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 the 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 the 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 the 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 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