Skip to content
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
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ interface IlluminateCriteriaParser
* - 'builder': Query builder with filters, ordering, and pagination applied
* - 'total': Total count before pagination
* - 'page': Page value object from criteria
* - 'currentPage': Current page number (1-indexed)
*
* @return array{builder: Builder, total: int, page: Page, currentPage: int}
*/
Expand Down
125 changes: 106 additions & 19 deletions wiki/Eloquent-Criteria-Parser.md
Original file line number Diff line number Diff line change
@@ -1,30 +1,42 @@
# Eloquent Criteria Parser

Using the Eloquent Criteria Parser is quite simple. Just instantiate the class, next, pass a Criteria instance to
the `applyCriteria()` method along with a `EloquentQueryBuilder` instance:
The Eloquent Criteria Parser translates domain Criteria objects into Eloquent query builders with pagination support.

## Basic Usage

Instantiate the parser and pass a Criteria instance along with an `EloquentQueryBuilder`:

```php
$parser = new EloquentCriteriaParser();
$query = $parser->applyCriteria(User::query(), $criteria);
$result = $parser->applyCriteria(User::query(), $criteria);
```

The returned `EloquentQueryBuilder` has the criteria applied. You just need to call the `get` method to fetch the data
from the database.
The `applyCriteria()` method returns an array containing:

- `builder`: EloquentQueryBuilder with filters, ordering, and pagination applied
- `total`: Total count of records before pagination
- `page`: Page value object from the criteria
- `currentPage`: Current page number (1-indexed)

```php
$users = $query->get();
$items = $result['builder']->get();
$total = $result['total'];
$currentPage = $result['currentPage'];
```

Alternatively, you can pass an array of strings to map the attributes between the domain and the database.
## Attribute Mapping

Map domain attributes to database columns by passing an array to the constructor:

```php
$parser = new EloquentCriteriaParser([
'domain-attribute' => 'database-attribute',
]);
```

For example,
given the following table:
### Example

Given this users table:

```php
$this->builder->create('users', function (Blueprint $table) {
Expand All @@ -37,7 +49,7 @@ $this->builder->create('users', function (Blueprint $table) {
});
```

You may use the following configuration to use `name` and `surname` instead of `first_name` and `last_name`:
Use `name` and `surname` in your domain instead of `first_name` and `last_name`:

```php
$parser = new EloquentCriteriaParser([
Expand All @@ -46,19 +58,94 @@ $parser = new EloquentCriteriaParser([
]);
```

A criteria search will be something like this:
## Repository Implementation with PaginatedCollection

The recommended pattern is to use `PaginatedCollection` in your repositories:

```php
use ComplexHeart\Domain\Criteria\Contracts\PaginatedResult;
use ComplexHeart\Infrastructure\Laravel\Pagination\PaginatedCollection;

class UsersEloquentRepository implements UserRepository
{
private IlluminateCriteriaParser $criteriaParser;

public function __construct()
{
$this->criteriaParser = new EloquentCriteriaParser([
'name' => 'first_name',
'surname' => 'last_name'
]);
}

public function match(Criteria $criteria): PaginatedResult
{
$result = $this->criteriaParser->applyCriteria(User::query(), $criteria);

$items = $result['builder']
->get()
->map(fn($row) => UserEntity::fromSource($row))
->toArray();

return PaginatedCollection::paginate(
items: $items,
total: $result['total'],
perPage: $result['page']->limit(),
currentPage: $result['currentPage'],
);
}
}
```

## PaginatedCollection

`PaginatedCollection` extends Laravel's `Collection` and implements the `PaginatedResult` interface from php-criteria.

### Collection Methods Support

You can use all Laravel Collection methods:

```php
$result = $repository->match($criteria);

// Standard Collection methods
$names = $result->map(fn($user) => $user->name)->toArray();
$emails = $result->pluck('email')->toArray();
$filtered = $result->filter(fn($user) => $user->isActive);
```

### Pagination Metadata

Access pagination information through these methods:

```php
$result->items(); // Get current page items as array
$result->total(); // Total records across all pages
$result->perPage(); // Items per page
$result->currentPage(); // Current page number (1-indexed)
$result->lastPage(); // Last page number
$result->hasMorePages(); // Check if more pages exist
$result->isEmpty(); // Check if current page is empty
$result->isNotEmpty(); // Check if current page has items
$result->count(); // Count items on current page
```

### Example Usage

```php
$criteria = Criteria::default()
->withFilterGroup(FilterGroup::create()
->addFilterEqual('name', 'Vicent'));
->addFilterEqual('name', 'Vincent'))
->withPageNumber(1, 25);

$builder = User::query();
$result = $repository->match($criteria);

$parser = new EloquentCriteriaParser();
$users = $parser
->applyCriteria($builder, $criteria)
->get();
```
if ($result->isNotEmpty()) {
echo "Page {$result->currentPage()} of {$result->lastPage()}";
echo "Showing {$result->count()} of {$result->total()} users";

This is useful to expose different attributes from different interfaces as HTTP, or CLI.
foreach ($result as $user) {
echo $user->name;
}
}
```