PHP - Google Charts - php

I'm trying to use Google Charts and i'm having some trouble to create array for API.
https://developers.google.com/chart/interactive/docs/gallery/linechart#creating-material-line-charts
My source of data has 3 columns, which is quantity, date and user name.
(you can see in attachment).
How to convert his form of array to use with google charts api (line charts)?
I think i need to convert each different value from column "responsavel" to a new column and set these column the value of column "qt_pedido"
In this example, the final array should be something like this:
Date | user1 | user2
-------------------------------
03/09/2018 | 58 | 19
05/09/2019 | 23 | 5
Result from sql server

it would be easier to use google data table methods to transform the rows to columns,
see following working snippet...
google.charts.load('current', {
packages: ['line']
}).then(function () {
// create data table
var data = google.visualization.arrayToDataTable([
['Date', 'responsavel', 'qt_pedido'],
['20/10/2018', 'user1', 10],
['20/10/2018', 'user2', 20],
['20/10/2018', 'user3', 30],
['20/11/2018', 'user1', 40],
['20/11/2018', 'user2', 50],
['20/11/2018', 'user3', 60],
['20/12/2018', 'user1', 70],
['20/12/2018', 'user2', 80],
['20/12/2018', 'user3', 90],
]);
// create data view
var view = new google.visualization.DataView(data);
// column arrays
var aggColumns = [];
var viewColumns = [0];
// build view & agg columns for each responsibility
data.getDistinctValues(1).forEach(function (responsibility, index) {
viewColumns.push({
calc: function (dt, row) {
if (dt.getValue(row, 1) === responsibility) {
return dt.getValue(row, 2);
}
return null;
},
label: responsibility,
type: 'number'
});
aggColumns.push({
aggregation: google.visualization.data.sum,
column: index + 1,
label: responsibility,
type: 'number'
});
});
// set view columns
view.setColumns(viewColumns);
// sum view by date
var aggData = google.visualization.data.group(
view,
[0],
aggColumns
);
// draw chart
var chart = new google.charts.Line(document.getElementById('chart_div'));
chart.draw(aggData);
});
<script src="https://www.gstatic.com/charts/loader.js"></script>
<div id="chart_div"></div>

Related

PhP Convert a multidimensional array column by column?

I'm trying to build a google chart column chart where each column represents a property (house), and the income, per year of that property. The year would be represented on the x-axis, and the y-axis represents the income amount.
End result in JS would need to look like this:
var columnChartData = google.visualization.arrayToDataTable([
['Year', 'Property 1', 'Property 2'],
['2022', 300, 4000],
['2023', 6000, 8000]
]);
I'm currently struggling with converting my PHP array below, to the required google format. How can I build an array column by column?
array([Property 1] => Array ( [2021] => 353.93 [2022] => 12628.65 [2023] => 12841.57 )
[Property 2] => Array ( [2022] => 370.78 [2023] => 12841.57 ))
required JS/GoogleChart:
['Year', 'Property 1', 'Property 2'],
['2022', 300, 4000],
['2023', 6000, 8000]
Here is one approach. This heavily relies on the assumption that for each property, a record for the Year is included. For example, we are assuming that the nth element of Property1 has the same year as the nth element of Property2.
Note, if OP controls the source with SQL or some other data source, it would be much easier to use PIVOT which is available in many RDBMS (but notably not mySQL). Likewise, using a charting framework like plotly might be helpful as well - I like the examples from this website which shows us using two separate data sources on the same plot area. We could imagine applying this by having a dataset for Property1 and a separate one for Property2.
https://plotly.com/javascript/line-charts/
<?php
$data = ["Property1" => [2022=>300, 2023=>6000], "Property2" => [2022=>4000, 2023=>8000]];
// Reshape so that each row consists of [Year, Property1, Property2]
$formattedArray = array_map(function ($year, $prop1, $prop2) {
return [$year, $prop1, $prop2];
},
array_keys($data["Property1"]),
array_values($data["Property1"]),
array_values($data["Property2"]));
// Add a header
$res = array_merge([["Year", "Property1", "Property2"]], $formattedArray);
// Method 2
$keys = array_keys($data);
$res2 = [array_merge(["Year"], $keys)];
// assume each property has the same years
$years = array_keys($data[$keys[0]]);
for ($i = 0; $i<count($years); $i++) {
$thisRow = [$years[$i]];
foreach($data as $prop) {
array_push($thisRow, array_values($prop)[$i]);
}
array_push($res2, $thisRow);
}
print_r($res2);
?>
<html>
<head> <!--Load the AJAX API-->
<script type="text/javascript" src="https://www.gstatic.com/charts/loader.js"></script>
<script type="text/javascript">
let thisData = <?php echo json_encode($res);?>;
// Load the Visualization API and the corechart package.
google.charts.load('current', {'packages':['corechart']});
// Set a callback to run when the Google Visualization API is loaded.
google.charts.setOnLoadCallback(drawChart);
// Callback that creates and populates a data table,
// instantiates the scatter chart, passes in the data and
// draws it.
function drawChart() {
// Create the data table.
<?php echo "var chartData = google.visualization.arrayToDataTable(" . json_encode($res2) . ");"?>
// Set chart options
var options = {'title':'Different Properties By Year',
'width':600,
'height':400};
// Instantiate and draw our chart, passing in some options.
var chart = new google.visualization.ScatterChart(document.getElementById('chart_div'));
chart.draw(chartData, options);
}
</script>
</head>
<body>
<!--Div that will hold the pie chart-->
<div id="chart_div"></div>
</body>
</html>

How to dynamically fetch dates from database table to disable dates

I'm selling 20 cupcakes per day. I need to disable dates that have fulfilled 20 orders. I have a database table for date and quantity sold.
How can I dynamically fetch dates from database table into
var dates = [ ]
var dates = ["20/07/2020", "21/07/2020", "22/07/2020", "23/07/2020"];
function disableDates(date) {
var string = jQuery.datepicker.formatDate('dd/mm/yy', date);
let isDefaultDisabled = false;
if(date.getDay()===2 || date.getDay()==3 || date.getDay()==4){
isDefaultDisabled = true;
}
return [ isDefaultDisabled && dates.indexOf(string) == -1 ];
}
$(function() {
$("#date").datepicker({
beforeShowDay: disableDates
});
});

mySQL with PHP to JSON for clientside chart

I am hoping to get a better way to do what I am doing. I have several charts to do and want to go about it the best way.
I am using HighCharts which requires a JSON object for data. I am using Laravel as my back end. I am sending 2 variables (1 for labels & 1 for values) to my view. I see i can do this in the controller but haven't started yet
My Issue:
I have this table
| date | printer | quant |
| 2017-01-23 | JP1 | 20 |
| 2017-01-23 | JP1 | 12 |
| 2017-01-23 | JP2 | 18 |
| 2017-01-24 | JP5 | 16 |
| 2017-01-24 | JP1 | 8 |
| 2017-01-26 | JP2 | 12 |
I think that hits all the cases, variable printers, missed days, need to sum quantity per day and make 0 if missing. Need an array for each printer which I dont have a premade list because they change a lot. my querys will be with a date range. The list of printers will be distinct for that date range.
What I have done is make a pivot mySQL query which gets me this
[
{
"date": "2017-01-23",
"sum": "1416",
"JP4_3": "375",
"JP4_1": "533",
"JP4_2": "508",
"B3": null,
"A2": null
},
{
"date": "2017-01-24",
"sum": "2151",
"JP4_3": "847",
"JP4_1": "499",
"JP4_2": "805",
"B3": null,
"A2": null
},
{
"date": "2017-01-25",
"sum": "2097",
"JP4_3": "284",
"JP4_1": "917",
"JP4_2": "896",
"B3": null,
"A2": null
}
]
I can loop through and grab each property to make an array.
I am wondering is there a better way. I need to get a label and data to work with high charts. Here is an example.
xAxis: {
categories: ['Apples', 'Bananas', 'Oranges']
},
series: [{
name: 'Jane',
data: [1, 0, 4]
}, {
name: 'John',
data: [5, 7, 3]
}]
I know this is a complicated and long winded question. Thank you for any help and direction.
return just array of data from laravel and in view balde use
var data = JSON.parse( '{!! json_encode($chartData) !!}' );
I used this in my code
Wanted to give a code example for how I did this. Kind of a pain but from Samsquanch i guess this is normal. This does several database calls but still plenty fast for me. This is using laravel framework.
// Main chart info
$PrinterChartArray ["chart"] = array (
'type' => 'line'
);
$PrinterChartArray ["title"] = array (
"text" => "Units per Printer"
);
$PrinterChartArray ["credits"] = array (
"enabled" => false
);
$PrinterChartArray ["xAxis"] = array (
"categories" => array ()
);
$PrinterChartArray ["tooltip"] = array (
"valueSuffix" => " units"
);
$PrinterChartArray ["yAxis"] = array (
"title" => array (
"text" => "Units"
)
);
// Get series Names
$printerNames = PrintJob::distinct()->select('printer')
->whereDate('job_date', '>=', date($from))
->whereDate('job_date', '<=', date($to))
->get();
// loop through series names and set series name and data for high charts
$chartSeries = [];
foreach($printerNames as $printer)
{
$printName = $printer->printer;
// Used where inside join so wouldnt miss dates
// used a calendar table with all dates to fill in missing dates
$data = PrintJob::select(DB::raw('COALESCE(SUM(print_jobs.quantity), 0) as sum'), DB::raw('calendar.datefield'))
->rightJoin('calendar', function($join) use ($printName)
{
$join->on('print_jobs.job_date', '=', 'calendar.datefield')
->where('printer', '=', $printName);
})
->whereDate('calendar.datefield', '>=', date($from))
->whereDate('calendar.datefield', '<=', date($to))
->groupBy('calendar.datefield')
->orderBy('calendar.datefield', 'ASC')
//->toSql();
->get();
//->pluck('sum');
// Needed to convert the returned value from a string value to a int value (apparently a driver issue with php and mysql)
foreach ($data as $key => $var) {
$data[$key] = (int)$var->sum;
}
// Add series to high chart
$chartSeries[] = array(
"name" => $printName,
"data" => $data
);
}
// Unessesary to use PrintJob for this but keeping similar to series to not mess up label
$chartLabel = PrintJob::select(DB::raw('calendar.datefield'))
->rightJoin('calendar', function($join) use ($printName)
{
$join->on('print_jobs.job_date', '=', 'calendar.datefield');
})
->whereDate('calendar.datefield', '>=', date($from))
->whereDate('calendar.datefield', '<=', date($to))
->groupBy('calendar.datefield')
->orderBy('calendar.datefield', 'ASC')
//->toSql(); --- This will show the actual query
//->get(); --- Will give array of objects
->pluck('calendar.datefield'); // --- Will give a array of just the values wanted
$PrinterChartArray ["series"] = $chartSeries;
$PrinterChartArray ["xAxis"] = array (
"categories" => $chartLabel
);
//return $PrinterChartArray; -- used to see output without view
return view('print.charts', compact('jobsByDayLabels', 'jobsByDayValues', 'unitsByDayLabels', 'unitsByDayValues', 'PrinterChartArray'));
And this is the view part
<!-- Chart for Units by Printer by Day -->
<div class="row">
<div class="col-md-12 col-sm-12 col-xs-12">
<div class="x_panel">
<div id="printerUnitsByDay" style="min-width: 310px; height: 500px; margin: 0 auto"></div>
</div>
</div>
</div>
and this below is the script on the view
$(function() {
$('#printerUnitsByDay').highcharts({!! json_encode($PrinterChartArray) !!})
});

Parent-child sorting

I am trying to sort a table that has parent- and child-rows. Sorting should always be performed based on the parent rows, but the childrows should always immediately follow the parent. Table data is in format of
[
{name: 'xxx', group: 'A', type: 'parent'},
{name: 'yyy', group: 'B', type: 'parent'},
{name: 'zzz', group: 'A', type: 'child'},
{name: 'qqq', group: 'A', type: 'child'}
]
So, sorted by name the correct order would be xxx,qqq,zzz,yyy.
The data comes from a Laravel/Eloquent ajax query and is displayed in a datatables table, so sorting it either client or server side would be fine.
Not related to multisort (comment below)
Use orderFixed option to always apply ordering to a certain column before/after any other columns.
For example:
var table = $('#example').DataTable({
ajax: {
url:'https://api.myjson.com/bins/4n6ey',
dataSrc: ''
},
orderFixed: {
pre: [[1, 'asc'], [2, 'desc']]
},
columns: [
{ data: 'name' },
{ data: 'group' },
{ data: 'type'}
]
});
See this jsFiddle for code and demonstration.
Couldn't figure out how to do it in SQL, DataTables callbacks/sorters (although a plugin would be an workable option), Eloquent callbacks or Yajra DataTables adapter options. So I just went the brute force way.
sort by intended column using sql
separate master/child (debt/guarantee) lines
look up each child lines' master record for the correct sorting index and create a new indexing column for the "true" order
$dataTable_column_map = $claimPortfolio->getColumnMap();
$claimPortfolioLines = $claimPortfolio->lines()->orderBy($dataTable_column_map[$request->get('order')[0]['column']]['name'], $request->get('order')[0]['dir'])->get();
$claimPortfolioLines = ClaimPortfolioService::orderGuarantees($claimPortfolioLines);
$claimPortfolioLines = ClaimPortfolioService::filterLines($claimPortfolioLines, Input::get('search.value'));
Session::put('claimPortfolioLineIdList', $claimPortfolioLines->lists('id')->toArray());
return Datatables::of($claimPortfolioLines)
->order(function() {
return true; // cancel built-in ordering & filtering
})
->filter(function() {
return true;
})
->make(true);
public static function orderGuarantees(Collection $claimPortfolioLines)
{
$claimPortfolioLinesGuarantees = $claimPortfolioLines->filter(function ($claimPortfolioLine) {
return $claimPortfolioLine->line_type == 'GUARANTEE';
});
$claimPortfolioLines = $claimPortfolioLines->filter(function ($claimPortfolioLine) {
return $claimPortfolioLine->line_type == 'DEBT';
});
foreach ($claimPortfolioLines as $idx_line => $claimPortfolioLine)
{
$claimPortfolioLine->sortOrder = $idx_line;
foreach ($claimPortfolioLinesGuarantees as $idx_guaranteeLine => $claimPortfolioLineGuarantee)
{
if ($claimPortfolioLineGuarantee->contract_no == $claimPortfolioLine->contract_no && $claimPortfolioLine->line_type == 'DEBT')
{
$claimPortfolioLineGuarantee->sortOrder = "{$idx_line}.{$idx_guaranteeLine}";
$claimPortfolioLines->push($claimPortfolioLineGuarantee);
}
}
}
$claimPortfolioLines = $claimPortfolioLines->sortBy('sortOrder');
return $claimPortfolioLines;
}
The general solution for this in SQL is to self join and order by multiple columns, including whether it's a parent. In OP's case, assuming a table of data d (t an alias meaning table, and s meaning sort):
SELECT t.*
FROM d AS t
INNER JOIN d AS s
ON s.group = t.group
AND t.type = 'parent'
ORDER BY s.name, t.type = 'parent' DESC, t.name

Populating Google combo chart with multidimensional PHP array

I have a php array with data on students. It looks like this:
array(5) {
["question1"]=>
array(30) {
["2014, 03, 02"]=>
array(10) {
["student1"]=>
int(54)
["student2"]=>
int(43)
... etc. ...
["median"]=>
string(2) "49"
}
Each day the students answers five questions with a value from 1 to 100. Each day a median value for all answers to a single question is calculated. The answers and the median value is stored like above.
Now I want to populate a Google Charts combo chart with this data but I just can't get it to work.
I would like to visualise the data with the date on the X-axis, the 1-100 value on the Y-axis and each answer as a point. The data for the median value should be displayed as a curve over the points. Points and curves for each question should have its own colour.
But I'm pretty much stuck. I can't figure out how to insert the data. I have tried this approach:
var jsonData = (<?= json_encode($data)?>);
var data = new google.visualization.arrayToDataTable(jsonData);
but I only get the following error message:
Uncaught Error: Not an array format+da,default+da,ui+da,corechart+da.I.js:181
lda format+da,default+da,ui+da,corechart+da.I.js:181
Gp format+da,default+da,ui+da,corechart+da.I.js:183
drawChart ?side=graf:4888
You need to change the format of your data. The Visualization API expects data in a tabular format, where your x-axis data is in the first column and each data series (a set of colored points) is its own column of data. You can either construct a json representation of a DataTable (for use with the DataTable constructor) or an array of arrays of data (for use with the arrayToDataTable function).
Since you are already using the arrayToDataTable function, this is the structure you need to have:
$data = array(
array('Date', 'Question 1', array('type' => 'number', 'role' => 'interval', 'id' => 'Q1Median'), 'Question 2', array('type' => 'number', 'role' => 'interval', 'id' => 'Q2Median') /* repeat for other questions */),
// format is array(date, q1 score, q1 median, q2 score, q2 median...)
// set the median in the first row for the day, leave it null for all other rows
array('2014, 03, 02', 54, 49, /* repeat for other questions */),
array('2014, 03, 02', 43, null, /* repeat for other questions */),
// repeat one row for each student for each day
);
The median values are set in an "interval" role column, which you can style to display as a curved line in the chart options:
interval: {
// use the column ID of an interval to set the options for that interval
Q1median: {
style: 'line'
},
Q2median: {
style: 'line'
}
// etc...
}
Options for styling intervals are documented here.

Categories