I have a simple report page, and I am using Laravel 7
to build it.
I want to trigger auto-download a PDF with that view.
What would be the most lightweights I should look into? I did a quick Google, and I saw so many options.
I decided to try this and did all the steps, below is my final code
Final Codes
Note this line : $pdf = PDF::loadView('layouts.be.baby.report', get_defined_vars());
public function downloadReportPDF($id) { $code = Request::get('code'); $baby = Baby::where('adminCode',$code)->where('id',strtolower($id)) ->orWhere('readOnlyCode',$code)->where('id',strtolower($id)) ->first(); if($baby){ $inputs = Request::all(); $interval = 'week'; if(array_key_exists('interval', $inputs)){ $interval = $inputs['interval']; } switch ($interval) { case 'day': $q = BabyLog::where('created_at', '>', now()->today()); break; case 'week': $q = BabyLog::where('created_at', '>', now()->subWeek()); break; case 'month': $q = BabyLog::where('created_at', '>', now()->subMonth()); break; case 'year': $q = BabyLog::where('created_at', '>', now()->subYear()); break; default: $q = BabyLog::orderBy('created_at', 'desc'); break; } $logs = $q->where('babyId',$baby->id)->orderBy('created_at', 'desc')->get()->groupBy(function ($log) { return $log->created_at->format('m/d/y'); }); // dd($logs); $graphData = []; foreach($logs as $date => $logsOnThatDay){ $pee = 0; $poop = 0; $feed = 0; $medicine = 0; $sleep = 0; foreach($logsOnThatDay as $logOnThatDay){ // if(strtotime($logOnThatDay->created_at) < strtotime(date('Y-m-d H:i:s'))){ if($logOnThatDay->type == 'pee'){ $pee++; } if($logOnThatDay->type == 'poop'){ $poop++; } if($logOnThatDay->type == 'feed'){ $feed++; } if($logOnThatDay->type == 'medicine'){ $medicine++; } if($logOnThatDay->type == 'sleep'){ $sleep++; } $graphData[$date]['pee'] = $pee; $graphData[$date]['poop'] = $poop; $graphData[$date]['feed'] = $feed; $graphData[$date]['medicine'] = $medicine; $graphData[$date]['sleep'] = $sleep; } } array_pop($graphData); //PDF $pdf = PDF::loadView('layouts.be.baby.report', get_defined_vars()); return $pdf->download('file.pdf'); } }
It is working, when I visit the route :
Route::get(‘/baby/{id}/report/download’,’BabyController@downloadReportPDF’);
But the styles seems very messed up.
- Images are missing, and so on.
How do I improve on that ?
Updated
I’ve updated my images links to remote url now… they loaded now.
Somehow styles still messed up.
I even tried inline style for my padding, and still rendering wrong… 🙁
report.blade.php
<!DOCTYPE html> <html> <head> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="author" content="Bunlong Heng"> <meta name="csrf-token" value="{{ csrf_token() }}"> <title>Report</title> <link id="favicon" rel="shortcut icon" href="{{$baby->babyProfilePath}}?q={{microtime()}}" type="image/x-icon" /> <link rel="shortcut icon" href="{{$baby->babyProfilePath}}?q={{microtime()}}" type="image/favicon.ico" /> <link rel="apple-touch-icon" href="{{$baby->babyProfilePath}}?q={{microtime()}}" sizes="152x152"> <link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet"> <script src="https://www.chartjs.org/dist/2.8.0/Chart.min.js"></script> <script src="https://www.chartjs.org/samples/latest/utils.js"></script> <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script> <style type="text/css"> body { background-color: white; color: black; } h5 { font-weight: normal; color: #86868b; } .text-poop { border: #ffb54c 1px solid !important; color: #ffb54c; } .text-pee { border: #46b8da 1px solid !important; color: #46b8da; } .text-medicine { border: #ffdf0a 1px solid !important; color: #ffdf0a; } .text-sleep { border: #ca88ff 1px solid !important; color: #ca88ff; } .text-feed { border: black 1px solid !important; color: black; } </style> </head> <body> <div class="container "> <div class="row "> <div class="col-sm-2 "></div> <div class="col-sm-8"> <h1>{{ $baby->babyName }}</h1> <hr> <h5> Interval : {{ $interval }}ly. </h5> <h5> Today : {{ date('D F j, Y, g:i a') }}</h5> <h5> Parents : {{ $baby->name }} ({{ $baby->email }})</h5> <h5> URL : {{ env('APP_URL') }}/baby/{{ $baby->id }}/report?code={{ $baby->readOnlyCode }}</h5> <hr> <canvas id="canvas"></canvas> <hr> <table class="table skill-table"> <thead class="thin-border-bottom"> <th>date</th> <th>Ago</th> <th><img src="https://i.imgur.com/Xhg4Iwi.png" /> </th> <th><img src="https://i.imgur.com/peU9Bas.png" /> </th> <th><img src="https://i.imgur.com/Y3rrj9T.png" /> </th> <th><img src="https://i.imgur.com/zQrE1o5.png" /> </th> <th><img src="https://i.imgur.com/yCk62aM.png" /> </th> </thead> <tbody> @foreach ($graphData as $date => $graph) <tr> <td > {{$date}} </td> <td>{{ DateHelper::ago($date) }} ago.</td> <td > <a style="padding: 5px; " class="text-poop">{{$graph['poop'] ?? '' }}</a> </td> <td > <a style="padding: 5px; " class="text-pee">{{$graph['pee'] ?? '' }}</a> </td> <td > <a style="padding: 5px; " class="text-feed">{{$graph['feed'] ?? '' }}</a> </td> <td > <a style="padding: 5px; " class="text-medicine">{{$graph['medicine'] ?? '' }}</a> </td> <td > <a style="padding: 5px; " class="text-sleep">{{$graph['sleep'] ?? '' }}</a> </td> </tr> @endforeach </div> </div> </div> <script type="text/javascript"> function hexToRgb(hex, opacity=1) { var h=hex.replace('#', ''); h = h.match(new RegExp('(.{'+h.length/3+'})', 'g')); for(var i=0; i<h.length; i++) h[i] = parseInt(h[i].length==1? h[i]+h[i]:h[i], 16); if (typeof opacity != 'undefined') h.push(opacity); return 'rgba('+h.join(',')+')'; } var url_string = window.location.href; var url = new URL(url_string); var data = {}; if(url.searchParams.get("interval") != null){ data.interval = url.searchParams.get("interval"); } // console.log(data.interval); $.ajax({ method: 'POST', url: '/baby/{{$id}}/graphsData', crossDomain: true, contentType: false, headers: { 'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('value'), "Accept": "application/json", "Content-Type": "application/x-www-form-urlencoded", "Cache-Control": "no-cache" }, data: data, success: function(response){ // console.log(response); keys = []; peeData = []; poopData = []; feedData = []; medicineData = []; sleepData = []; $.each(response, function(key,val) { keys.push(key); peeData.push(val.pee); poopData.push(val.poop); feedData.push(val.feed); medicineData.push(val.medicine); sleepData.push(val.sleep); }); // console.log(peeData, poopData, feedData, medicineData, sleepData); var originalLineDraw = Chart.controllers.line.prototype.draw; Chart.helpers.extend(Chart.controllers.line.prototype, { draw: function() { originalLineDraw.apply(this, arguments); var chart = this.chart; var ctx = chart.chart.ctx; var index = chart.config.data.lineAtIndex; if (index) { var xaxis = chart.scales['x-axis-0']; var yaxis = chart.scales['y-axis-0']; ctx.save(); ctx.beginPath(); ctx.moveTo(xaxis.getPixelForValue(undefined, index), yaxis.top); ctx.strokeStyle = '#ff0000'; ctx.textAlign = '#ff0000'; ctx.fillText('NOW',0,0); ctx.lineTo(xaxis.getPixelForValue(undefined, index), yaxis.bottom); ctx.stroke(); ctx.restore(); } } }); var options = { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric', hour: '2-digit', minute: '2-digit', second: '2-digit', hour12: false }; var fullDayText = new Date().toLocaleTimeString('en-us', options); var chartColors = [hexToRgb('#fff',1), hexToRgb("ccc",1)]; let canvas = document.getElementById('canvas').getContext('2d'); canvas.height = 2000; var renderChart = function() { new Chart(canvas, { type:'bar', data:{ labels: keys, datasets:[ { label: 'poop', data: poopData, backgroundColor: hexToRgb('#ffb54c',.5), borderWidth:1, borderColor: hexToRgb('#ffb54c',1), hoverBorderWidth:3, }, { label: 'pee', data: peeData, backgroundColor: hexToRgb('#46b8da',.5), borderWidth:1, borderColor: hexToRgb('#46b8da',1), hoverBorderWidth:3, }, { label: 'feed', data: feedData, backgroundColor: hexToRgb('#fff',.5), borderWidth:1, borderColor: hexToRgb('#ccc',1), hoverBorderWidth:3, }, { label: 'medecine', data: medicineData, backgroundColor: hexToRgb('#ffdf0a',.5), borderWidth:1, borderColor: hexToRgb('#ffdf0a',1), hoverBorderWidth:3, }, { label: 'sleep', data: sleepData, backgroundColor: hexToRgb('#ca88ff',.5), borderWidth:1, borderColor: hexToRgb('#ca88ff',1), hoverBorderWidth:3, } ], lineAtIndex: new Date().getHours() }, options:{ legend:{ display:true, position:'right', labels:{ fontColor:'#000' } } } }); }; renderChart(); setTimeout(renderChart(), 1000); }, error: function(jqXHR, textStatus, errorThrown) { console.log(JSON.stringify(jqXHR)); console.log("AJAX error: " + textStatus + ' : ' + errorThrown); } }); </script> </body> </html>
Advertisement
Answer
If you want use trigger print you can do it by using window.print
, it’s will show print dialog that contain print as pdf. example code
<input type="button" value="Print" onClick="window.print()"> this will show in print preview
For print another page you can use iframe. you can hide it using CSS too. example:
<button onclick='window.frames["printf"].print();'>print</button> <iframe id="printf" name="printf" src="test.html" height="1%" width="100%" style="visibility: hidden"></iframe>
I tried print page contain chartjs and works 100%, you can test it out
If you want to trigger download as PDF, you should use package. JS solution is jsPDF and PHP/laravel solution is laravel-dompdf . Both packages will not print exactly as browser render especially if you are using CSS3 or Image source from local. You need to custom the CSS and asset that compatible for both packages.
If you are using laravel dompdf you need to change below code:
<img src="/media/image.png" />
to
<img src="{{ public_path('/media/image.png') }}">
you can using asset()
but you need activate remote asset in laravel dompdf too.
You can check DomPDF CSS Compatibility here.