Completed
Push — master ( 075a1a...b1f908 )
by max
02:16
created

CriteriaFactory::buildCustomCriteria()   B

Complexity

Conditions 3
Paths 3

Size

Total Lines 25
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 25
rs 8.8571
cc 3
eloc 14
nc 3
nop 2
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
        if (empty($filter)) {
38
            return $criteria;
39
        }
40
41
        $relations = [];
42
        if (isset($filter['relations'])) {
43
            $relations = $filter['relations'];
44
            unset($filter['relations']);
45
        }
46
47
        $this->applyFilter($criteria, $filter);
48
49
        foreach ($relations as $relationEntity => $relationFilter) {
50
            $relation = $criteria->relation($relationEntity);
51
            $this->applyFilter($relation, $relationFilter);
52
        }
53
54
55
        return $criteria;
56
    }
57
58
    /**
59
     * @param CriteriaInterface $criteria
60
     * @param array $filter
61
     * @return CriteriaInterface
62
     */
63
    private function applyFilter(CriteriaInterface $criteria, array $filter)
64
    {
65
        foreach ($filter as $expressionString => $value) {
66
67
            $criteriaMap = $this->config->getCriteriaMap($criteria->getEntityName());
68
            if (isset($criteriaMap[$expressionString])) {
69
                $expressionString = $criteriaMap[$expressionString];
70
            }
71
72
            if (in_array($expressionString, ['limit', 'offset'])) {
73
                $value = (int)$value;
74
                if ($value < 0) {
75
                    throw new RuntimeException(
76
                        sprintf('Predicate %s must unsigned int, %s given', $expressionString, $value)
77
                    );
78
                }
79
80
                $criteria->{$expressionString}($value);
81
                continue;
82
            }
83
84
            if ($expressionString == 'page') {
85
                if (!isset($filter['limit'])) {
86
                    throw new RuntimeException(sprintf('Predicate %s require limit', $expressionString));
87
                }
88
                $criteria->offset($filter['limit'] * ($value - 1));
89
                continue;
90
            }
91
92
            if ($expressionString == 'order') {
93
                $criteria->order($value);
94
                continue;
95
            }
96
97
            if (strpos($expressionString, '.') !== false) {
98
                $expressionArray = explode('.', $expressionString);
99
            } else {
100
                $expressionArray = explode('_', $expressionString);
101
            }
102
103
            if (count($expressionArray) > 2) {
104
                continue;
105
            }
106
107
            if (count($expressionArray) == 2) {
108
                list($attribute, $method) = $expressionArray;
109
110
                if (in_array($method, ['isNull', 'isNotNull']) && $value) {
111
                    $criteria->{$method}($attribute);
112
                    continue;
113
                }
114
115
                if ($method == 'between') {
116
                    if (!is_array($value) || !isset($value[0]) || !isset($value[1])) {
117
                        throw new RuntimeException(
118
                            sprintf('Predicate %s must contain array [MIN_VALUE, MAX_VALUE], ', $method)
119
                        );
120
                    }
121
122
                    $criteria->between($attribute, $value[0], $value[1]);
123
                    continue;
124
                }
125
126
                if (!method_exists($criteria, $method)) {
127
                    throw new RuntimeException(sprintf('Predicate %s does not exists', $method));
128
                }
129
130
                call_user_func([$criteria, $method], $attribute, $value);
131
            }
132
133
            if (count($expressionArray) == 1) {
134
                $customCriteriaInstance = $this->buildCustomCriteria($expressionArray[0], $criteria);
135
                $customCriteriaInstance($criteria, $value);
136
            }
137
        }
138
139
        return $criteria;
140
    }
141
142
    public function buildCustomCriteria($criteriaName, $baseCriteria)
143
    {
144
        $customCriteria = ucfirst($criteriaName);
145
        $customCriteriaClass = $this->config->getCustomCriteriaClass($baseCriteria->getEntityName(), $customCriteria);
146
147
        if (!class_exists($customCriteriaClass)) {
148
            throw new RuntimeException(
149
                sprintf('Wrong criteria %s. Class %s does not exists.', $customCriteria, $customCriteriaClass)
150
            );
151
        }
152
153
        $customCriteriaInstance = new $customCriteriaClass();
154
155
        if (!is_callable($customCriteriaInstance)) {
156
            throw new RuntimeException(
157
                sprintf(
158
                    'Wrong criteria %s. Object of type %s is not callable.',
159
                    $customCriteria,
160
                    $customCriteriaClass
161
                )
162
            );
163
        }
164
165
        return $customCriteriaInstance;
166
    }
167
}
168