Passed
Branch dev_2x (3e8772)
by Adrian
01:42
created

Relation   A

Complexity

Total Complexity 41

Size/Duplication

Total Lines 230
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 41
eloc 91
c 0
b 0
f 0
dl 0
loc 230
rs 9.1199

How to fix   Complexity   

Complex Class

Complex classes like Relation often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Relation, and based on these observations, apply Extract Interface, too.

1
<?php
2
declare(strict_types=1);
3
4
namespace Sirius\Orm\Blueprint;
5
6
use Closure;
7
use Nette\PhpGenerator\Literal;
8
use Nette\PhpGenerator\Parameter;
9
use ReflectionFunction;
10
use Sirius\Orm\Helpers\Str;
11
use Sirius\Orm\Relation\RelationConfig;
12
13
abstract class Relation extends Base
14
{
15
    use MapperAwareTrait;
16
17
    protected $observer;
18
19
    protected $name;
20
21
    protected $type;
22
23
    protected $nativeKey;
24
25
    protected $foreignMapper;
26
27
    protected $foreignKey;
28
29
    protected $foreignGuards = [];
30
31
    protected $loadStrategy = RelationConfig::LOAD_LAZY;
32
33
    protected $queryCallback;
34
35
    /**
36
     * @param string $foreignMapper defaults to the relation's name
37
     *
38
     * @return Relation|static
39
     */
40
    public static function make($foreignMapper = '')
41
    {
42
        $mapper = new static;
43
        $mapper->setForeignMapper($foreignMapper);
44
45
        return $mapper;
46
    }
47
48
    public function getErrors(): array
49
    {
50
        $errors = [];
51
52
        if (! $this->name) {
53
            $errors[] = "Unknown relation name";
54
        }
55
56
        if (! $this->type) {
57
            $errors[] = "Unknown relation type";
58
        }
59
60
        if (! $this->nativeKey) {
61
            $errors[] = "Missing native key column";
62
        }
63
64
        if (! $this->foreignMapper) {
65
            $errors[] = "Missing foreign mapper name";
66
        }
67
68
        if (! $this->foreignKey) {
69
            $errors[] = "Missing foreign key";
70
        }
71
72
        $strategies = [RelationConfig::LOAD_LAZY, RelationConfig::LOAD_EAGER, RelationConfig::LOAD_NONE];
73
        if (! in_array($this->loadStrategy, $strategies)) {
74
            $errors[] = sprintf("Relation loading strategy is not valid (allowed values: %s)", implode(', ', $strategies));
75
        }
76
77
        return $errors;
78
    }
79
80
    public function getObservers(): array
81
    {
82
        $observer = $this->getObserver()->with($this);
83
84
        return [
85
            $this->getMapper()->getName() . '_base_entity' => [$observer]
86
        ];
87
    }
88
89
    public function getNativeKey(): string
90
    {
91
        return $this->nativeKey;
92
    }
93
94
    public function setNativeKey(string $nativeKey): Relation
95
    {
96
        $this->nativeKey = $nativeKey;
97
98
        return $this;
99
    }
100
101
    public function getForeignMapper(): string
102
    {
103
        return $this->foreignMapper;
104
    }
105
106
    public function setForeignMapper($foreignMapper): Relation
107
    {
108
        $this->foreignMapper = $foreignMapper;
109
110
        return $this;
111
    }
112
113
    public function getForeignKey(): string
114
    {
115
        return $this->foreignKey;
116
    }
117
118
    public function setForeignKey($foreignKey): Relation
119
    {
120
        $this->foreignKey = $foreignKey;
121
122
        return $this;
123
    }
124
125
    public function getLoadStrategy(): string
126
    {
127
        return $this->loadStrategy;
128
    }
129
130
    public function setLoadStrategy(string $loadStrategy): Relation
131
    {
132
        $this->loadStrategy = $loadStrategy;
133
134
        return $this;
135
    }
136
137
    public function getForeignGuards(): array
138
    {
139
        return $this->foreignGuards;
140
    }
141
142
    public function setForeignGuards(array $foreignGuards): Relation
143
    {
144
        $this->foreignGuards = $foreignGuards;
145
146
        return $this;
147
    }
148
149
    public function getQueryCallback(): ?callable
150
    {
151
        return $this->queryCallback;
152
    }
153
154
    public function setQueryCallback(callable $queryCallback = null): Relation
155
    {
156
        $this->queryCallback = $queryCallback;
157
158
        return $this;
159
    }
160
161
    public function getName(): string
162
    {
163
        return $this->name;
164
    }
165
166
    public function setName(string $name): Relation
167
    {
168
        $this->name = $name;
169
170
        return $this;
171
    }
172
173
    public function getObserver(): \Sirius\Orm\CodeGenerator\Observer\Base
174
    {
175
        if ($this->observer) {
176
            return $this->observer;
177
        }
178
179
        $class = get_class($this);
180
        $observerClass = str_replace(
181
            '\\Blueprint\\',
182
            '\\CodeGenerator\\Observer\\',
183
            $class
184
        ) . 'Observer';
185
186
        return new $observerClass();
187
    }
188
189
    public function setObserver(\Sirius\Orm\CodeGenerator\Observer\Base $observer): Relation
190
    {
191
        $this->observer = $observer;
192
193
        return $this;
194
    }
195
196
    public function toArray()
197
    {
198
        $result = [];
199
        foreach (get_object_vars($this) as $prop => $value) {
200
            if (in_array($prop, ['mapper', 'name'])) {
201
                continue;
202
            }
203
204
            if ($value !== null &&
205
                $value !== 0 &&
206
                $value !== '' &&
207
                $value !== [] &&
208
                ! is_object($value)) {
209
                $result[Str::underscore($prop)] = $value;
210
            }
211
            if (is_object($value) && is_callable($value)) {
212
                /** @var Closure $value */
213
                $result[Str::underscore($prop)] = new Literal($this->getClosureDump($value));
214
            }
215
        }
216
217
        return $result;
218
    }
219
220
    protected function getClosureDump(Closure $c)
221
    {
222
        $closure = \Nette\PhpGenerator\Closure::from($c);
223
        $r       = new ReflectionFunction($c);
224
        $body    = '';
225
        $lines   = file($r->getFileName());
226
        for ($l = $r->getStartLine(); $l < $r->getEndLine(); $l++) {
227
            $body .= trim($lines[$l], " \t");
228
        }
229
        // strip everything after the last }
230
        $body = preg_replace('/\}[^\}]+$/', '', $body);
231
        $closure->setBody($body);
232
233
        $params = $closure->getParameters();
234
        /** @var Parameter $param */
235
        foreach ($params as $param) {
236
            if (strpos($param->getType(), '\\') !== false
237
                && strpos($param->getType(), '\\') !== 0) {
238
                $param->setType('\\' . $param->getType());
239
            }
240
        }
241
242
        return (string)$closure;
243
    }
244
}
245