Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
98.08% covered (success)
98.08%
51 / 52
90.00% covered (success)
90.00%
9 / 10
CRAP
0.00% covered (danger)
0.00%
0 / 1
Filter
98.08% covered (success)
98.08%
51 / 52
90.00% covered (success)
90.00%
9 / 10
29
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 create
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 __toString
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 empty
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 params
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 parse
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
1
 parseQueryFilters
100.00% covered (success)
100.00%
15 / 15
100.00% covered (success)
100.00%
1 / 1
5
 filterGroupToFilterString
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 buildFilterWithParams
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
1
 createFilterWithParams
94.74% covered (success)
94.74%
18 / 19
0.00% covered (danger)
0.00%
0 / 1
16.04
1<?php
2
3declare(strict_types=1);
4
5namespace Projom\Storage\SQL\Component;
6
7use Projom\Storage\SQL\Component\ComponentInterface;
8use Projom\Storage\SQL\Component\Column;
9use Projom\Storage\SQL\Component\Filter\Between;
10use Projom\Storage\SQL\Component\Filter\In;
11use Projom\Storage\SQL\Component\Filter\Nullable;
12use Projom\Storage\SQL\Component\Filter\Standard;
13use Projom\Storage\SQL\Component\Filter\Util;
14use Projom\Storage\SQL\Util\Operator;
15
16class Filter implements ComponentInterface
17{
18    private readonly string $filter;
19    private readonly array $params;
20
21    private int $filterID = 0;
22
23    public function __construct(array $queryFilters)
24    {
25        $this->parse($queryFilters);
26    }
27
28    public static function create(array $queryFilters): Filter
29    {
30        return new Filter($queryFilters);
31    }
32
33    public function __toString(): string
34    {
35        return $this->filter;
36    }
37
38    public function empty(): bool
39    {
40        return empty($this->filter);
41    }
42
43    public function params(): array
44    {
45        return $this->params;
46    }
47
48    private function parse(array $queryFilters): void
49    {
50        [$filterGroups, $filterParams] = $this->parseQueryFilters($queryFilters);
51
52        $filter = Util::join($filterGroups, ' ');
53
54        // Remove empty params and set.
55        $filterParams = Util::removeEmpty($filterParams);
56        $params = Util::flatten($filterParams);
57
58        $this->filter = $filter;
59        $this->params = $params;
60    }
61
62    private function parseQueryFilters(array $queryFilters): array
63    {
64        $filterGroups = [];
65        $filterParams = [];
66
67        foreach ($queryFilters as [$queryFilterList, $outerLogicalOperator]) {
68
69            $filterGroup = [];
70            foreach ($queryFilterList as [$field, $operator, $value, $innerLogicalOperator]) {
71
72                [$filter, $params] = $this->buildFilterWithParams($field, $operator, $value);
73
74                if ($filterGroup)
75                    $filterGroup[] = $innerLogicalOperator->value;
76
77                $filterGroup[] = $filter;
78                $filterParams[] = $params;
79            }
80
81            if ($filterGroups)
82                $filterGroups[] = $outerLogicalOperator->value;
83
84            $filterGroups[] = $this->filterGroupToFilterString($filterGroup);
85        }
86
87        $filterGroups = Util::addParentheses($filterGroups);
88
89        return [$filterGroups, $filterParams];
90    }
91
92    private function filterGroupToFilterString(array $filterGroup): string
93    {
94        $filterGroupString = Util::join($filterGroup, ' ');
95        $filterGroupString = "$filterGroupString )";
96        return $filterGroupString;
97    }
98
99    private function buildFilterWithParams(string $field, Operator $operator, mixed $value): array 
100    {
101        $this->filterID++;
102        $column = Column::create([$field]);
103        [$filter, $params] = $this->createFilterWithParams($column, $operator, $value);
104
105        return [$filter, $params];
106    }
107
108    private function createFilterWithParams(Column $column, Operator $operator, mixed $value): array
109    {
110        switch ($operator) {
111            case Operator::IS_NULL:
112            case Operator::IS_NOT_NULL:
113                return Nullable::create($column, $operator);
114
115            case Operator::IN:
116            case Operator::NOT_IN:
117                return In::create($column, $operator, $value, $this->filterID);
118
119            case Operator::EQ:
120            case Operator::NE:
121            case Operator::GT:
122            case Operator::GTE:
123            case Operator::LT:
124            case Operator::LTE:
125            case Operator::LIKE:
126            case Operator::NOT_LIKE:
127                return Standard::create($column, $operator, $value, $this->filterID);
128
129            case Operator::BETWEEN:
130            case Operator::NOT_BETWEEN:
131                return Between::create($column, $operator, $value, $this->filterID);
132                
133
134            default:
135                throw new \Exception("Operator not implemented: {$operator->value}", 400);
136        }
137    }
138}