diff --git a/.idea/laravel-metrics.iml b/.idea/laravel-metrics.iml
index 4d75f98..1618cfb 100644
--- a/.idea/laravel-metrics.iml
+++ b/.idea/laravel-metrics.iml
@@ -54,6 +54,7 @@
+
diff --git a/.idea/php.xml b/.idea/php.xml
index 9867cfb..f9d643e 100644
--- a/.idea/php.xml
+++ b/.idea/php.xml
@@ -66,6 +66,7 @@
+
diff --git a/README.md b/README.md
index bd8aaf2..6d42dbb 100644
--- a/README.md
+++ b/README.md
@@ -282,6 +282,19 @@ LaravelMetrics::query(...)
->groupByDay()
```
+### Group data (only for ```trends```)
+You can group data of a column with multiple values to use it in a dataset for your charts. For example :
+
+```php
+Order::metrics()
+ ->countByMonth(column: 'status')
+ ->groupData(['pending', 'delivered', 'cancelled'], Aggregate::SUM->value)
+ ->fillMissingData()
+ ->trends();
+```
+
+***Note :*** Follow same order in the example to avoid false data.
+
## Translations
Days and months names are automatically translated using `config(app.locale)` except 'week' period.
diff --git a/src/LaravelMetrics.php b/src/LaravelMetrics.php
index 47d213c..c7d7d70 100644
--- a/src/LaravelMetrics.php
+++ b/src/LaravelMetrics.php
@@ -57,6 +57,10 @@ class LaravelMetrics
protected string $groupBy;
+ protected string $groupedData = '';
+
+ protected array $groupedDataLabels = [];
+
public function __construct(protected Builder|QueryBuilder $builder)
{
$this->table = $this->builder->from;
@@ -519,7 +523,7 @@ protected function trendsData(): Collection
{
if (is_array($this->period)) {
return $this->builder
- ->selectRaw($this->asData().', '.$this->asLabel($this->formatDateColumn(), false))
+ ->selectRaw($this->asData().', '.$this->asLabel($this->formatDateColumn(), false).$this->groupedData)
->whereBetween(DB::raw($this->formatDateColumn()), [$this->period[0], $this->period[1]])
->groupBy('label')
->orderBy('label')
@@ -528,7 +532,7 @@ protected function trendsData(): Collection
return match ($this->period) {
Period::DAY->value => $this->builder
- ->selectRaw($this->asData().', '.$this->asLabel(Period::DAY->value))
+ ->selectRaw($this->asData().', '.$this->asLabel(Period::DAY->value).$this->groupedData)
->whereYear($this->dateColumn, $this->year)
->whereMonth($this->dateColumn, $this->month)
->when($this->count === 1, function (Builder|QueryBuilder $query) {
@@ -542,7 +546,7 @@ protected function trendsData(): Collection
->get(),
Period::WEEK->value => $this->builder
- ->selectRaw($this->asData().', '.$this->asLabel(Period::WEEK->value))
+ ->selectRaw($this->asData().', '.$this->asLabel(Period::WEEK->value).$this->groupedData)
->whereYear($this->dateColumn, $this->year)
->whereMonth($this->dateColumn, $this->month)
->when($this->count === 1, function (Builder|QueryBuilder $query) {
@@ -556,7 +560,7 @@ protected function trendsData(): Collection
->get(),
Period::MONTH->value => $this->builder
- ->selectRaw($this->asData().', '.$this->asLabel(Period::MONTH->value))
+ ->selectRaw($this->asData().', '.$this->asLabel(Period::MONTH->value).$this->groupedData)
->whereYear($this->dateColumn, $this->year)
->when($this->count === 1, function (Builder|QueryBuilder $query) {
return $query->where(DB::raw($this->formatPeriod(Period::MONTH->value)), $this->month);
@@ -569,7 +573,7 @@ protected function trendsData(): Collection
->get(),
Period::YEAR->value => $this->builder
- ->selectRaw($this->asData().', '.$this->asLabel(Period::YEAR->value))
+ ->selectRaw($this->asData().', '.$this->asLabel(Period::YEAR->value).$this->groupedData)
->when($this->count === 1, function (Builder|QueryBuilder $query) {
return $query->where(DB::raw($this->formatPeriod(Period::YEAR->value)), $this->year);
})
@@ -583,19 +587,33 @@ protected function trendsData(): Collection
->get(),
default => $this->builder
- ->selectRaw($this->asData().', '.$this->asLabel())
+ ->selectRaw($this->asData().', '.$this->asLabel().$this->groupedData)
->groupBy('label')
->orderBy('label')
->get(),
};
}
- protected function asData(): string
+ public function groupData(array $dataLabels, string $aggregate): self
+ {
+ $this->groupedDataLabels = $dataLabels;
+ $result = [];
+
+ foreach ($dataLabels as $key => $value) {
+ $result[] = $aggregate.'('.$this->column.' = "'.$value.'")'." as data$key";
+ }
+
+ $this->groupedData = ', '.implode(', ', $result);
+
+ return $this;
+ }
+
+ protected function asData(string $name = 'data'): string
{
- return "$this->aggregate($this->column) as data";
+ return "$this->aggregate($this->column) as $name";
}
- protected function asLabel(?string $label = null, bool $format = true): string
+ protected function asLabel(string $label = null, bool $format = true): string
{
if (is_null($this->labelColumn)) {
$label = ! $format ? $label : $this->formatPeriod($label);
@@ -606,7 +624,7 @@ protected function asLabel(?string $label = null, bool $format = true): string
return $label.' as label';
}
- protected function populateMissingDataForPeriod(array $data, bool $inPercent = false): array
+ protected function populateMissingDataForPeriod(array $data, bool $inPercent = false, string $dataLabel = 'data'): array
{
$dates = $this->getCustomPeriod();
$data = collect($data);
@@ -618,7 +636,7 @@ protected function populateMissingDataForPeriod(array $data, bool $inPercent = f
if ($dataForDate) {
$result[] = [
'label' => $dataForDate['label'],
- 'data' => $dataForDate['data'],
+ 'data' => (int) $dataForDate[$dataLabel],
];
} else {
$result[] = [
@@ -723,36 +741,55 @@ public function metricsWithVariations(int $previousCount, string $previousPeriod
return $result;
}
- /**
- * Generate trends data for charts
- */
- public function trends(bool $inPercent = false): array
+ protected function trendsWithMergedData(bool $inPercent = false): array
{
+ $result = [];
+
$trendsData = $this
->trendsData()
->toArray();
$trendsData = array_map(fn ($datum) => (array) $datum, $trendsData);
+ $data = [$this->getFormattedTrendsData($trendsData, $inPercent)];
+
+ foreach ($this->groupedDataLabels as $key => $value) {
+ $data[] = $this->getFormattedTrendsData($trendsData, $inPercent, "data$key");
+ }
+
+ foreach ($data as $key => $value) {
+ $result['labels'] = $value['labels'];
+
+ if ($key === 0) {
+ $result['data']['total'] = $value['data'];
+ } else {
+ $result['data'][$this->groupedDataLabels[$key - 1]] = $value['data'];
+ }
+ }
+
+ return $result;
+ }
+ protected function getFormattedTrendsData(array $trendsData, bool $inPercent = false, string $dataLabel = 'data'): array
+ {
if (! $this->fillMissingData) {
$trendsData = $this->formatDate($trendsData);
- return $this->formatTrends($trendsData, $inPercent);
+ return $this->formatTrends($trendsData, $inPercent, $dataLabel);
} else {
if (! is_null($this->labelColumn)) {
- $trendsData = $this->formatTrends($trendsData, $inPercent);
+ $trendsData = $this->formatTrends($trendsData, $inPercent, $dataLabel);
return $this->populateMissingData($this->getLabelsData(), $trendsData);
}
if (is_array($this->period)) {
- return $this->populateMissingDataForPeriod($trendsData, $inPercent);
+ return $this->populateMissingDataForPeriod($trendsData, $inPercent, $dataLabel);
}
if (is_string($this->period)) {
$trendsData = $this->formatDate($trendsData);
- return $this->populateMissingData($this->getPeriod(), $this->formatTrends($trendsData, $inPercent));
+ return $this->populateMissingData($this->getPeriod(), $this->formatTrends($trendsData, $inPercent, $dataLabel));
}
}
@@ -762,9 +799,28 @@ public function trends(bool $inPercent = false): array
];
}
- protected function formatTrends(array $data, bool $inPercent = false): array
+ /**
+ * Generate trends data for charts
+ */
+ public function trends(bool $inPercent = false): array
+ {
+ $trendsData = $this
+ ->trendsData()
+ ->toArray();
+
+ $trendsData = array_map(fn ($datum) => (array) $datum, $trendsData);
+
+ if (! empty($this->groupedDataLabels)) {
+ return $this->trendsWithMergedData($inPercent);
+ }
+
+ return $this->getFormattedTrendsData($trendsData, $inPercent);
+ }
+
+ protected function formatTrends(array $data, bool $inPercent = false, string $dataLabel = 'data'): array
{
$total = 0;
+
$result = [
'labels' => [],
'data' => [],
@@ -772,8 +828,8 @@ protected function formatTrends(array $data, bool $inPercent = false): array
foreach ($data as $datum) {
$result['labels'][] = $datum['label'];
- $result['data'][] = $datum['data'];
- $total += $datum['data'];
+ $result['data'][] = (int) $datum[$dataLabel];
+ $total += $datum[$dataLabel];
}
if (! $inPercent) {