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
63 changes: 46 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,31 +12,60 @@ interface.
Just install the package from Packagist using composer:

```bash
composer require complexheart/php-criteria
composer require complex-heart/criteria
```

## Usage

Just import the class:
Import the class and use the fluent interface:

```php
namespace ComplexHeart\Test\Domain\Criteria;

$criteria = Criteria::createDefault()
->addFilterEqual('name', 'Vincent')
->addFilterNotEqual('surname', 'Winnfield')
->addFilterGreaterThan('money', '10000')
->addFilterGreaterOrEqualThan('age', '35')
->addFilterLessThan('cars', '2')
->addFilterLessOrEqualThan('houses', '2')
->addFilterLike('favoriteMeal', 'pork')
->addFilterIn('boss', ['marcellus', 'mia'])
->addFilterNotIn('hates', ['ringo', 'yolanda'])
->withOrder(Order::createDescBy('name'))
namespace ComplexHeart\Domain\Criteria;

// Match the users with status active and more than 7k followers and from Spain and France
$g1 = FilterGroup::create()
->addFilterEqual('status', 1)
->addFilterGreaterThan('followers', 7000)
->addFilterIn('country', ['es', 'fr']);

$criteria = Criteria::default()
->withFilterGroup($g1)
->withOrderBy('surname')
->withOrderType(Order::TYPE_ASC)
->withPageLimit(10)
->withPageOffset(5);

$users = $repository->match($criteria);

// alternative, same as above
$criteria = Criteria::default()
->withFilterGroup(FilterGroup::create()
->addFilterEqual('status', 1)
->addFilterGreaterThan('followers', 7000)
->addFilterIn('country', ['es', 'fr']))
->withOrderBy('surname')
->withOrderType(Order::TYPE_ASC)
->withPageLimit(10)
->withPageOffset(5)
->withPageOffset(5);

// In SQL, we may have something like:
// WHERE status = 1 AND followers >= 700 AND country in ('es', 'fr')

$customers = $customerRepository->match($criteria);
$users = $repository->match($criteria);
```

A `FilterGroup` is a set of filters or conditions that must match all together (`AND`). To match one group or another
(OR), just add more `FilterGroup`.

```php

// Match articles with given term in title, or in tagline, or in content.
$criteria = Criteria::default()
->withFilterGroup(FilterGroup::create()->addFilterContains('title', $term))
->withFilterGroup(FilterGroup::create()->addFilterContains('tagline', $term))
->withFilterGroup(FilterGroup::create()->addFilterContains('content', $term))
->withOrderBy('created_at')
->withOrderType(Order::TYPE_ASC)
->withPageLimit(10)
->withPageOffset(5);
```
136 changes: 55 additions & 81 deletions src/Criteria.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,17 @@

namespace ComplexHeart\Domain\Criteria;

use Closure;
use ComplexHeart\Domain\Contracts\Model\ValueObject;
use ComplexHeart\Domain\Model\IsValueObject;

use function Lambdish\Phunctional\map;

/**
* Class Criteria
*
* @author Unay Santisteban <usantisteban@othercode.io>
* @package ComplexHeart\SDK\Domain\Criteria
* @package ComplexHeart\Domain\Criteria
*/
final class Criteria implements ValueObject
{
Expand All @@ -20,130 +23,97 @@ final class Criteria implements ValueObject
/**
* Criteria constructor.
*
* @param FilterGroup<Filter> $filters
* @param array<FilterGroup<Filter>> $groups
* @param Order $order
* @param Page $page
*/
public function __construct(
private readonly FilterGroup $filters,
private readonly array $groups,
private readonly Order $order,
private readonly Page $page,
) {
$this->check();
}

public static function create(FilterGroup $filters, Order $order, Page $page): self
/**
* @param array<FilterGroup<Filter>> $groups
* @param Order $order
* @param Page $page
* @return Criteria
*/
public static function create(array $groups, Order $order, Page $page): self
{
return new self($filters, $order, $page);
return new self($groups, $order, $page);
}

public static function createDefault(): self
public static function default(): self
{
return self::create(FilterGroup::create(), Order::none(), Page::create());
return self::create([], Order::none(), Page::create());
}

public function withFilters(FilterGroup $filters): self
/**
* Returns a new instance of the criteria with the given FilterGroups.
*
* @param array<FilterGroup<Filter>> $groups
* @return Criteria
*/
public function withFilterGroups(array $groups): self
{
return new self($filters, $this->order, $this->page);
return self::create($groups, $this->order, $this->page);
}

/**
* Returns a new instance of the criteria adding the given FilterGroup.
*
* @param FilterGroup|Closure $group
* @return $this
*/
public function withFilterGroup(FilterGroup|Closure $group): self
{
if (is_callable($group)) {
$group = $group(new FilterGroup());
}

return $this->withFilterGroups(array_merge($this->groups, [$group]));
}

public function withOrder(Order $order): self
{
return new self($this->filters, $order, $this->page);
return self::create($this->groups, $order, $this->page);
}

public function withOrderBy(string $field): self
{
return new self($this->filters, Order::create($field, $this->orderType()), $this->page);
return self::create($this->groups, Order::create($field, $this->orderType()), $this->page);
}

public function withOrderType(string $type): self
{
return new self($this->filters, Order::create($this->orderBy(), $type), $this->page);
return self::create($this->groups, Order::create($this->orderBy(), $type), $this->page);
}

public function withPage(Page $page): self
{
return new self($this->filters, $this->order, $page);
return self::create($this->groups, $this->order, $page);
}

public function withPageOffset(int $offset): self
{
return new self($this->filters, $this->order, Page::create($this->pageLimit(), $offset));
return self::create($this->groups, $this->order, Page::create($this->pageLimit(), $offset));
}

public function withPageLimit(int $limit): self
{
return new self($this->filters, $this->order, Page::create($limit, $this->pageOffset()));
}

public function filters(): FilterGroup
{
return $this->filters;
}

public function addFilterEqual(string $field, mixed $value): self
{
$this->filters->addFilter(Filter::createEqual($field, $value));
return $this;
}

public function addFilterNotEqual(string $field, mixed $value): self
{
$this->filters->addFilter(Filter::createNotEqual($field, $value));
return $this;
}

public function addFilterGreaterThan(string $field, string $value): self
{
$this->filters->addFilter(Filter::createGreaterThan($field, $value));
return $this;
}

public function addFilterGreaterOrEqualThan(string $field, string $value): self
{
$this->filters->addFilter(Filter::createGreaterOrEqualThan($field, $value));
return $this;
}

public function addFilterLessThan(string $field, string $value): self
{
$this->filters->addFilter(Filter::createLessThan($field, $value));
return $this;
}

public function addFilterLessOrEqualThan(string $field, string $value): self
{
$this->filters->addFilter(Filter::createLessOrEqualThan($field, $value));
return $this;
return self::create($this->groups, $this->order, Page::create($limit, $this->pageOffset()));
}

/**
* @param string $field
* @param array<scalar> $value
* @return $this
*/
public function addFilterIn(string $field, array $value): self
{
$this->filters->addFilter(Filter::createIn($field, $value));
return $this;
}

/**
* @param string $field
* @param array<scalar> $value
* @return $this
* Returns the list of group filters.
*
* @return array<FilterGroup<Filter>>
*/
public function addFilterNotIn(string $field, array $value): self
{
$this->filters->addFilter(Filter::createNotIn($field, $value));
return $this;
}

public function addFilterLike(string $field, string $value): self
public function groups(): array
{
$this->filters->addFilter(Filter::createLike($field, $value));
return $this;
return $this->groups;
}

public function order(): Order
Expand Down Expand Up @@ -178,6 +148,10 @@ public function pageLimit(): int

public function __toString(): string
{
return sprintf('%s#%s#%s', $this->filters, $this->order, $this->page);
$groups = join('||', map(fn(FilterGroup $group): string => $group->__toString(), $this->groups));
$order = $this->order->__toString();
$page = $this->page->__toString();

return sprintf('%s#%s#%s', $groups, $order, $page);
}
}
15 changes: 15 additions & 0 deletions src/Filter.php
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,21 @@ public static function createLike(string $field, mixed $value): self
return self::create($field, Operator::like(), $value);
}

public static function createNotLike(string $field, mixed $value): self
{
return self::create($field, Operator::notLike(), $value);
}

public static function createContains(string $field, mixed $value): self
{
return self::create($field, Operator::contains(), $value);
}

public static function createNotContains(string $field, mixed $value): self
{
return self::create($field, Operator::notContains(), $value);
}

/**
* Retrieve the filter field value object.
*
Expand Down
Loading