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
Pull Request — master (#688)
by
unknown
18:52
created

NamedFormElement::required()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 3
nc 1
nop 1
dl 0
loc 6
ccs 3
cts 3
cp 1
crap 1
rs 9.4285
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) {
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 object<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()))) {
362 1
            return $value;
363
        }
364
365 2
        $model = $this->getModel();
366 2
        $path = $this->getPath();
367 2
        $value = $this->getDefaultValue();
368
369 2
        if (is_null($model) || ! $model->exists) {
370 2
            return $value;
371
        }
372
373
        /*
374
         * Implement json parsing
375
         */
376 1
        if (strpos($path, '->') !== false) {
377
            $casts = collect($model->getCasts());
378
            $jsonParts = collect(explode('->', $path));
379
380
            $jsonAttr = $model->{$jsonParts->first()};
381
382
            $cast = $casts->get($jsonParts->first(), false);
383
384 View Code Duplication
            if ($cast == 'object') {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
385
                $jsonAttr = json_decode(json_encode($jsonAttr), true);
386
            } elseif ($cast != 'array') {
387
                $jsonAttr = json_decode($jsonAttr);
388
            }
389
390
            return Arr::get($jsonAttr, $jsonParts->slice(1)->implode('.'));
391
        }
392
393 1
        $relations = explode('.', $path);
394 1
        $count = count($relations);
395
396 1 View Code Duplication
        if ($count === 1) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
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
410 View Code Duplication
            if ($count === 2) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
411
                $attribute = $model->getAttribute($relation);
412
413
                if (! empty($attribute) || is_null($value)) {
414
                    return $attribute;
415
                }
416
            }
417
418
            if (is_null($this->getDefaultValue())) {
419
                throw new LogicException("Can not fetch value for field '{$path}'. Probably relation definition is incorrect");
420
            }
421
        }
422
423
        return $value;
424
    }
425
426
    /**
427
     * @param \Illuminate\Http\Request $request
428
     *
429
     * @return void
430
     */
431 1
    public function save(\Illuminate\Http\Request $request)
432
    {
433 1
        $this->setModelAttribute(
434 1
            $this->getValueFromRequest(
435
                $request
436 1
            )
437 1
        );
438 1
    }
439
440
    /**
441
     * @param mixed  $value
442
     *
443
     * @return void
444
     */
445 3
    public function setModelAttribute($value)
446
    {
447 3
        $model = $this->getModelByPath(
448 3
            $this->getPath()
449 3
        );
450
451 3
        if ($this->isValueSkipped()) {
452 1
            return;
453
        }
454
455 3
        $model->setAttribute(
456 3
            $this->getModelAttributeKey(),
457 3
            $this->prepareValue($value)
458 3
        );
459 3
    }
460
461
    /**
462
     * @param string $path
463
     *
464
     * @return Model|null
465
     */
466 5
    protected function getModelByPath($path)
467
    {
468 5
        $model = $this->getModel();
469
470 5
        $relations = explode('.', $path);
471 5
        $count = count($relations);
472 5
        $i = 1;
473
474 5
        if ($count > 1) {
475 2
            $i++;
476 2
            $previousModel = $model;
477
478
            /* @var Model $model */
479 2
            foreach ($relations as $relation) {
480 2
                $relatedModel = null;
481 2
                if ($previousModel->getAttribute($relation) instanceof Model) {
482 2
                    $relatedModel = $previousModel->getAttribute($relation);
483 2
                } elseif (method_exists($previousModel, $relation)) {
484
485
                    /* @var Relation $relation */
486
                    $relationObject = $previousModel->{$relation}();
487
488
                    switch (get_class($relationObject)) {
489
                        case BelongsTo::class:
490
                            $relationObject->associate($relatedModel = $relationObject->getRelated());
491
                            break;
492
                        case HasOne::class:
493
                        case MorphOne::class:
494
                            $relatedModel = $relationObject->getRelated()->newInstance();
495
                            $relatedModel->setAttribute($this->getForeignKeyNameFromRelation($relationObject), $relationObject->getParentKey());
496
                            $model->setRelation($relation, $relatedModel);
0 ignored issues
show
Documentation introduced by
$relation is of type object<Illuminate\Databa...ent\Relations\Relation>, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

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