Skip to content

Commit f2a613e

Browse files
authored
Merge pull request #63 from MortalFlesh/feature/apply-function
Add apply function
2 parents 858074d + 4c7b926 commit f2a613e

File tree

8 files changed

+344
-13
lines changed

8 files changed

+344
-13
lines changed

src/ApiFilter.php

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,17 @@
77
use Lmc\ApiFilter\Constant\Priority;
88
use Lmc\ApiFilter\Entity\Filterable;
99
use Lmc\ApiFilter\Exception\ApiFilterExceptionInterface;
10+
use Lmc\ApiFilter\Exception\TupleException;
1011
use Lmc\ApiFilter\Filter\FilterInterface;
1112
use Lmc\ApiFilter\Filters\FiltersInterface;
1213
use Lmc\ApiFilter\Service\FilterApplicator;
1314
use Lmc\ApiFilter\Service\FilterFactory;
1415
use Lmc\ApiFilter\Service\FunctionCreator;
1516
use Lmc\ApiFilter\Service\Functions;
1617
use Lmc\ApiFilter\Service\QueryParametersParser;
18+
use MF\Collection\Exception\TupleExceptionInterface;
19+
use MF\Collection\Immutable\ITuple;
20+
use MF\Collection\Immutable\Tuple;
1721

1822
class ApiFilter
1923
{
@@ -288,4 +292,42 @@ public function executeFunction(string $functionName, array $queryParameters, $f
288292
->execute($functionName, $filters, new Filterable($filterable))
289293
->getValue();
290294
}
295+
296+
/**
297+
* Apply a function with parsed query parameters and prepare values
298+
*
299+
* It will parse filters and apply registered function with parsed filters + prepare values for applied function
300+
*
301+
* @example
302+
* Applying a function directly on filterable
303+
* [$sql, $preparedValues] = $apiFilter
304+
* ->declareFunction('fullName', ['firstName', 'surname'])
305+
* ->applyFunction('fullName', ['fullName' => '(Jon, Snow)'], 'SELECT * FROM person');
306+
*
307+
* $sql: SELECT * FROM person WHERE firstName = :firstName_function_parameter AND surname = :surname_function_parameter
308+
* $preparedValues: ['firstName_function_parameter' => 'Jon', 'surname_function_parameter' => 'Snow']
309+
*
310+
* @see ApiFilter::declareFunction()
311+
* @see ApiFilter::registerFunction()
312+
* @see ApiFilter::executeFunction() if you just want to bypass ApiFilter applicators
313+
*
314+
* @param mixed $filterable of type <T>
315+
* @throws ApiFilterExceptionInterface
316+
* @return ITuple (<U>, array) where <U> is the output of the registered function and array contains prepared values
317+
*/
318+
public function applyFunction(string $functionName, array $queryParameters, $filterable): ITuple
319+
{
320+
try {
321+
$filters = $this->parser->parse($queryParameters);
322+
$this->applicator->setFilters($filters);
323+
$filterable = new Filterable($filterable);
324+
325+
$appliedFilterable = $this->functions->execute($functionName, $filters, $filterable);
326+
$preparedValues = $this->applicator->getPreparedValues($filters, $filterable);
327+
328+
return Tuple::of($appliedFilterable->getValue(), $preparedValues);
329+
} catch (TupleExceptionInterface $e) {
330+
throw TupleException::forBaseTupleException($e);
331+
}
332+
}
291333
}

src/Filters/Filters.php

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
use Lmc\ApiFilter\Applicator\ApplicatorInterface;
66
use Lmc\ApiFilter\Assertion;
77
use Lmc\ApiFilter\Entity\Filterable;
8+
use Lmc\ApiFilter\Filter\FilterFunction;
89
use Lmc\ApiFilter\Filter\FilterIn;
910
use Lmc\ApiFilter\Filter\FilterInterface;
1011
use Lmc\ApiFilter\Filter\FunctionParameter;
@@ -48,11 +49,19 @@ public function getIterator(): iterable
4849
yield from $this->filters;
4950
}
5051

51-
public function getPreparedValues(ApplicatorInterface $applicator): array
52-
{
52+
public function getPreparedValues(
53+
ApplicatorInterface $applicator,
54+
callable $findParametersForFunction,
55+
callable $findParameterDefinitions
56+
): array {
5357
$preparedValues = [];
5458
foreach ($this->filters as $filter) {
55-
$preparedValues += $applicator->getPreparedValue($filter);
59+
$preparedValues += $filter instanceof FilterFunction
60+
? $applicator->getPreparedValuesForFunction(
61+
$findParametersForFunction($filter),
62+
$findParameterDefinitions($filter)
63+
)
64+
: $applicator->getPreparedValue($filter);
5665
}
5766

5867
return $preparedValues;

src/Filters/FiltersInterface.php

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,15 @@ interface FiltersInterface extends IEnumerable
1616
*/
1717
public function applyAllTo(Filterable $filterable, FilterApplicator $filterApplicator): Filterable;
1818

19-
public function getPreparedValues(ApplicatorInterface $applicator): array;
19+
/**
20+
* @param callable $findParametersForFunction (FilterFunction): FunctionParameter[]
21+
* @param callable $findParameterDefinitions (FilterFunction): ParameterDefinition[]
22+
*/
23+
public function getPreparedValues(
24+
ApplicatorInterface $applicator,
25+
callable $findParametersForFunction,
26+
callable $findParameterDefinitions
27+
): array;
2028

2129
public function hasFilter(FilterInterface $filter): bool;
2230

src/Service/FilterApplicator.php

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,14 @@ public function applyAll(FiltersInterface $filters, Filterable $filterable): Fil
9898

9999
public function getPreparedValues(FiltersInterface $filters, Filterable $filterable): array
100100
{
101-
return $filters->getPreparedValues($this->findApplicatorFor($filterable));
101+
return $filters->getPreparedValues(
102+
$this->findApplicatorFor($filterable),
103+
function (FilterFunction $filterFunction) {
104+
return $this->getParametersForFunction($filterFunction);
105+
},
106+
function (FilterFunction $filterFunction) {
107+
return $this->functions->getParameterDefinitionsFor($filterFunction->getColumn());
108+
}
109+
);
102110
}
103111
}

src/Service/QueryParametersParser.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
use Lmc\ApiFilter\Constant\Priority;
66
use Lmc\ApiFilter\Exception\TupleException;
7+
use Lmc\ApiFilter\Filter\FilterInterface;
78
use Lmc\ApiFilter\Filters\Filters;
89
use Lmc\ApiFilter\Filters\FiltersInterface;
910
use Lmc\ApiFilter\Service\Parser\FunctionParser;
@@ -64,6 +65,7 @@ public function parse(array $queryParameters): FiltersInterface
6465
}
6566
}
6667

68+
/** @return FilterInterface[] */
6769
private function parseFilters(array $queryParameters): iterable
6870
{
6971
foreach ($queryParameters as $rawColumn => $rawValue) {

tests/AbstractTestCase.php

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ protected function assertDqlWhere(?array $expectedDqlWhere, QueryBuilder $queryB
3232
if ($expectedDqlWhere === null) {
3333
$this->assertNull($where);
3434
} else {
35-
$this->assertSame($expectedDqlWhere, $where->getParts());
35+
$this->assertSameValues($expectedDqlWhere, $where->getParts());
3636
}
3737
}
3838

@@ -63,4 +63,20 @@ protected function iteratorToArray(iterable $iterable): array
6363

6464
return $result;
6565
}
66+
67+
protected function assertSameValues(array $expected, array $actual): void
68+
{
69+
if (!empty($expected)) {
70+
[$key] = array_keys($expected);
71+
if (is_int($key)) {
72+
sort($expected);
73+
sort($actual);
74+
} else {
75+
asort($expected);
76+
asort($actual);
77+
}
78+
}
79+
80+
$this->assertSame($expected, $actual);
81+
}
6682
}

0 commit comments

Comments
 (0)