GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Completed
Branch master (9e3162)
by Dave
63:34
created

NamedFormElement::getValidationMessages()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 10
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 2

Importance

Changes 0
Metric Value
cc 2
eloc 5
nc 2
nop 0
dl 0
loc 10
ccs 7
cts 7
cp 1
crap 2
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace SleepingOwl\Admin\Form\Element;
4
5
use LogicException;
6
use Illuminate\Support\Arr;
7
use Illuminate\Database\Eloquent\Model;
8
use SleepingOwl\Admin\Form\FormElement;
9
use Illuminate\Contracts\Support\Htmlable;
10
use KodiComponents\Support\HtmlAttributes;
11
use Illuminate\Database\Eloquent\Relations\HasOne;
12
use Illuminate\Database\Eloquent\Relations\MorphOne;
13
use Illuminate\Database\Eloquent\Relations\Relation;
14
use Illuminate\Database\Eloquent\Relations\BelongsTo;
15
use SleepingOwl\Admin\Exceptions\Form\FormElementException;
16
17
abstract class NamedFormElement extends FormElement
18
{
19
    use HtmlAttributes;
20
    /**
21
     * @var string
22
     */
23
    protected $path;
24
25
    /**
26
     * @var string
27
     */
28
    protected $name;
29
30
    /**
31
     * @var string
32
     */
33
    protected $modelAttributeKey;
34
35
    /**
36
     * @var string
37
     */
38
    protected $label;
39
40
    /**
41
     * @var string
42
     */
43
    protected $helpText;
44
45
    /**
46
     * @var mixed
47
     */
48
    protected $defaultValue;
49
50
    /**
51
     * @var \Closure
52
     */
53
    protected $mutator;
54
55
    /**
56
     * @param string $path
57
     * @param string|null $label
58
     *
59
     * @throws FormElementException
60
     */
61 27
    public function __construct($path, $label = null)
62
    {
63 27
        if (empty($path)) {
64 1
            throw new FormElementException('You must specify field path');
65
        }
66
67 26
        $this->setPath($path);
68 26
        $this->setLabel($label);
69
70 26
        $parts = explode('.', $path);
71 26
        $this->setName($this->composeName($parts));
72 26
        $this->setModelAttributeKey(end($parts));
73
74 26
        parent::__construct();
75 26
    }
76
77
    /**
78
     * Compose html name from array like this: 'first[second][third]'.
79
     *
80
     * @param array $parts
81
     *
82
     * @return string
83
     */
84 26
    private function composeName(array $parts)
85
    {
86 26
        $name = array_shift($parts);
87
88 26
        while (! empty($parts)) {
89 7
            $part = array_shift($parts);
90 7
            $name .= "[$part]";
91 7
        }
92
93 26
        return $name;
94
    }
95
96
    /**
97
     * @return string
98
     */
99 13
    public function getPath()
100
    {
101 13
        return $this->path;
102
    }
103
104
    /**
105
     * @param string $path
106
     *
107
     * @return $this
108
     */
109 26
    public function setPath($path)
110
    {
111 26
        $this->path = $path;
112
113 26
        return $this;
114
    }
115
116
    /**
117
     * @return string
118
     */
119 5
    public function getName()
120
    {
121 5
        return $this->name;
122
    }
123
124
    /**
125
     * @param string $name
126
     *
127
     * @return $this
128
     */
129 26
    public function setName($name)
130
    {
131 26
        $this->name = $name;
132
133 26
        return $this;
134
    }
135
136
    /**
137
     * @return string
138
     */
139 4
    public function getLabel()
140
    {
141 4
        return $this->label;
142
    }
143
144
    /**
145
     * @param string $label
146
     *
147
     * @return $this
148
     */
149 26
    public function setLabel($label)
150
    {
151 26
        $this->label = $label;
152
153 26
        return $this;
154
    }
155
156
    /**
157
     * @return string
158
     */
159 6
    public function getModelAttributeKey()
160
    {
161 6
        return $this->modelAttributeKey;
162
    }
163
164
    /**
165
     * @param string $key
166
     *
167
     * @return $this
168
     */
169 26
    public function setModelAttributeKey($key)
170
    {
171 26
        $this->modelAttributeKey = $key;
172
173 26
        return $this;
174
    }
175
176
    /**
177
     * @return mixed
178
     */
179 3
    public function getDefaultValue()
180
    {
181 3
        return $this->defaultValue;
182
    }
183
184
    /**
185
     * @param mixed $defaultValue
186
     *
187
     * @return $this
188
     */
189 1
    public function setDefaultValue($defaultValue)
190
    {
191 1
        $this->defaultValue = $defaultValue;
192
193 1
        return $this;
194
    }
195
196
    /**
197
     * @return string
198
     */
199 2
    public function getHelpText()
200
    {
201 2
        if ($this->helpText instanceof Htmlable) {
0 ignored issues
show
introduced by
$this->helpText is never a sub-type of Illuminate\Contracts\Support\Htmlable. If $this->helpText can have other possible types, add them to src/Form/Element/NamedFormElement.php:41.
Loading history...
202 1
            return $this->helpText->toHtml();
203
        }
204
205 2
        return $this->helpText;
206
    }
207
208
    /**
209
     * @param string|Htmlable $helpText
210
     *
211
     * @return $this
212
     */
213 1
    public function setHelpText($helpText)
214
    {
215 1
        $this->helpText = $helpText;
0 ignored issues
show
Documentation Bug introduced by
It seems like $helpText can also be of type Illuminate\Contracts\Support\Htmlable. However, the property $helpText is declared as type string. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
216
217 1
        return $this;
218
    }
219
220
    /**
221
     * @param string|null $message
222
     *
223
     * @return $this
224
     */
225 2
    public function required($message = null)
226
    {
227 2
        $this->addValidationRule('required', $message);
228
229 2
        return $this;
230
    }
231
232
    /**
233
     * @param string|null $message
234
     *
235
     * @return $this
236
     */
237 2
    public function unique($message = null)
238
    {
239 2
        $this->addValidationRule('_unique');
240
241 2
        if (! is_null($message)) {
242 1
            $this->addValidationMessage('unique', $message);
243 1
        }
244
245 2
        return $this;
246
    }
247
248
    /**
249
     * @return array
250
     */
251 3
    public function getValidationMessages()
252
    {
253 3
        $messages = parent::getValidationMessages();
254
255 3
        foreach ($messages as $rule => $message) {
256 3
            $messages[$this->getName().'.'.$rule] = $message;
257 3
            unset($messages[$rule]);
258 3
        }
259
260 3
        return $messages;
261
    }
262
263
    /**
264
     * @return array
265
     */
266 1
    public function getValidationLabels()
267
    {
268 1
        return [$this->getPath() => $this->getLabel()];
269
    }
270
271
    /**
272
     * If FormElement has `_unique` rule, it will get all appropriate
273
     * validation rules based on underlying model.
274
     *
275
     * @return array
276
     */
277 2
    public function getValidationRules()
278
    {
279 2
        $rules = parent::getValidationRules();
280
281 2
        foreach ($rules as &$rule) {
282 2
            if ($rule !== '_unique') {
283 1
                continue;
284
            }
285
286 1
            $model = $this->resolvePath();
287 1
            $table = $model->getTable();
288
289 1
            $rule = 'unique:'.$table.','.$this->getModelAttributeKey();
290 1
            if ($model->exists) {
291
                $rule .= ','.$model->getKey();
292
            }
293 2
        }
294 2
        unset($rule);
295
296 2
        return [$this->getPath() => $rules];
297
    }
298
299
    /**
300
     * Get model related to form element.
301
     *
302
     * @return mixed
303
     */
304 2
    public function resolvePath()
305
    {
306 2
        $model = $this->getModel();
307 2
        $relations = explode('.', $this->getPath());
308 2
        $count = count($relations);
309
310 2
        if ($count === 1) {
311 2
            return $model;
312
        }
313
314 1
        foreach ($relations as $relation) {
315 1
            if ($count === 1) {
316 1
                return $model;
317
            }
318
319 1
            if ($model->exists && ($value = $model->getAttribute($relation)) instanceof Model) {
320 1
                $model = $value;
321
322 1
                $count--;
323 1
                continue;
324
            }
325
326 1
            if (method_exists($model, $relation)) {
327 1
                $relation = $model->{$relation}();
328
329 1
                if ($relation instanceof Relation) {
330 1
                    $model = $relation->getModel();
331 1
                    $count--;
332 1
                    continue;
333
                }
334
            }
335
336
            break;
337
        }
338
339
        throw new LogicException("Can not resolve path for field '{$this->getPath()}'. Probably relation definition is incorrect");
340
    }
341
342
    /**
343
     * @param \Illuminate\Http\Request $request
344
     *
345
     * @return array|string
346
     */
347 6
    public function getValueFromRequest(\Illuminate\Http\Request $request)
348
    {
349 6
        if ($request->hasSession() && ! is_null($value = $request->old($this->getPath()))) {
350 1
            return $value;
351
        }
352
353 5
        return $request->input($this->getPath());
354
    }
355
356
    /**
357
     * @return mixed
358
     */
359 3
    public function getValueFromModel()
360
    {
361 3
        if (! is_null($value = $this->getValueFromRequest(request()))) {
0 ignored issues
show
introduced by
The condition is_null($value = $this->...FromRequest(request())) is always false.
Loading history...
362 1
            return $value;
363
        }
364
365 2
        $model = $this->getModel();
366 2
        $path = $this->getPath();
367 2
        $value = $this->getDefaultValue();
368
369 2
        /*
370 2
         * Implement json parsing
371
         */
372
        if (strpos($path, '->') !== false) {
373
            $casts = collect($model->getCasts());
374
            $jsonParts = collect(explode('->', $path));
375
376 1
            $jsonAttr = $model->{$jsonParts->first()};
377
378
            $cast = $casts->get($jsonParts->first(), false);
379
380
            if ($cast == 'object') {
381
                $jsonAttr = json_decode(json_encode($jsonAttr), true);
382
            } elseif ($cast != 'array') {
383
                $jsonAttr = json_decode($jsonAttr);
384
            }
385
386
            return Arr::get($jsonAttr, $jsonParts->slice(1)->implode('.'));
387
        }
388
389
        if (is_null($model) || ! $model->exists) {
390
            return $value;
391
        }
392
393 1
        $relations = explode('.', $path);
394 1
        $count = count($relations);
395
396 1
        if ($count === 1) {
397 1
            $attribute = $model->getAttribute($this->getModelAttributeKey());
398
399 1
            if (! empty($attribute) || $attribute === 0 || is_null($value)) {
400 1
                return $attribute;
401
            }
402
        }
403
404
        foreach ($relations as $relation) {
405
            if ($model->{$relation} instanceof Model) {
406
                $model = $model->{$relation};
407
                continue;
408
            }
409
            if ($count === 2) {
410
                if (str_contains($relation, '->')) {
411
                    $parts = explode('->', $relation);
412
                    $relationField = array_shift($array);
413
                    $jsonPath = implode('.', $parts);
414
                    $attribute = data_get($model->{$relationField}, $jsonPath);
415
                } else {
416
                    $attribute = $model->getAttribute($relation);
417
                }
418
                if (! empty($attribute) || is_null($value)) {
419
                    return $attribute;
420
                }
421
            }
422
423
            if (is_null($this->getDefaultValue())) {
424
                throw new LogicException("Can not fetch value for field '{$path}'. Probably relation definition is incorrect");
425
            }
426
        }
427
428
        return $value;
429
    }
430
431 1
    /**
432
     * @param \Illuminate\Http\Request $request
433 1
     *
434 1
     * @return void
435
     */
436 1
    public function save(\Illuminate\Http\Request $request)
437 1
    {
438 1
        $this->setModelAttribute(
439
            $this->getValueFromRequest(
440
                $request
441
            )
442
        );
443
    }
444
445 3
    /**
446
     * @param mixed $value
447 3
     *
448 3
     * @return void
449 3
     */
450
    public function setModelAttribute($value)
451 3
    {
452 1
        $model = $this->getModelByPath(
453
            $this->getPath()
454
        );
455 3
456 3
        if ($this->isValueSkipped()) {
457 3
            return;
458 3
        }
459 3
460
        $model->setAttribute(
461
            $this->getModelAttributeKey(),
462
            $this->prepareValue($value)
463
        );
464
    }
465
466 5
    /**
467
     * @param string $path
468 5
     *
469
     * @return Model|null
470 5
     */
471 5
    protected function getModelByPath($path)
472 5
    {
473
        $model = $this->getModel();
474 5
475 2
        $relations = explode('.', $path);
476 2
        $count = count($relations);
477
        $i = 1;
478
479 2
        if ($count > 1) {
480 2
            $i++;
481 2
            $previousModel = $model;
482 2
483 2
            /* @var Model $model */
484
            foreach ($relations as $relation) {
485
                $relatedModel = null;
486
                if ($previousModel->getAttribute($relation) instanceof Model) {
487
                    $relatedModel = $previousModel->getAttribute($relation);
488
                } elseif (method_exists($previousModel, $relation)) {
489
490
                    /* @var Relation $relation */
491
                    $relationObject = $previousModel->{$relation}();
492
493
                    switch (get_class($relationObject)) {
494
                        case BelongsTo::class:
495
                            $relationObject->associate($relatedModel = $relationObject->getRelated());
496
                            break;
497
                        case HasOne::class:
498
                        case MorphOne::class:
499
                            $relatedModel = $relationObject->getRelated()->newInstance();
500
                            $relatedModel->setAttribute($this->getForeignKeyNameFromRelation($relationObject),
501 2
                                $relationObject->getParentKey());
502 2
                            $model->setRelation($relation, $relatedModel);
0 ignored issues
show
Bug introduced by
$relation of type Illuminate\Database\Eloquent\Relations\Relation is incompatible with the type string expected by parameter $relation of Illuminate\Database\Eloquent\Model::setRelation(). ( Ignorable by Annotation )

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

502
                            $model->setRelation(/** @scrutinizer ignore-type */ $relation, $relatedModel);
Loading history...
503 1
                            break;
504 1
                    }
505 1
                }
506 1
507
                $previousModel = $relatedModel;
508 2
                if ($i === $count) {
509
                    break;
510 1
                } elseif (is_null($relatedModel)) {
511 1
                    throw new LogicException("Field [{$path}] can't be mapped to relations of model ".get_class($model)
512
                        .'. Probably some dot delimeted segment is not a supported relation type');
513 4
                }
514
            }
515
516
            $model = $previousModel;
517
        }
518
519
        return $model;
520
    }
521
522
    protected function getForeignKeyNameFromRelation($relation)
523
    {
524
        return method_exists($relation, 'getForeignKeyName')
525
            ? $relation->getForeignKeyName()
526
            : $relation->getPlainForeignKey();
527
    }
528
529
    /**
530
     * Field->mutateValue(function($value) {
531
     *     return bcrypt($value);
532 2
     * }).
533
     *
534 2
     * @param \Closure $mutator
535
     *
536 2
     * @return $this
537
     */
538
    public function mutateValue(\Closure $mutator)
539
    {
540
        $this->mutator = $mutator;
541
542 5
        return $this;
543
    }
544 5
545
    /**
546
     * @return bool
547
     */
548
    public function hasMutator()
549
    {
550
        return is_callable($this->mutator);
551
    }
552 4
553
    /**
554 4
     * @param mixed $value
555 1
     *
556 1
     * @return mixed
557
     */
558 4
    public function prepareValue($value)
559
    {
560
        if ($this->hasMutator()) {
561
            $value = call_user_func($this->mutator, $value);
562
        }
563
564 1
        return $value;
565
    }
566 1
567 1
    /**
568 1
     * @return array
569 1
     */
570
    public function toArray()
571 1
    {
572 1
        $this->setHtmlAttributes([
573 1
            'id'   => $this->getName(),
574 1
            'name' => $this->getName(),
575 1
        ]);
576 1
577 1
        return array_merge(parent::toArray(), [
578 1
            'id'         => $this->getName(),
579 1
            'value'      => $this->getValueFromModel(),
580 1
            'name'       => $this->getName(),
581
            'path'       => $this->getPath(),
582
            'label'      => $this->getLabel(),
583
            'attributes' => $this->htmlAttributesToString(),
584
            'helpText'   => $this->getHelpText(),
585
            'required'   => in_array('required', $this->validationRules),
586
        ]);
587
    }
588
}
589