Scrutinizer GitHub App not installed

We could not synchronize checks via GitHub's checks API since Scrutinizer's GitHub App is not installed for this repository.

Install GitHub App

Test Failed
Pull Request — master (#3410)
by
unknown
11:02
created

Relationships   A

Complexity

Total Complexity 41

Size/Duplication

Total Lines 218
Duplicated Lines 0 %

Importance

Changes 6
Bugs 2 Features 0
Metric Value
eloc 81
dl 0
loc 218
rs 9.1199
c 6
b 2
f 0
wmc 41

10 Methods

Rating   Name   Duplication   Size   Complexity  
A isNestedRelation() 0 7 2
B getRelationInstance() 0 30 7
A inferRelationTypeFromRelationship() 0 5 1
A parseRelationFieldNamesFromHtml() 0 17 6
A getFieldsWithRelationType() 0 13 3
A inferFieldModelFromRelationship() 0 5 1
B inferFieldTypeFromFieldRelation() 0 12 7
A guessIfFieldHasPivotFromRelationType() 0 10 3
B guessIfFieldHasMultipleFromRelationType() 0 13 8
A getOnlyRelationEntity() 0 19 3

How to fix   Complexity   

Complex Class

Complex classes like Relationships 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 Relationships, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace Backpack\CRUD\app\Library\CrudPanel\Traits;
4
5
use Illuminate\Database\Eloquent\Model;
6
use Illuminate\Support\Arr;
7
use Illuminate\Support\Str;
8
9
trait Relationships
10
{
11
    /**
12
     * From the field entity we get the relation instance.
13
     *
14
     * @param array $entity
15
     * @return object
16
     */
17
    public function getRelationInstance($field)
18
    {
19
        $entity = $this->getOnlyRelationEntity($field);
20
        $entity_array = explode('.', $entity);
21
        $relation_model = $this->getRelationModel($entity);
0 ignored issues
show
Bug introduced by
The method getRelationModel() does not exist on Backpack\CRUD\app\Librar...el\Traits\Relationships. Did you maybe mean getRelationInstance()? ( Ignorable by Annotation )

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

21
        /** @scrutinizer ignore-call */ 
22
        $relation_model = $this->getRelationModel($entity);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
22
23
        $related_method = Arr::last($entity_array);
24
        if (count(explode('.', $entity)) == count(explode('.', $field['entity']))) {
25
            $relation_model = $this->getRelationModel($entity, -1);
26
        }
27
        $relation_model = new $relation_model();
28
29
        // if counts are diferent means that last element of entity is the field in relation.
30
        if (count(explode('.', $entity)) != count(explode('.', $field['entity']))) {
31
            if (in_array($related_method, $relation_model->getFillable())) {
32
                if (count($entity_array) > 1) {
33
                    $related_method = $entity_array[(count($entity_array) - 2)];
34
                    $relation_model = $this->getRelationModel($entity, -2);
35
                } else {
36
                    $relation_model = $this->model;
37
                }
38
            }
39
        }
40
        if (count($entity_array) == 1) {
41
            if (method_exists($this->model, $related_method)) {
42
                return $this->model->{$related_method}();
43
            }
44
        }
45
46
        return $relation_model->{$related_method}();
47
    }
48
49
    /**
50
     * Get the fields for relationships, according to the relation type. It looks only for direct
51
     * relations - it will NOT look through relationships of relationships.
52
     *
53
     * @param string|array $relation_types Eloquent relation class or array of Eloquent relation classes. Eg: BelongsTo
54
     *
55
     * @return array The fields with corresponding relation types.
56
     */
57
    public function getFieldsWithRelationType($relation_types): array
58
    {
59
        $relation_types = (array) $relation_types;
60
61
        return collect($this->fields())
0 ignored issues
show
Bug introduced by
It seems like fields() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

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

61
        return collect($this->/** @scrutinizer ignore-call */ fields())
Loading history...
62
            ->where('model')
63
            ->whereIn('relation_type', $relation_types)
64
            ->filter(function ($item) {
65
                $related_model = get_class($this->model->{Arr::first(explode('.', $item['entity']))}()->getRelated());
66
67
                return Str::contains($item['entity'], '.') && $item['model'] !== $related_model ? false : true;
68
            })
69
            ->toArray();
70
    }
71
72
    /**
73
     * Grabs an relation instance and returns the class name of the related model.
74
     *
75
     * @param array $field
76
     * @return string
77
     */
78
    public function inferFieldModelFromRelationship($field)
79
    {
80
        $relation = $this->getRelationInstance($field);
81
82
        return get_class($relation->getRelated());
83
    }
84
85
    /**
86
     * Return the relation type from a given field: BelongsTo, HasOne ... etc.
87
     *
88
     * @param array $field
89
     * @return string
90
     */
91
    public function inferRelationTypeFromRelationship($field)
92
    {
93
        $relation = $this->getRelationInstance($field);
94
95
        return Arr::last(explode('\\', get_class($relation)));
96
    }
97
98
    /**
99
     * Parse the field name back to the related entity after the form is submited.
100
     * Its called in getAllFieldNames().
101
     *
102
     * @param array $fields
103
     * @return array
104
     */
105
    public function parseRelationFieldNamesFromHtml($fields)
106
    {
107
        foreach ($fields as &$field) {
108
            // we only want to parse fields that has a relation type and their name contains [ ] used in html.
109
            if (isset($field['relation_type']) && preg_match('/[\[\]]/', $field['name']) !== 0) {
110
                $chunks = explode('[', $field['name']);
111
112
                foreach ($chunks as &$chunk) {
113
                    if (strpos($chunk, ']')) {
114
                        $chunk = str_replace(']', '', $chunk);
115
                    }
116
                }
117
                $field['name'] = implode('.', $chunks);
118
            }
119
        }
120
121
        return $fields;
122
    }
123
124
    /**
125
     * Based on relation type returns the default field type.
126
     *
127
     * @param string $relation_type
128
     * @return string
129
     */
130
    public function inferFieldTypeFromFieldRelation($field)
131
    {
132
        switch ($field['relation_type']) {
133
            case 'BelongsToMany':
134
            case 'HasMany':
135
            case 'HasManyThrough':
136
            case 'MorphMany':
137
            case 'MorphToMany':
138
            case 'BelongsTo':
139
                return 'relationship';
140
            default:
141
                return 'text';
142
        }
143
    }
144
145
    /**
146
     * Based on relation type returns if relation allows multiple entities.
147
     *
148
     * @param string $relation_type
149
     * @return bool
150
     */
151
    public function guessIfFieldHasMultipleFromRelationType($relation_type)
152
    {
153
        switch ($relation_type) {
154
            case 'BelongsToMany':
155
            case 'HasMany':
156
            case 'HasManyThrough':
157
            case 'HasOneOrMany':
158
            case 'MorphMany':
159
            case 'MorphOneOrMany':
160
            case 'MorphToMany':
161
                return true;
162
            default:
163
                return false;
164
        }
165
    }
166
167
    /**
168
     * Based on relation type returns if relation has a pivot table.
169
     *
170
     * @param string $relation_type
171
     * @return bool
172
     */
173
    public function guessIfFieldHasPivotFromRelationType($relation_type)
174
    {
175
        switch ($relation_type) {
176
            case 'BelongsToMany':
177
            case 'MorphToMany':
178
                return true;
179
            break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
180
            default:
181
                return false;
182
            break;
183
        }
184
    }
185
186
    /**
187
     * Check if field name contains a dot, if so, meaning it's a nested relation.
188
     *
189
     * @param array $field
190
     * @return bool
191
     */
192
    protected function isNestedRelation($field): bool
193
    {
194
        if (strpos($field['entity'], '.') !== false) {
195
            return true;
196
        }
197
198
        return false;
199
    }
200
201
    /**
202
     * Return the relation without any model attributes there.
203
     * Eg. user.entity_id would return user, as entity_id is not a relation in user.
204
     *
205
     * @param array $relation_field
206
     * @return string
207
     */
208
    public function getOnlyRelationEntity($relation_field)
209
    {
210
        $entity_array = explode('.', $relation_field['entity']);
211
212
        $relation_model = $this->getRelationModel($relation_field['entity'], -1);
213
214
        $related_method = Arr::last($entity_array);
215
216
        if (! method_exists($relation_model, $related_method)) {
217
            if (count($entity_array) <= 1) {
218
                return $relation_field['entity'];
219
            } else {
220
                array_pop($entity_array);
221
            }
222
223
            return implode('.', $entity_array);
224
        }
225
226
        return $relation_field['entity'];
227
    }
228
}
229