Skip to content

Commit

Permalink
Add group data of column with multiple values
Browse files Browse the repository at this point in the history
  • Loading branch information
eliseekn committed Jun 22, 2024
1 parent 8e65a3e commit 5413c70
Show file tree
Hide file tree
Showing 4 changed files with 93 additions and 22 deletions.
1 change: 1 addition & 0 deletions .idea/laravel-metrics.iml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions .idea/php.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 13 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
100 changes: 78 additions & 22 deletions src/LaravelMetrics.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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')
Expand All @@ -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) {
Expand All @@ -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) {
Expand All @@ -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);
Expand All @@ -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);
})
Expand All @@ -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);
Expand All @@ -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);
Expand All @@ -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[] = [
Expand Down Expand Up @@ -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));
}
}

Expand All @@ -762,18 +799,37 @@ 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' => [],
];

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) {
Expand Down

0 comments on commit 5413c70

Please sign in to comment.