Skip to content

Instantly share code, notes, and snippets.

@gbuckingham89
Last active March 5, 2024 14:12
Show Gist options
  • Star 38 You must be signed in to star a gist
  • Fork 8 You must be signed in to fork a gist
  • Save gbuckingham89/b3ff459eed67e53d7b8737e05cda13be to your computer and use it in GitHub Desktop.
Save gbuckingham89/b3ff459eed67e53d7b8737e05cda13be to your computer and use it in GitHub Desktop.
Laravel Livewire Charts
<?php
namespace App\Charts;
use App\Support\Livewire\ChartComponentData;
use ConsoleTVs\Charts\Classes\Chartjs\Chart;
/**
* Class WanSpeedTestsChart
*
* @package App\Charts
*/
class WanSpeedTestsChart extends Chart
{
/**
* WanSpeedTestsChart constructor.
*
* @param \App\Support\Livewire\ChartComponentData $data
*/
public function __construct(ChartComponentData $data)
{
parent::__construct();
$this->loader(false);
$this->options([
'maintainAspectRatio' => false,
'legend' => [
'display' => false,
],
'scales' => [
'yAxes' => [
[
'ticks' => [
'maxTicksLimit' => 6,
'beginAtZero' => true,
],
],
],
'xAxes' => [
[
'display' => false,
],
],
],
]);
$this->labels($data->labels());
$this->dataset("Upload speed (Mbps)", "line", $data->datasets()[0])->options([
'backgroundColor' => 'rgb(127,156,245, 0.4)',
'borderColor' => '#7F9CF5',
'pointBackgroundColor' => 'rgb(255, 255, 255, 0)',
'pointBorderColor' => 'rgb(255, 255, 255, 0)',
'pointHoverBackgroundColor' => '#7F9CF5',
'pointHoverBorderColor' => '#7F9CF5',
'borderWidth' => 1,
'pointRadius' => 1,
]);
$this->dataset("Download speed (Mbps)", "line", $data->datasets()[1])->options([
'backgroundColor' => 'rgb(127, 156, 245, 0.4)',
'borderColor' => '#A3BFFA',
'pointBackgroundColor' => 'rgb(255, 255, 255, 0)',
'pointBorderColor' => 'rgb(255, 255, 255, 0)',
'pointHoverBackgroundColor' => '#A3BFFA',
'pointHoverBorderColor' => '#A3BFFA',
'borderWidth' => 1,
'pointRadius' => 1,
]);
}
}
<?php
namespace App\Http\Livewire;
use App\Charts\WanSpeedTestsChart;
use App\Models\WanSpeedTest;
use App\Support\Livewire\ChartComponent;
use App\Support\Livewire\ChartComponentData;
use Carbon\Carbon;
use Illuminate\Support\Collection;
/**
* Class WanSpeedTests
*
* @package App\Http\Livewire
*/
class WanSpeedTests extends ChartComponent
{
/**
* @return string
*/
protected function view(): string
{
return '_livewire.wan-speed-tests';
}
/**
* @return string
*/
protected function chartClass(): string
{
return WanSpeedTestsChart::class;
}
/**
* @return \App\Support\Livewire\ChartComponentData
*/
protected function chartData(): ChartComponentData
{
$wan_speed_tests = WanSpeedTest::query()
->select(['id', 'created_at', 'speed_down_mbps', 'speed_up_mbps', 'ping_ms'])
->where('created_at', '>=', Carbon::now()->subDay())
->where('created_at', '<=', Carbon::now())
->get();
$labels = $wan_speed_tests->map(function(WanSpeedTest $wan_speed_test, $key) {
return $wan_speed_test->created_at->format('Y-m-d H:i:s');
});
$datasets = new Collection([
$wan_speed_tests->map(function(WanSpeedTest $wan_speed_test) {
return number_format($wan_speed_test->speed_up_mbps, 2, '.', '');
}),
$wan_speed_tests->map(function(WanSpeedTest $wan_speed_test) {
return number_format($wan_speed_test->speed_down_mbps, 2, '.', '');
})
]);
return (new ChartComponentData($labels, $datasets));
}
}
<?php
namespace App\Support\Livewire;
use Illuminate\View\View;
use Livewire\Component;
/**
* Class ChartComponent
*
* @package App\Support\Livewire
*/
abstract class ChartComponent extends Component
{
/**
* @var string|null
*/
public ?string $chart_id = null;
/**
* @var string|null
*/
public ?string $chart_data_checksum = null;
/**
* @return string
*/
protected abstract function chartClass(): string;
/**
* @return \App\Support\Livewire\ChartComponentData
*/
protected abstract function chartData(): ChartComponentData;
/**
* @return string
*/
protected abstract function view(): string;
/**
* @return \Illuminate\View\View
*/
public function render(): View
{
$chart_data = $this->chartData();
if(!$this->chart_id)
{
$chart_class = $this->chartClass();
$chart = new $chart_class($chart_data);
$this->chart_id = $chart->id;
}
elseif($chart_data->checksum()!==$this->chart_data_checksum)
{
$this->emit('chartUpdate', $this->chart_id, $chart_data->labels(), $chart_data->datasets());
}
$this->chart_data_checksum = $chart_data->checksum();
return view($this->view(), [
'chart' => ($chart ?? null)
]);
}
}
<?php
namespace App\Support\Livewire;
use Illuminate\Contracts\Support\Arrayable;
use Illuminate\Support\Collection;
/**
* Class ChartComponentData
*
* @package App\Support\Livewire
*/
class ChartComponentData implements Arrayable
{
/**
* @var \Illuminate\Support\Collection
*/
private Collection $labels;
/**
* @var \Illuminate\Support\Collection
*/
private Collection $datasets;
/**
* ChartComponentData constructor.
*
* @param \Illuminate\Support\Collection $labels
* @param \Illuminate\Support\Collection $datasets
*/
public function __construct(Collection $labels, Collection $datasets)
{
$this->labels = $labels;
$this->datasets = $datasets;
}
/**
* @return array
*/
public function toArray(): array
{
return [
'labels' => $this->labels,
'datasets' => $this->datasets
];
}
/**
* @return string
*/
public function checksum(): string
{
return md5(json_encode($this->toArray()));
}
/**
* @return \Illuminate\Support\Collection
*/
public function labels(): Collection
{
return $this->labels;
}
/**
* @return \Illuminate\Support\Collection
*/
public function datasets(): Collection
{
return $this->datasets;
}
}
window.livewire.on('chartUpdate', (chartId, labels, datasets) => {
let chart = window[chartId].chart;
chart.data.datasets.forEach((dataset, key) => {
dataset.data = datasets[key];
});
chart.data.labels = labels;
chart.update();
});
<div wire:poll.10s>
<header>
<h2>WAN speed tests <small>Past 24hours</small></h2>
</header>
<div wire:ignore wire:key={{ $chart->id }}>
@if($chart)
{!! $chart->container() !!}
@endif
</div>
</div>
@if($chart)
@push('scripts')
{!! $chart->script() !!}
@endpush
@endif
@extends('skeleton')
@section('body')
<livewire:dashboard.wan-speed-tests/>
@endsection
<!DOCTYPE html>
<html lang="en" class="w-full h-full">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<meta name="csrf-token" content="{{ csrf_token() }}">
<link rel="stylesheet" href="{{ mix('/assets/build/css/app.css') }}" type="text/css">
@livewireStyles
</head>
<body>
<div>
@yield('body')
</div>
@livewireScripts
<script type="text/javascript" src="{{ mix('/assets/build/js/chartjs.js') }}"></script>
<script type="text/javascript" src="{{ mix('/assets/build/js/app.js') }}"></script>
@stack('scripts')
</body>
</html>
@usernotnull
Copy link

@gbuckingham89 thanks, and hopefully you'll keep us updated!

@lironesamoun
Copy link

Hi ! Is this code updated with the new version of chartisan ?

@gbuckingham89
Copy link
Author

@lilouch no unfortunately not. I also believe there is a newer release of Livewire now - so there maybe more changes needed to support that too.

Unfortunately I think it's now unlikely that I'll publish any update - for this particular project I can see myself moving to Inertia.js with Vue3 instead of Livewire.

@arthurkawalewale
Copy link

I'm having an issue with the \Illuminate\view\view the return type. It keeps on telling me that it is receiving an \illuminate\contract\view\view. How do I overcome this?

@arthurkawalewale
Copy link

I'm having an issue with $chart->id it keeps on reporting: Attempt to read property "id" on null. What can be the problem?

@mnluribef
Copy link

I'm having the same issue as the before commenter. Attempt to read property "id" on null, file wan-speed-tests.blade.php, line 5 <div wire:ignore wire:key={{ $chart->id }}>

Any ideas on how to solve this?

@AbdulSamad00
Copy link

Hello, anyone help me, i want to create real-time chart cards in the PHP Laravel updated version.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment