Passed
Push — master ( f112c9...a480d7 )
by Gabriel
02:57
created

Definition::newProperty()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 9
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
eloc 6
c 0
b 0
f 0
dl 0
loc 9
ccs 2
cts 2
cp 1
rs 10
cc 1
nc 1
nop 1
crap 1
1
<?php
2
3
namespace ByTIC\Models\SmartProperties\Properties\Definitions;
4
5
use ByTIC\Models\SmartProperties\Properties\AbstractProperty\Generic;
6
use ByTIC\Models\SmartProperties\Properties\AbstractProperty\Generic as Property;
7
use ByTIC\Models\SmartProperties\RecordsTraits\HasSmartProperties\RecordsTrait;
8
use Exception;
9
use Nip\Records\RecordManager;
10
use Nip\Utility\Str;
11
use RecursiveDirectoryIterator;
12
use RecursiveIteratorIterator;
13
14
/**
15
 * Class Definition
16
 * @package ByTIC\Models\SmartProperties\Properties\Definitions
17
 */
18
class Definition
19
{
20
    use Traits\HasItemsDirectoryTrait;
21
22
    /**
23
     * @var RecordManager|RecordsTrait
24
     */
25
    protected $manager;
26
27
    /**
28
     * @var string
29
     */
30
    protected $name = null;
31
32
    /**
33
     * @var string
34
     */
35
    protected $label = null;
36
37
    /**
38
     * @var string
39
     */
40
    protected $field;
41
42
    protected $items = null;
43
    protected $itemsAliases = [];
44
45
    protected $defaultValue = null;
46
47
    /**
48
     * @param $name
49
     *
50 10
     * @return Property
51
     * @throws Exception
52 10
     */
53 10
    public function getItem($name): Property
54
    {
55
        $items = $this->getItems();
56
        if (!$this->hasItem($name)) {
57
            throw new Exception(
58
                'Bad Item [' . $name . '] for smart property 
59
                [' . $this->getManager()->getController() . '::' . $this->getName() . ']
60
                [' . implode(',', array_keys($items)) . ']'
0 ignored issues
show
Bug introduced by
It seems like $items can also be of type null; however, parameter $input of array_keys() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

60
                [' . implode(',', array_keys(/** @scrutinizer ignore-type */ $items)) . ']'
Loading history...
61 10
            );
62
        }
63
        if (isset($this->itemsAliases[$name])) {
64
            $name = $this->itemsAliases[$name];
65
        }
66
        return $items[$name];
67 14
    }
68
69 14
    /**
70 14
     * @return null|Property[]
71
     */
72
    public function getItems(): ?array
73 14
    {
74
        if ($this->items == null) {
75
            $this->initItems();
76 14
        }
77
78 14
        return $this->items;
79 14
    }
80 14
81 14
    public function initItems()
82 14
    {
83 14
        $names       = $this->getItemsNames();
84
        $this->items = [];
85
        foreach ($names as $name) {
86 14
            if (! $this->isAbstractItemName($name)) {
87
                $object = $this->newProperty($name);
88
                $this->addItem($object);
89
            }
90
        }
91 15
    }
92
93 15
    /**
94
     * @return array
95 15
     */
96
    public function getItemsNames()
97
    {
98
        $names = $this->getItemsNamesFromManager();
99
100
        return $names ? $names : $this->getItemsNamesFromFiles();
0 ignored issues
show
Bug Best Practice introduced by
The expression return $names ? $names :...etItemsNamesFromFiles() also could return the type true which is incompatible with the documented return type array.
Loading history...
101 15
    }
102
103 15
    /**
104 15
     * @return array|boolean
105
     */
106
    protected function getItemsNamesFromManager()
107
    {
108 15
        $methodName = 'get' . $this->getName() . 'Names';
109
        if (method_exists($this->getManager(), $methodName)) {
110
            return $this->getManager()->$methodName();
111
        }
112
113
        return false;
114 20
    }
115
116 20
    /**
117 19
     * @return mixed
118
     */
119
    public function getName(): ?string
120 20
    {
121
        if ($this->name === null) {
122
            $this->initName();
123
        }
124
125
        return $this->name;
126 20
    }
127
128 20
    /**
129 20
     * @param mixed $name
130
     */
131 19
    public function setName($name)
132
    {
133 19
        $this->name = $name;
134 19
    }
135 19
136
    protected function initName()
137
    {
138
        $name = inflector()->classify($this->getField());
139
        $this->setName($name);
140 19
    }
141
142 19
    /**
143
     * @return string|null
144
     */
145
    public function getField(): ?string
146
    {
147
        return $this->field;
148 18
    }
149
150 18
    /**
151 18
     * @param mixed $field
152
     */
153
    public function setField($field)
154
    {
155
        $this->field = $field;
156 17
    }
157
158 17
    /**
159
     * @return RecordManager
160
     */
161
    public function getManager()
162
    {
163
        return $this->manager;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->manager also could return the type ByTIC\Models\SmartProper...Properties\RecordsTrait which is incompatible with the documented return type Nip\Records\RecordManager.
Loading history...
164 19
    }
165
166 19
    /**
167 19
     * @param RecordManager|RecordsTrait $manager
168
     */
169
    public function setManager($manager)
170
    {
171
        $this->manager = $manager;
172 15
    }
173
174 15
    /**
175 15
     * @return array
176 15
     */
177 15
    protected function getItemsNamesFromFiles(): array
178 15
    {
179 15
        $directory = $this->getItemsDirectory();
180
        $files = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($directory));
181 15
        $names = [];
182 15
        foreach ($files as $file) {
183 15
            if ($file->isDir()) {
184
                continue;
185
            }
186 15
            $name = str_replace($directory, '', $file->getPathname());
187
            $name = str_replace('.php', '', $name);
188
            $names[] = trim($name, DIRECTORY_SEPARATOR . '\\');
189
        }
190
191
        return array_unique($names);
192
    }
193 15
194
195 15
    /**
196 15
     * @return string
197
     */
198
    public function getLabel(): ?string
199 15
    {
200
        if ($this->label === null) {
201
            $this->initLabel();
202
        }
203
204
        return $this->label;
205 15
    }
206
207 15
    /**
208 15
     * @param string $label
209
     */
210 15
    public function setLabel(string $label)
211
    {
212 15
        $this->label = $label;
213 15
    }
214 15
215
    protected function initLabel()
216
    {
217
        $name = inflector()->pluralize($this->getName());
218
        $this->setLabel($name);
219
    }
220
221 14
    /**
222
     * @param string $name
223 14
     *
224
     * @return bool
225
     */
226 14
    public function isAbstractItemName(string $name): bool
227 14
    {
228
        if (in_array($name, ['Abstract', 'Generic'])) {
229 14
            return true;
230
        }
231
        if (strpos($name, 'Abstract') === 0) {
232 14
            return true;
233 3
        }
234
        if (Str::endsWith($name, 'Trait')) {
235
            return true;
236 14
        }
237
        if (strpos($name, '\Abstract') !== false) {
238
            return true;
239
        }
240
        if (strpos($name, DIRECTORY_SEPARATOR . 'Abstract') !== false) {
241
            return true;
242
        }
243
244 14
        return false;
245
    }
246 14
247 14
    /**
248
     * @param string $type
249 14
     *
250 14
     * @return Property
251
     */
252 14
    public function newProperty($type = null): Property
253
    {
254
        $className = $this->getPropertyClass($type);
0 ignored issues
show
Bug introduced by
It seems like $type can also be of type string; however, parameter $type of ByTIC\Models\SmartProper...ion::getPropertyClass() does only seem to accept null, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

254
        $className = $this->getPropertyClass(/** @scrutinizer ignore-type */ $type);
Loading history...
255
        $object = new $className();
256
        /** @var Property $object */
257
        $object->setManager($this->getManager());
258
        $object->setField($this->getField());
259
        $object->setName(inflector()->unclassify($type));
260 14
        return $object;
261
    }
262 14
263 14
    /**
264
     * @param null $type
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $type is correct as it would always require null to be passed?
Loading history...
265 14
     *
266
     * @return string
267
     */
268
    public function getPropertyClass($type = null): string
269
    {
270
        $type = $type ? $type : $this->getDefaultValue();
0 ignored issues
show
introduced by
$type is of type null, thus it always evaluated to false.
Loading history...
271 6
        $type = str_replace(DIRECTORY_SEPARATOR, '\\', $type);
272
273 6
        return $this->getPropertyItemsRootNamespace() . inflector()->classify($type);
274 6
    }
275
276
    /**
277 6
     * @return string
278
     */
279
    public function getDefaultValue(): ?string
280
    {
281
        if ($this->defaultValue === null) {
282
            $this->initDefaultValue();
283 6
        }
284
285 6
        return $this->defaultValue;
286 6
    }
287
288 6
    /**
289
     * @param null $defaultValue
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $defaultValue is correct as it would always require null to be passed?
Loading history...
290 6
     */
291 6
    public function setDefaultValue($defaultValue)
292
    {
293
        $this->defaultValue = $defaultValue;
294 6
    }
295 6
296
    /**
297 6
     * @param Generic $object
298 6
     */
299
    protected function addItem(Property $object)
300
    {
301
        $this->items[$object->getName()] = $object;
302
        $aliases = $object->getAliases();
303 6
        foreach ($aliases as $alias) {
304
            $this->itemsAliases[$alias] = $object->getName();
305 6
        }
306 6
    }
307
308
    protected function initDefaultValue()
309
    {
310 6
        $managerDefaultValue = $this->getDefaultValueFromManager();
311
        if ($managerDefaultValue && $this->hasItem($managerDefaultValue)) {
312
            $defaultValue = $managerDefaultValue;
313
        } else {
314
            $keys         = array_keys($this->getItems());
0 ignored issues
show
Bug introduced by
It seems like $this->getItems() can also be of type null; however, parameter $input of array_keys() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

314
            $keys         = array_keys(/** @scrutinizer ignore-type */ $this->getItems());
Loading history...
315
            $defaultValue = reset($keys);
316
        }
317
        $this->setDefaultValue($defaultValue);
0 ignored issues
show
Bug introduced by
It seems like $defaultValue can also be of type string and true; however, parameter $defaultValue of ByTIC\Models\SmartProper...tion::setDefaultValue() does only seem to accept null, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

317
        $this->setDefaultValue(/** @scrutinizer ignore-type */ $defaultValue);
Loading history...
318 10
    }
319
320 10
    /**
321
     * @return bool|string
322 10
     */
323
    protected function getDefaultValueFromManager()
324
    {
325
        $method = 'getDefault' . $this->getName();
326
        if (method_exists($this->getManager(), $method)) {
327
            return $this->getManager()->{$method}();
328 14
        }
329
330 14
        return false;
331 14
    }
332 14
333
    /**
334
     * @param $name
335
     * @return bool
336 14
     */
337 14
    public function hasItem($name): bool
338
    {
339
        $items = $this->getItems();
340
341 14
        return isset($items[$name]) || isset($this->itemsAliases[$name]);
342
    }
343
344
    /**
345
     * @return string
346
     */
347
    protected function getPropertyItemsRootNamespace(): string
348
    {
349 2
        $manager = $this->getManager();
350
        $method = 'get' . $this->getName() . 'ItemsRootNamespace';
351 2
        if (method_exists($manager, $method)) {
352 2
            return $manager->{$method}();
353
        }
354 2
355 2
        $method = 'get' . $this->getName() . 'Namespace';
356 2
        if (method_exists($manager, $method)) {
357 2
            return $manager->{$method}();
358
        }
359
360
        return $manager->getModelNamespace() . $this->getLabel() . '\\';
361
    }
362
363 2
    /**
364
     * @param $name
365
     *
366
     * @return array
367
     */
368
    public function getValues($name): array
369
    {
370
        $return = [];
371
        $items = $this->getItems();
372
373
        foreach ($items as $type) {
374
            $method = 'get' . ucfirst($name);
375
            if (method_exists($type, $method)) {
376
                $return[] = $type->$method();
377
            } else {
378
                $return[] = $type->{$name};
379
            }
380
        }
381
382
        return $return;
383
    }
384
}
385