Skip to content

feat: Add groupBy as a new feature in repository #622

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Feb 17, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions docs-v2/content/en/api/repositories.md
Original file line number Diff line number Diff line change
Expand Up @@ -819,3 +819,14 @@ public static $withs = ['posts'];
`withs` is not a typo. Laravel uses the `with` property on models, on repositories we use `$withs`, it's not a typo.

</alert>

## Group by

The group by filter is useful when you want to group the results by a certain column.

```php
class PostRepository extends Repository
{
public static array $groupBy = ['user_id'];
}
```
5 changes: 5 additions & 0 deletions src/Http/Requests/RestifyRequest.php
Original file line number Diff line number Diff line change
Expand Up @@ -77,4 +77,9 @@ public function filters(): array
? $this->input('filters', [])
: (json_decode(base64_decode($this->input('filters')), true) ?? []);
}

public function groupBy(): ?string
{
return $this->input('group_by');
}
}
5 changes: 5 additions & 0 deletions src/Repositories/Repository.php
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,11 @@ class Repository implements JsonSerializable, RestifySearchable
*/
public static array $sort;

/**
* The list of fields that can be used for grouping.
*/
public static array $groupBy = [];

/**
* Attribute that should be used for displaying single model.
*/
Expand Down
25 changes: 25 additions & 0 deletions src/Services/Search/RepositorySearchService.php
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ public function search(RestifyRequest $request, Repository $repository): Builder

$query = $this->applyFilters($request, $repository, $query);

$query = $this->applyGroupBy($request, $repository, $query);

$ordersBuilder = $this->prepareOrders($request, $query);

return tap(
Expand Down Expand Up @@ -217,6 +219,29 @@ protected function applyFilters(RestifyRequest $request, Repository $repository,
return $query;
}

protected function applyGroupBy(RestifyRequest $request, Repository $repository, $query)
{
if (! $request->has('group_by')) {
return $query;
}

$model = $query->getModel();
$groupByColumns = explode(',', $request->input('group_by'));

foreach ($groupByColumns as $column) {
if (! in_array($column, $repository::$groupBy)) {
abort(422, sprintf(
'The column [%s] is not allowed for grouping. Allowed columns are: %s',
$column,
implode(', ', $repository::$groupBy)
));
}
$query->groupBy($model->qualifyColumn($column));
}

return $query;
}

public static function make(): static
{
return new static;
Expand Down
57 changes: 57 additions & 0 deletions tests/Feature/RepositoryGroupByTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
<?php

namespace Binaryk\LaravelRestify\Tests\Feature;

use Binaryk\LaravelRestify\Tests\Fixtures\User\User;
use Binaryk\LaravelRestify\Tests\Fixtures\User\UserRepository;
use Binaryk\LaravelRestify\Tests\IntegrationTestCase;

class RepositoryGroupByTest extends IntegrationTestCase
{
public function test_it_can_group_by_the_results(): void
{
User::factory(4)->create([
'name' => 'John Doe',
]);

User::factory(4)->create([
'name' => 'Second John Doe',
]);

UserRepository::$groupBy = ['name'];

$this->getJson(UserRepository::route(query: ['group_by' => 'name']))->assertJsonCount(2, 'data');
}

public function test_it_can_group_by_the_results_multiple_columns(): void
{
User::factory(3)->create([
'name' => 'John Doe',
'avatar' => 'image.jpg',
]);

User::factory(1)->create([
'name' => 'Another John Doe',
'avatar' => 'image.jpg',
]);

UserRepository::$groupBy = ['name', 'avatar'];

$this->getJson(UserRepository::route(query: ['group_by' => 'name,avatar']))->assertJsonCount(2, 'data');
}

public function test_it_can_not_group_by_the_results_because_wrong_column(): void
{
User::factory(4)->create([
'name' => 'John Doe',
]);

User::factory(4)->create([
'name' => 'Second John Doe',
]);

UserRepository::$groupBy = ['email'];

$this->getJson(UserRepository::route(query: ['group_by' => 'name']))->assertUnprocessable();
}
}
Loading