CriteriaFactory   A
last analyzed

Complexity

Total Complexity 32

Size/Duplication

Total Lines 187
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 4

Importance

Changes 0
Metric Value
wmc 32
lcom 1
cbo 4
dl 0
loc 187
rs 9.84
c 0
b 0
f 0

6 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 1
A build() 0 10 2
A buildInMemory() 0 10 2
A prepare() 0 23 5
D applyFilter() 0 78 19
A buildCustomCriteria() 0 25 3
1
<?php
2
3
namespace T4webInfrastructure;
4
5
use T4webDomainInterface\Infrastructure\CriteriaInterface;
6
use RuntimeException;
7
8
class CriteriaFactory
9
{
10
    /**
11
     * @var Config
12
     */
13
    protected $config;
14
15
    /**
16
     * CriteriaFactory constructor.
17
     * @param Config $config
18
     */
19
    public function __construct(Config $config)
20
    {
21
        $this->config = $config;
22
    }
23
24
    /**
25
     * @param string $entityName
26
     * @param array $filter
27
     * @return CriteriaInterface
28
     */
29
    public function build($entityName, array $filter = [])
30
    {
31
        if (!is_string($entityName)) {
32
            throw new RuntimeException(sprintf('Entity mame must be string, %s given', gettype($entityName)));
33
        }
34
35
        $criteria = new Criteria($entityName, $this->config);
36
37
        return $this->prepare($filter, $criteria);
38
    }
39
40
    /**
41
     * @param string $entityName
42
     * @param array $filter
43
     * @return CriteriaInterface
44
     */
45
    public function buildInMemory($entityName, array $filter = [])
46
    {
47
        if (!is_string($entityName)) {
48
            throw new RuntimeException(sprintf('Entity mame must be string, %s given', gettype($entityName)));
49
        }
50
51
        $criteria = new InMemoryCriteria($entityName);
52
53
        return $this->prepare($filter, $criteria);
54
    }
55
56
    /**
57
     * @param array $filter
58
     * @param CriteriaInterface $criteria
59
     * @return CriteriaInterface
60
     */
61
    protected function prepare(array $filter, $criteria)
62
    {
63
        if (empty($filter)) {
64
            return $criteria;
65
        }
66
67
        $relations = [];
68
        if (isset($filter['relations'])) {
69
            $relations = $filter['relations'];
70
            unset($filter['relations']);
71
        }
72
73
        $this->applyFilter($criteria, $filter);
74
75
        foreach ($relations as $relationEntity => $relationFilter) {
76
            if (!empty($relationFilter)) {
77
                $relation = $criteria->relation($relationEntity);
78
                $this->applyFilter($relation, $relationFilter);
79
            }
80
        }
81
82
        return $criteria;
83
    }
84
85
    /**
86
     * @param CriteriaInterface $criteria
87
     * @param array $filter
88
     * @return CriteriaInterface
89
     */
90
    private function applyFilter(CriteriaInterface $criteria, array $filter)
91
    {
92
        foreach ($filter as $expressionString => $value) {
93
94
            $criteriaMap = $this->config->getCriteriaMap($criteria->getEntityName());
95
            if (isset($criteriaMap[$expressionString])) {
96
                $expressionString = $criteriaMap[$expressionString];
97
            }
98
99
            if (in_array($expressionString, ['limit', 'offset'])) {
100
                $value = (int)$value;
101
                if ($value < 0) {
102
                    throw new RuntimeException(
103
                        sprintf('Predicate %s must unsigned int, %s given', $expressionString, $value)
104
                    );
105
                }
106
107
                $criteria->{$expressionString}($value);
108
                continue;
109
            }
110
111
            if ($expressionString == 'page') {
112
                if (!isset($filter['limit'])) {
113
                    throw new RuntimeException(sprintf('Predicate %s require limit', $expressionString));
114
                }
115
                $criteria->offset($filter['limit'] * ($value - 1));
116
                continue;
117
            }
118
119
            if ($expressionString == 'order') {
120
                $criteria->order($value);
121
                continue;
122
            }
123
124
            if (strpos($expressionString, '.') !== false) {
125
                $expressionArray = explode('.', $expressionString);
126
            } else {
127
                $expressionArray = explode('_', $expressionString);
128
            }
129
130
            if (count($expressionArray) > 2) {
131
                continue;
132
            }
133
134
            if (count($expressionArray) == 2) {
135
                list($attribute, $method) = $expressionArray;
136
137
                if (in_array($method, ['isNull', 'isNotNull']) && $value) {
138
                    $criteria->{$method}($attribute);
139
                    continue;
140
                }
141
142
                if ($method == 'between') {
143
                    if (!is_array($value) || !isset($value[0]) || !isset($value[1])) {
144
                        throw new RuntimeException(
145
                            sprintf('Predicate %s must contain array [MIN_VALUE, MAX_VALUE], ', $method)
146
                        );
147
                    }
148
149
                    $criteria->between($attribute, $value[0], $value[1]);
150
                    continue;
151
                }
152
153
                if (!method_exists($criteria, $method)) {
154
                    throw new RuntimeException(sprintf('Predicate %s does not exists', $method));
155
                }
156
157
                call_user_func([$criteria, $method], $attribute, $value);
158
            }
159
160
            if (count($expressionArray) == 1) {
161
                $customCriteriaInstance = $this->buildCustomCriteria($expressionArray[0], $criteria);
162
                $customCriteriaInstance($criteria, $value);
163
            }
164
        }
165
166
        return $criteria;
167
    }
168
169
    public function buildCustomCriteria($criteriaName, $baseCriteria)
170
    {
171
        $customCriteria = ucfirst($criteriaName);
172
        $customCriteriaClass = $this->config->getCustomCriteriaClass($baseCriteria->getEntityName(), $customCriteria);
173
174
        if (!class_exists($customCriteriaClass)) {
175
            throw new RuntimeException(
176
                sprintf('Wrong criteria %s. Class %s does not exists.', $customCriteria, $customCriteriaClass)
177
            );
178
        }
179
180
        $customCriteriaInstance = new $customCriteriaClass();
181
182
        if (!is_callable($customCriteriaInstance)) {
183
            throw new RuntimeException(
184
                sprintf(
185
                    'Wrong criteria %s. Object of type %s is not callable.',
186
                    $customCriteria,
187
                    $customCriteriaClass
188
                )
189
            );
190
        }
191
192
        return $customCriteriaInstance;
193
    }
194
}
195