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.
Passed
Push — master ( b2510c...ee0bb8 )
by
unknown
12:46
created

NamedFormElement::getForeignKeyNameFromRelation()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
dl 0
loc 6
c 0
b 0
f 0
cc 2
eloc 4
nc 2
nop 1
ccs 0
cts 4
cp 0
crap 6
rs 9.4285
1
<?php
2
3
namespace SleepingOwl\Admin\Form\Element;
4
5
use LogicException;
6
use Illuminate\Database\Eloquent\Model;
7
use SleepingOwl\Admin\Form\FormElement;
8
use Illuminate\Contracts\Support\Htmlable;
9
use KodiComponents\Support\HtmlAttributes;
10
use Illuminate\Database\Eloquent\Relations\HasOne;
11
use Illuminate\Database\Eloquent\Relations\MorphOne;
12
use Illuminate\Database\Eloquent\Relations\Relation;
13
use Illuminate\Database\Eloquent\Relations\BelongsTo;
14
use SleepingOwl\Admin\Exceptions\Form\FormElementException;
15
16
abstract class NamedFormElement extends FormElement
17
{
18
    use HtmlAttributes;
19
    /**
20
     * @var string
21
     */
22
    protected $path;
23
24
    /**
25
     * @var string
26
     */
27
    protected $name;
28
29
    /**
30
     * @var string
31
     */
32
    protected $modelAttributeKey;
33
34
    /**
35
     * @var string
36
     */
37
    protected $label;
38
39
    /**
40
     * @var string
41
     */
42
    protected $helpText;
43
44
    /**
45
     * @var mixed
46
     */
47
    protected $defaultValue;
48
49
    /**
50
     * @var \Closure
51
     */
52
    protected $mutator;
53
54
    /**
55
     * @param string $path
56
     * @param string|null $label
57
     *
58
     * @throws FormElementException
59
     */
60 27
    public function __construct($path, $label = null)
61
    {
62 27
        if (empty($path)) {
63 1
            throw new FormElementException('You must specify field path');
64
        }
65
66 26
        $this->setPath($path);
67 26
        $this->setLabel($label);
68
69 26
        $parts = explode('.', $path);
70 26
        $this->setName($this->composeName($parts));
71 26
        $this->setModelAttributeKey(end($parts));
72
73 26
        parent::__construct();
74 26
    }
75
76
    /**
77
     * Compose html name from array like this: 'first[second][third]'.
78
     *
79
     * @param array $parts
80
     *
81
     * @return string
82
     */
83 26
    private function composeName(array $parts)
84
    {
85 26
        $name = array_shift($parts);
86
87 26
        while (! empty($parts)) {
88 7
            $part = array_shift($parts);
89 7
            $name .= "[$part]";
90 7
        }
91
92 26
        return $name;
93
    }
94
95
    /**
96
     * @return string
97
     */
98 13
    public function getPath()
99
    {
100 13
        return $this->path;
101
    }
102
103
    /**
104
     * @param string $path
105
     *
106
     * @return $this
107
     */
108 26
    public function setPath($path)
109
    {
110 26
        $this->path = $path;
111
112 26
        return $this;
113
    }
114
115
    /**
116
     * @return string
117
     */
118 5
    public function getName()
119
    {
120 5
        return $this->name;
121
    }
122
123
    /**
124
     * @param string $name
125
     *
126
     * @return $this
127
     */
128 26
    public function setName($name)
129
    {
130 26
        $this->name = $name;
131
132 26
        return $this;
133
    }
134
135
    /**
136
     * @return string
137
     */
138 4
    public function getLabel()
139
    {
140 4
        return $this->label;
141
    }
142
143
    /**
144
     * @param string $label
145
     *
146
     * @return $this
147
     */
148 26
    public function setLabel($label)
149
    {
150 26
        $this->label = $label;
151
152 26
        return $this;
153
    }
154
155
    /**
156
     * @return string
157
     */
158 6
    public function getModelAttributeKey()
159
    {
160 6
        return $this->modelAttributeKey;
161
    }
162
163
    /**
164
     * @param string $key
165
     *
166
     * @return $this
167
     */
168 26
    public function setModelAttributeKey($key)
169
    {
170 26
        $this->modelAttributeKey = $key;
171
172 26
        return $this;
173
    }
174
175
    /**
176
     * @return mixed
177
     */
178 3
    public function getDefaultValue()
179
    {
180 3
        return $this->defaultValue;
181
    }
182
183
    /**
184
     * @param mixed $defaultValue
185
     *
186
     * @return $this
187
     */
188 1
    public function setDefaultValue($defaultValue)
189
    {
190 1
        $this->defaultValue = $defaultValue;
191
192 1
        return $this;
193
    }
194
195
    /**
196
     * @return string
197
     */
198 2
    public function getHelpText()
199
    {
200 2
        if ($this->helpText instanceof Htmlable) {
201 1
            return $this->helpText->toHtml();
202
        }
203
204 2
        return $this->helpText;
205
    }
206
207
    /**
208
     * @param string|Htmlable $helpText
209
     *
210
     * @return $this
211
     */
212 1
    public function setHelpText($helpText)
213
    {
214 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...
215
216 1
        return $this;
217
    }
218
219
    /**
220
     * @param string|null $message
221
     *
222
     * @return $this
223
     */
224 2
    public function required($message = null)
225
    {
226 2
        $this->addValidationRule('required', $message);
227
228 2
        return $this;
229
    }
230
231
    /**
232
     * @param string|null $message
233
     *
234
     * @return $this
235
     */
236 2
    public function unique($message = null)
237
    {
238 2
        $this->addValidationRule('_unique');
239
240 2
        if (! is_null($message)) {
241 1
            $this->addValidationMessage('unique', $message);
242 1
        }
243
244 2
        return $this;
245
    }
246
247
    /**
248
     * @return array
249
     */
250 3
    public function getValidationMessages()
251
    {
252 3
        $messages = parent::getValidationMessages();
253
254 3
        foreach ($messages as $rule => $message) {
255 3
            $messages[$this->getName().'.'.$rule] = $message;
256 3
            unset($messages[$rule]);
257 3
        }
258
259 3
        return $messages;
260
    }
261
262
    /**
263
     * @return array
264
     */
265 1
    public function getValidationLabels()
266
    {
267 1
        return [$this->getPath() => $this->getLabel()];
268
    }
269
270
    /**
271
     * If FormElement has `_unique` rule, it will get all appropriate
272
     * validation rules based on underlying model.
273
     *
274
     * @return array
275
     */
276 2
    public function getValidationRules()
277
    {
278 2
        $rules = parent::getValidationRules();
279
280 2
        foreach ($rules as &$rule) {
281 2
            if ($rule !== '_unique') {
282 1
                continue;
283
            }
284
285 1
            $model = $this->resolvePath();
286 1
            $table = $model->getTable();
287
288 1
            $rule = 'unique:'.$table.','.$this->getModelAttributeKey();
289 1
            if ($model->exists) {
290
                $rule .= ','.$model->getKey();
291
            }
292 2
        }
293 2
        unset($rule);
294
295 2
        return [$this->getPath() => $rules];
296
    }
297
298
    /**
299
     * Get model related to form element.
300
     *
301
     * @return mixed
302
     */
303 2
    public function resolvePath()
304
    {
305 2
        $model = $this->getModel();
306 2
        $relations = explode('.', $this->getPath());
307 2
        $count = count($relations);
308
309 2
        if ($count === 1) {
310 2
            return $model;
311
        }
312
313 1
        foreach ($relations as $relation) {
314 1
            if ($count === 1) {
315 1
                return $model;
316
            }
317
318 1
            if ($model->exists && ($value = $model->getAttribute($relation)) instanceof Model) {
319 1
                $model = $value;
320
321 1
                $count--;
322 1
                continue;
323
            }
324
325 1
            if (method_exists($model, $relation)) {
326 1
                $relation = $model->{$relation}();
327
328 1
                if ($relation instanceof Relation) {
329 1
                    $model = $relation->getModel();
330 1
                    $count--;
331 1
                    continue;
332
                }
333
            }
334
335
            break;
336
        }
337
338
        throw new LogicException("Can not resolve path for field '{$this->getPath()}'. Probably relation definition is incorrect");
339
    }
340
341
    /**
342
     * @param \Illuminate\Http\Request $request
343
     *
344
     * @return array|string
345
     */
346 6
    public function getValueFromRequest(\Illuminate\Http\Request $request)
347
    {
348 6
        if ($request->hasSession() && ! is_null($value = $request->old($this->getPath()))) {
349 1
            return $value;
350
        }
351
352 5
        return $request->input($this->getPath());
353
    }
354
355
    /**
356
     * @return mixed
357
     */
358 3
    public function getValueFromModel()
359
    {
360 3
        if (! is_null($value = $this->getValueFromRequest(request()))) {
361 1
            return $value;
362
        }
363
364 2
        $model = $this->getModel();
365 2
        $path = $this->getPath();
366 2
        $value = $this->getDefaultValue();
367
368 2
        if (is_null($model) or ! $model->exists) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
Using logical operators such as or instead of || is generally not recommended.

PHP has two types of connecting operators (logical operators, and boolean operators):

  Logical Operators Boolean Operator
AND - meaning and &&
OR - meaning or ||

The difference between these is the order in which they are executed. In most cases, you would want to use a boolean operator like &&, or ||.

Let’s take a look at a few examples:

// Logical operators have lower precedence:
$f = false or true;

// is executed like this:
($f = false) or true;


// Boolean operators have higher precedence:
$f = false || true;

// is executed like this:
$f = (false || true);

Logical Operators are used for Control-Flow

One case where you explicitly want to use logical operators is for control-flow such as this:

$x === 5
    or die('$x must be 5.');

// Instead of
if ($x !== 5) {
    die('$x must be 5.');
}

Since die introduces problems of its own, f.e. it makes our code hardly testable, and prevents any kind of more sophisticated error handling; you probably do not want to use this in real-world code. Unfortunately, logical operators cannot be combined with throw at this point:

// The following is currently a parse error.
$x === 5
    or throw new RuntimeException('$x must be 5.');

These limitations lead to logical operators rarely being of use in current PHP code.

Loading history...
369 2
            return $value;
370
        }
371
372 1
        $relations = explode('.', $path);
373 1
        $count = count($relations);
374
375 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...
376 1
            $attribute = $model->getAttribute($this->getModelAttributeKey());
377
378 1
            if (! empty($attribute) || $attribute === 0 || is_null($value)) {
379 1
                return $attribute;
380
            }
381
        }
382
383
        foreach ($relations as $relation) {
384
            if ($model->{$relation} instanceof Model) {
385
                $model = $model->{$relation};
386
                continue;
387
            }
388
389 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...
390
                $attribute = $model->getAttribute($relation);
391
392
                if (! empty($attribute) or is_null($value)) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
Using logical operators such as or instead of || is generally not recommended.

PHP has two types of connecting operators (logical operators, and boolean operators):

  Logical Operators Boolean Operator
AND - meaning and &&
OR - meaning or ||

The difference between these is the order in which they are executed. In most cases, you would want to use a boolean operator like &&, or ||.

Let’s take a look at a few examples:

// Logical operators have lower precedence:
$f = false or true;

// is executed like this:
($f = false) or true;


// Boolean operators have higher precedence:
$f = false || true;

// is executed like this:
$f = (false || true);

Logical Operators are used for Control-Flow

One case where you explicitly want to use logical operators is for control-flow such as this:

$x === 5
    or die('$x must be 5.');

// Instead of
if ($x !== 5) {
    die('$x must be 5.');
}

Since die introduces problems of its own, f.e. it makes our code hardly testable, and prevents any kind of more sophisticated error handling; you probably do not want to use this in real-world code. Unfortunately, logical operators cannot be combined with throw at this point:

// The following is currently a parse error.
$x === 5
    or throw new RuntimeException('$x must be 5.');

These limitations lead to logical operators rarely being of use in current PHP code.

Loading history...
393
                    return $attribute;
394
                }
395
            }
396
397
            if (is_null($this->getDefaultValue())) {
398
                throw new LogicException("Can not fetch value for field '{$path}'. Probably relation definition is incorrect");
399
            }
400
        }
401
402
        return $value;
403
    }
404
405
    /**
406
     * @param \Illuminate\Http\Request $request
407
     *
408
     * @return void
409
     */
410 1
    public function save(\Illuminate\Http\Request $request)
411
    {
412 1
        $this->setModelAttribute(
413 1
            $this->getValueFromRequest(
414
                $request
415 1
            )
416 1
        );
417 1
    }
418
419
    /**
420
     * @param mixed  $value
421
     *
422
     * @return void
423
     */
424 3
    public function setModelAttribute($value)
425
    {
426 3
        $model = $this->getModelByPath(
427 3
            $this->getPath()
428 3
        );
429
430 3
        if ($this->isValueSkipped()) {
431 1
            return;
432
        }
433
434 3
        $model->setAttribute(
435 3
            $this->getModelAttributeKey(),
436 3
            $this->prepareValue($value)
437 3
        );
438 3
    }
439
440
    /**
441
     * @param string $path
442
     *
443
     * @return Model|null
444
     */
445 5
    protected function getModelByPath($path)
446
    {
447 5
        $model = $this->getModel();
448
449 5
        $relations = explode('.', $path);
450 5
        $count = count($relations);
451 5
        $i = 1;
452
453 5
        if ($count > 1) {
454 2
            $i++;
455 2
            $previousModel = $model;
456
457
            /* @var Model $model */
458 2
            foreach ($relations as $relation) {
459 2
                $relatedModel = null;
460 2
                if ($previousModel->getAttribute($relation) instanceof Model) {
461 2
                    $relatedModel = $previousModel->getAttribute($relation);
462 2
                } elseif (method_exists($previousModel, $relation)) {
463
464
                    /* @var Relation $relation */
465
                    $relationObject = $previousModel->{$relation}();
466
467
                    switch (get_class($relationObject)) {
468
                        case BelongsTo::class:
469
                            $relationObject->associate($relatedModel = $relationObject->getRelated());
470
                            break;
471
                        case HasOne::class:
472
                        case MorphOne::class:
473
                            $relatedModel = $relationObject->getRelated()->newInstance();
474
                            $relatedModel->setAttribute($this->getForeignKeyNameFromRelation($relationObject), $relationObject->getParentKey());
475
                            $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...
476
                            break;
477
                    }
478
                }
479
480 2
                $previousModel = $relatedModel;
481 2
                if ($i === $count) {
482 1
                    break;
483 1
                } elseif (is_null($relatedModel)) {
484 1
                    throw new LogicException("Field [{$path}] can't be mapped to relations of model ".get_class($model)
485 1
                        .'. Probably some dot delimeted segment is not a supported relation type');
486
                }
487 2
            }
488
489 1
            $model = $previousModel;
490 1
        }
491
492 4
        return $model;
493
    }
494
495
    protected function getForeignKeyNameFromRelation($relation)
496
    {
497
        return method_exists($relation, 'getForeignKeyName')
498
            ? $relation->getForeignKeyName()
499
            : $relation->getPlainForeignKey();
500
    }
501
502
    /**
503
     * Field->mutateValue(function($value) {
504
     *     return bcrypt($value);
505
     * }).
506
     *
507
     * @param \Closure $mutator
508
     *
509
     * @return $this
510
     */
511 2
    public function mutateValue(\Closure $mutator)
512
    {
513 2
        $this->mutator = $mutator;
514
515 2
        return $this;
516
    }
517
518
    /**
519
     * @return bool
520
     */
521 5
    public function hasMutator()
522
    {
523 5
        return is_callable($this->mutator);
524
    }
525
526
    /**
527
     * @param mixed $value
528
     *
529
     * @return mixed
530
     */
531 4
    public function prepareValue($value)
532
    {
533 4
        if ($this->hasMutator()) {
534 1
            $value = call_user_func($this->mutator, $value);
535 1
        }
536
537 4
        return $value;
538
    }
539
540
    /**
541
     * @return array
542
     */
543 1
    public function toArray()
544
    {
545 1
        $this->setHtmlAttributes([
546 1
            'id' => $this->getName(),
547 1
            'name' => $this->getName(),
548 1
        ]);
549
550 1
        return array_merge(parent::toArray(), [
551 1
            'id' => $this->getName(),
552 1
            'value' => $this->getValueFromModel(),
553 1
            'name' => $this->getName(),
554 1
            'path' => $this->getPath(),
555 1
            'label' => $this->getLabel(),
556 1
            'attributes'=> $this instanceof Select ? $this->getHtmlAttributes() : $this->htmlAttributesToString(),
557 1
            'helpText' => $this->getHelpText(),
558 1
            'required' => in_array('required', $this->validationRules),
559 1
        ]);
560
    }
561
}
562