Skip to content

Commit f7dcf7c

Browse files
authored
Feature/Implement paginated result for EloquentCriteriaParser and related classes. (#8)
1 parent 155821b commit f7dcf7c

File tree

20 files changed

+475
-176
lines changed

20 files changed

+475
-176
lines changed

.github/workflows/documentation.yml

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,20 +6,19 @@ on:
66

77
jobs:
88
publish:
9+
name: Publish Documentation
910
runs-on: ubuntu-latest
1011

11-
strategy:
12-
matrix:
13-
php-version: [ '8.2' ]
14-
1512
steps:
1613
- name: Checkout source code
17-
uses: actions/checkout@v2
14+
uses: actions/checkout@v3
15+
with:
16+
fetch-depth: 0
1817

1918
- name: Install PHP
2019
uses: shivammathur/setup-php@v2
2120
with:
22-
php-version: ${{ matrix.php-version }}
21+
php-version: '8.3'
2322
extensions: mbstring
2423
coverage: xdebug
2524
tools: composer:v2
@@ -45,4 +44,4 @@ jobs:
4544
with:
4645
path: "wiki/"
4746
env:
48-
GH_PERSONAL_ACCESS_TOKEN: ${{ secrets.GH_PERSONAL_ACCESS_TOKEN }}
47+
GH_PERSONAL_ACCESS_TOKEN: ${{ secrets.GH_PERSONAL_ACCESS_TOKEN }}

.github/workflows/sonar.yml

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
name: SonarCloud
2+
3+
on:
4+
push:
5+
branches: [ 'main' ]
6+
pull_request:
7+
types: [ 'opened', 'synchronize', 'reopened' ]
8+
9+
jobs:
10+
sonarcloud:
11+
name: SonarCloud Analysis
12+
runs-on: ubuntu-latest
13+
14+
steps:
15+
- name: Checkout source code
16+
uses: actions/checkout@v3
17+
with:
18+
fetch-depth: 0
19+
20+
- name: Install PHP
21+
uses: shivammathur/setup-php@v2
22+
with:
23+
php-version: '8.3'
24+
coverage: xdebug
25+
tools: composer:v2
26+
27+
- name: Cache dependencies
28+
uses: actions/[email protected]
29+
with:
30+
path: ~/.composer/cache
31+
key: php-${{ matrix.php-version }}-composer-${{ hashFiles('**/composer.json') }}
32+
restore-keys: php-${{ matrix.php-version }}-composer-
33+
34+
- name: Validate composer.json and composer.lock
35+
run: composer validate
36+
37+
- name: Install Dependencies
38+
run: composer install --no-ansi --no-interaction --no-scripts --no-progress --prefer-dist
39+
40+
- name: Execute Unit, Integration and Acceptance Tests
41+
run: composer test
42+
43+
- name: Fix paths in coverage reports for Sonar
44+
run: |
45+
sed -i 's@'$GITHUB_WORKSPACE'@/github/workspace/@g' coverage.xml
46+
sed -i 's@'$GITHUB_WORKSPACE'@/github/workspace/@g' test.xml
47+
48+
- name: Run SonarCloud Scan
49+
uses: SonarSource/sonarcloud-github-action@v2
50+
with:
51+
projectBaseDir: .
52+
env:
53+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
54+
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}

.github/workflows/test.yml

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,12 @@ on:
88

99
jobs:
1010
test:
11+
name: Test on PHP ${{ matrix.php-version }}
1112
runs-on: ubuntu-latest
1213

1314
strategy:
1415
matrix:
15-
php-version: [ '8.2', '8.3' ]
16+
php-version: [ '8.2', '8.3', '8.4']
1617

1718
steps:
1819
- name: Checkout source code
@@ -37,7 +38,7 @@ jobs:
3738
- name: Validate composer.json and composer.lock
3839
run: composer validate
3940

40-
- name: Install dependencies
41+
- name: Install Dependencies
4142
if: steps.composer-cache.outputs.cache-hit != 'true'
4243
run: composer install --prefer-dist --no-progress --no-suggest
4344

@@ -46,14 +47,3 @@ jobs:
4647

4748
- name: Execute Unit, Integration and Acceptance Tests
4849
run: composer test
49-
50-
- name: Fix coverage.xml for Sonar
51-
run: |
52-
sed -i 's@'$GITHUB_WORKSPACE'@/github/workspace/@g' coverage.xml
53-
sed -i 's@'$GITHUB_WORKSPACE'@/github/workspace/@g' test.xml
54-
55-
- name: SonarCloud Scan
56-
uses: SonarSource/sonarcloud-github-action@master
57-
env:
58-
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
59-
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}

.serena/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/cache

.serena/project.yml

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
# language of the project (csharp, python, rust, java, typescript, go, cpp, or ruby)
2+
# * For C, use cpp
3+
# * For JavaScript, use typescript
4+
# Special requirements:
5+
# * csharp: Requires the presence of a .sln file in the project folder.
6+
language: php
7+
8+
# whether to use the project's gitignore file to ignore files
9+
# Added on 2025-04-07
10+
ignore_all_files_in_gitignore: true
11+
# list of additional paths to ignore
12+
# same syntax as gitignore, so you can use * and **
13+
# Was previously called `ignored_dirs`, please update your config if you are using that.
14+
# Added (renamed) on 2025-04-07
15+
ignored_paths: []
16+
17+
# whether the project is in read-only mode
18+
# If set to true, all editing tools will be disabled and attempts to use them will result in an error
19+
# Added on 2025-04-18
20+
read_only: false
21+
22+
# list of tool names to exclude. We recommend not excluding any tools, see the readme for more details.
23+
# Below is the complete list of tools for convenience.
24+
# To make sure you have the latest list of tools, and to view their descriptions,
25+
# execute `uv run scripts/print_tool_overview.py`.
26+
#
27+
# * `activate_project`: Activates a project by name.
28+
# * `check_onboarding_performed`: Checks whether project onboarding was already performed.
29+
# * `create_text_file`: Creates/overwrites a file in the project directory.
30+
# * `delete_lines`: Deletes a range of lines within a file.
31+
# * `delete_memory`: Deletes a memory from Serena's project-specific memory store.
32+
# * `execute_shell_command`: Executes a shell command.
33+
# * `find_referencing_code_snippets`: Finds code snippets in which the symbol at the given location is referenced.
34+
# * `find_referencing_symbols`: Finds symbols that reference the symbol at the given location (optionally filtered by type).
35+
# * `find_symbol`: Performs a global (or local) search for symbols with/containing a given name/substring (optionally filtered by type).
36+
# * `get_current_config`: Prints the current configuration of the agent, including the active and available projects, tools, contexts, and modes.
37+
# * `get_symbols_overview`: Gets an overview of the top-level symbols defined in a given file.
38+
# * `initial_instructions`: Gets the initial instructions for the current project.
39+
# Should only be used in settings where the system prompt cannot be set,
40+
# e.g. in clients you have no control over, like Claude Desktop.
41+
# * `insert_after_symbol`: Inserts content after the end of the definition of a given symbol.
42+
# * `insert_at_line`: Inserts content at a given line in a file.
43+
# * `insert_before_symbol`: Inserts content before the beginning of the definition of a given symbol.
44+
# * `list_dir`: Lists files and directories in the given directory (optionally with recursion).
45+
# * `list_memories`: Lists memories in Serena's project-specific memory store.
46+
# * `onboarding`: Performs onboarding (identifying the project structure and essential tasks, e.g. for testing or building).
47+
# * `prepare_for_new_conversation`: Provides instructions for preparing for a new conversation (in order to continue with the necessary context).
48+
# * `read_file`: Reads a file within the project directory.
49+
# * `read_memory`: Reads the memory with the given name from Serena's project-specific memory store.
50+
# * `remove_project`: Removes a project from the Serena configuration.
51+
# * `replace_lines`: Replaces a range of lines within a file with new content.
52+
# * `replace_symbol_body`: Replaces the full definition of a symbol.
53+
# * `restart_language_server`: Restarts the language server, may be necessary when edits not through Serena happen.
54+
# * `search_for_pattern`: Performs a search for a pattern in the project.
55+
# * `summarize_changes`: Provides instructions for summarizing the changes made to the codebase.
56+
# * `switch_modes`: Activates modes by providing a list of their names
57+
# * `think_about_collected_information`: Thinking tool for pondering the completeness of collected information.
58+
# * `think_about_task_adherence`: Thinking tool for determining whether the agent is still on track with the current task.
59+
# * `think_about_whether_you_are_done`: Thinking tool for determining whether the task is truly completed.
60+
# * `write_memory`: Writes a named memory (for future reference) to Serena's project-specific memory store.
61+
excluded_tools: []
62+
63+
# initial prompt for the project. It will always be given to the LLM upon activating the project
64+
# (contrary to the memories, which are loaded on demand).
65+
initial_prompt: ""
66+
67+
project_name: "on-laravel"

composer.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,15 @@
1313
"require": {
1414
"php": "^8.2.0",
1515
"complex-heart/sdk": "^2.0.0",
16+
"complex-heart/criteria": "^4.1",
1617
"laravel/framework": "^11.0.0"
1718
},
1819
"require-dev": {
1920
"pestphp/pest": "*",
2021
"pestphp/pest-plugin-faker": "^2.0",
2122
"phpstan/phpstan": "*",
22-
"mockery/mockery": "^1.6"
23+
"mockery/mockery": "^1.6",
24+
"laravel/pint": "^1.25"
2325
},
2426
"autoload": {
2527
"psr-4": {

phpstan.neon

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
parameters:
2+
paths:
3+
- src/
4+
- tests/
5+
level: 9
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace ComplexHeart\Infrastructure\Laravel\Pagination;
6+
7+
use ComplexHeart\Domain\Criteria\Contracts\PaginatedResult;
8+
use Illuminate\Support\Collection;
9+
10+
/**
11+
* Class PaginatedCollection
12+
*
13+
* @template TKey of array-key
14+
* @template TValue
15+
*
16+
* @extends Collection<TKey, TValue>
17+
*
18+
* @author Unay Santisteban <[email protected]>
19+
*/
20+
class PaginatedCollection extends Collection implements PaginatedResult
21+
{
22+
/**
23+
* PaginatedCollection constructor.
24+
*
25+
* @param iterable<TKey, TValue> $items
26+
*/
27+
public function __construct(
28+
iterable $items = [],
29+
private readonly int $total = 0,
30+
private readonly int $perPage = 0,
31+
private readonly int $currentPage = 1,
32+
) {
33+
parent::__construct($items);
34+
}
35+
36+
/**
37+
* Create a new PaginatedCollection instance.
38+
*
39+
* @param array<TKey, TValue> $items
40+
* @return self<TKey, TValue>
41+
*/
42+
public static function paginate(
43+
array $items,
44+
int $total,
45+
int $perPage,
46+
int $currentPage
47+
): self {
48+
return new self($items, $total, $perPage, $currentPage);
49+
}
50+
51+
/**
52+
* Get the items for the current page
53+
*
54+
* @return array<int, mixed>
55+
*/
56+
public function items(): array
57+
{
58+
return $this->all();
59+
}
60+
61+
/**
62+
* Get total number of items across all pages
63+
*/
64+
public function total(): int
65+
{
66+
return $this->total;
67+
}
68+
69+
/**
70+
* Get items per page
71+
*/
72+
public function perPage(): int
73+
{
74+
return $this->perPage;
75+
}
76+
77+
/**
78+
* Get current page number (1-indexed)
79+
*/
80+
public function currentPage(): int
81+
{
82+
return $this->currentPage;
83+
}
84+
85+
/**
86+
* Get last page number
87+
*/
88+
public function lastPage(): int
89+
{
90+
if ($this->perPage === 0) {
91+
return 1;
92+
}
93+
94+
return (int) ceil($this->total / $this->perPage);
95+
}
96+
97+
/**
98+
* Check if there are more pages after the current one
99+
*/
100+
public function hasMorePages(): bool
101+
{
102+
return $this->currentPage < $this->lastPage();
103+
}
104+
105+
/**
106+
* Check if the current page is empty
107+
*/
108+
public function isEmpty(): bool
109+
{
110+
return parent::isEmpty();
111+
}
112+
113+
/**
114+
* Check if the current page is not empty
115+
*/
116+
public function isNotEmpty(): bool
117+
{
118+
return parent::isNotEmpty();
119+
}
120+
}

src/Infrastructure/Laravel/Persistence/Contracts/IlluminateCriteriaParser.php

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,23 +5,25 @@
55
namespace ComplexHeart\Infrastructure\Laravel\Persistence\Contracts;
66

77
use ComplexHeart\Domain\Criteria\Criteria;
8+
use ComplexHeart\Domain\Criteria\Page;
89
use Illuminate\Contracts\Database\Query\Builder;
9-
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
1010

1111
/**
1212
* Interface IlluminateCriteriaParser
1313
*
1414
* @author Unay Santisteban <[email protected]>
15-
* @package ComplexHeart\Infrastructure\Laravel\Persistence\Contracts
1615
*/
1716
interface IlluminateCriteriaParser
1817
{
1918
/**
2019
* Apply a criteria into the given QueryBuilder.
2120
*
22-
* @param Builder $builder
23-
* @param Criteria $criteria
24-
* @return Builder|LengthAwarePaginator<int, mixed>
21+
* Returns an array containing:
22+
* - 'builder': Query builder with filters, ordering, and pagination applied
23+
* - 'total': Total count before pagination
24+
* - 'page': Page value object from criteria
25+
*
26+
* @return array{builder: Builder, total: int, page: Page, currentPage: int}
2527
*/
26-
public function applyCriteria(Builder $builder, Criteria $criteria): Builder|LengthAwarePaginator;
28+
public function applyCriteria(Builder $builder, Criteria $criteria): array;
2729
}

0 commit comments

Comments
 (0)