Completed
Push — master ( 26a074...85fd0f )
by Nicolaas
01:22
created

API   F

Complexity

Total Complexity 65

Size/Duplication

Total Lines 353
Duplicated Lines 1.7 %

Coupling/Cohesion

Components 5
Dependencies 5

Importance

Changes 0
Metric Value
wmc 65
lcom 5
cbo 5
dl 6
loc 353
rs 3.3333
c 0
b 0
f 0

21 Methods

Rating   Name   Duplication   Size   Complexity  
A inst() 0 10 2
A setBaseClass() 0 4 1
B retrieveDBFields() 0 14 5
C DbFields() 6 40 12
A MyDbFields() 0 4 1
A MyDbFieldsWithDefaults() 0 8 1
A MyDbFieldsFancyWithBelongs() 0 4 1
D MyDbFieldsFancyWithoutBelongs() 0 43 12
A MyDbFieldsAndHasOnes() 0 6 1
A MyDbFieldsAndHasOnesWithIDs() 0 11 2
A MyDbFieldsAndIndexes() 0 10 1
A MyAllFieldsWithBelongs() 0 4 1
A MyAllFieldsWithoutBelongs() 0 16 3
A IndexOptions() 0 8 1
A RequiredOptions() 0 7 1
A PossibleRelationsWithBaseClass() 0 5 1
C PossibleRelations() 0 26 7
A PossibleSearchFilters() 0 15 4
B ModelAdminOptions() 0 21 5
A SortOptions() 0 7 1
A CanOptions() 0 16 2

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like API 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. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

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 API, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace SunnySideUp\BuildDataObject;
4
5
class API extends \Object
6
{
7
8
    private static $excluded_data_objects = [
0 ignored issues
show
Unused Code introduced by
The property $excluded_data_objects is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
9
        'Image_Cached',
10
        'PermissionRoleCode',
11
        'LoginAttempt',
12
        'MemberPassword',
13
        'MemberPassword',
14
        'SiteConfig'
15
    ];
16
17
    private static $excluded_db_fields_types = [
0 ignored issues
show
Unused Code introduced by
The property $excluded_db_fields_types is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
18
        'DBField',
19
        'Field',
20
        'DBLocale',
21
        'Locale',
22
        'StringField',
23
        'CompositeField',
24
        'PrimaryKey',
25
        'ForeignKey'
26
    ];
27
28
    private static $additional_db_fields = [
0 ignored issues
show
Unused Code introduced by
The property $additional_db_fields is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
29
        'ID',
30
        'Created',
31
        'LastEdited',
32
        'ClassName'
33
    ];
34
35
36
    protected $myBaseClass = '';
37
38
    protected $_data = null;
39
40
    private static $_my_singleton = [];
41
42
    public static function inst($myBaseClass = 'DataObject', $data)
0 ignored issues
show
Coding Style introduced by
Parameters which have default values should be placed at the end.

If you place a parameter with a default value before a parameter with a default value, the default value of the first parameter will never be used as it will always need to be passed anyway:

// $a must always be passed; it's default value is never used.
function someFunction($a = 5, $b) { }
Loading history...
Documentation introduced by
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
43
    {
44
        if(! isset(self::$_my_singleton[$myBaseClass])) {
45
            self::$_my_singleton[$myBaseClass] = \Injector::inst()->get('SunnySideUp\BuildDataObject\API');
46
        }
47
        self::$_my_singleton[$myBaseClass]->_data = $data;
48
        self::$_my_singleton[$myBaseClass]->setBaseClass($myBaseClass);
49
50
        return self::$_my_singleton[$myBaseClass];
51
    }
52
53
    public function setBaseClass($myBaseClass)
54
    {
55
        $this->myBaseClass = $myBaseClass;
56
    }
57
58
59
    protected function retrieveDBFields($name)
60
    {
61
        $data = $this->_data;
62
        $ar = [];
63
        if(isset($data->$name)) {
64
            foreach($data->$name as $data) {
65
                if($data->Key && $data->Value) {
66
                    $ar[$data->Key] = $data->Key;
67
                }
68
            }
69
        }
70
71
        return $ar;
72
    }
73
74
    protected $_dbfieldCache = [];
75
76
    public function DbFields()
77
    {
78
        if(count($this->_dbfieldCache) === 0) {
79
            $list = \ClassInfo::subclassesFor('DbField');
80
            $newList = [];
81
            foreach($list as $class) {
0 ignored issues
show
Bug introduced by
The expression $list of type null|array is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
82 View Code Duplication
                if(substr($class, 0, 2) == 'DB') {
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...
83
                    $class = substr($class, 2, strlen($class));
84
                }
85 View Code Duplication
                if(substr($class, 0, 3) == 'SS_') {
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...
86
                    $class = substr($class, 3, strlen($class));
87
                }
88
                if('Varchar' === $class) {
89
                    $class = 'Varchar(n)';
90
                }
91
                if('HTMLVarchar' === $class) {
92
                    $class = 'HTMLVarchar(n)';
93
                }
94
                if('Enum' === $class) {
95
                    $class = 'Enum(\\\'Foo,Bar\\\', \\\'FOO\\\')';
96
                }
97
                if('MultiEnum' === $class) {
98
                    $class = 'MultiEnum(\\\'Foo,Bar\\\', \\\'FOO\\\')';
99
                }
100
                if(
0 ignored issues
show
Unused Code introduced by
This if statement is empty and can be removed.

This check looks for the bodies of if statements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

These if bodies can be removed. If you have an empty if but statements in the else branch, consider inverting the condition.

if (rand(1, 6) > 3) {
//print "Check failed";
} else {
    print "Check succeeded";
}

could be turned into

if (rand(1, 6) <= 3) {
    print "Check succeeded";
}

This is much more concise to read.

Loading history...
101
                    $class == 'DbField' ||
102
                    is_subclass_of($class, 'TestOnly') ||
103
                    in_array($class, $this->Config()->get('excluded_db_fields_types'))
104
                ) {
105
                    //do nothing
106
                } else {
107
                    $newList[$class] = $class;
108
                }
109
            }
110
            ksort($newList);
111
            $this->_dbfieldCache = $newList;
112
        }
113
114
        return $this->_dbfieldCache;
115
    }
116
117
    public function MyDbFields()
118
    {
119
        return $this->retrieveDBFields('db');
120
    }
121
122
    public function MyDbFieldsWithDefaults()
123
    {
124
        $list = $this->retrieveDBFields('db');
125
        $toAdd = $this->Config()->get('additional_db_fields');
126
        $toAdd = array_combine($toAdd, $toAdd);
127
128
        return $toAdd + $list;
129
    }
130
131
    public function MyDbFieldsFancyWithBelongs()
132
    {
133
        return $this->myDbFieldsFancyWithoutBelongs(true);
134
    }
135
136
    public function MyDbFieldsFancyWithoutBelongs($includeBelongs = false)
137
    {
138
        $ar = [];
139
        $list = $this->retrieveDBFields('db');
140
        foreach($list as $key => $value) {
141
            $ar[$key] = $key;
142
            $shortValue = explode('(',$value);
143
            $shortValue = $shortValue[0];
144
            switch($shortValue) {
145
                case 'Varchar':
146
                case 'HTMLTextField':
147
                case 'HTMLVarchar':
148
                case 'Text':
149
                    $ar[$key.'.LimitCharacters'] = $key.'.LimitCharacters';
150
                    break;
151
                default:
152
                    $ar[$key.'.Nice'] = $key.'.Nice';
153
            }
154
        }
155
        $list = [];
156
        if($includeBelongs) {
157
            $list += $this->retrieveDBFields('belongs_to');
158
        }
159
        $list += $this->retrieveDBFields('has_one');
160
        foreach($list as $key => $value) {
161
            if($value === 'Image' || is_subclass_of($value, 'Image')) {
162
                $ar[$key.'.Thumbnail'] = $key.'.Thumbnail';
163
            } else {
164
                $ar[$key.'.Title'] = $key.'.Title';
165
            }
166
        }
167
        $list =
168
            $this->retrieveDBFields('has_many') +
169
            $this->retrieveDBFields('many_many');
170
        if($includeBelongs) {
171
            $list += $this->retrieveDBFields('belongs_many_many');
172
        }
173
        foreach($list as $key => $value) {
174
            $ar[$key.'.Count'] = $key.'.Count';
175
        }
176
177
        return $ar;
178
    }
179
180
    public function MyDbFieldsAndHasOnes()
181
    {
182
        return
183
            $this->retrieveDBFields('db') +
184
            $this->retrieveDBFields('has_one');
185
    }
186
187
    public function MyDbFieldsAndHasOnesWithIDs()
188
    {
189
        $list = $this->retrieveDBFields('db');
190
        $hasOnes = $this->retrieveDBFields('has_one');
191
        foreach($hasOnes as $field => $type) {
192
            $fieldWithID = $field . 'ID';
193
            $list[$fieldWithID] = $fieldWithID;
194
        }
195
196
        return $list;
197
    }
198
199
    public function MyDbFieldsAndIndexes()
200
    {
201
        return
202
            $this->MyDbFieldsWithDefaults() +
203
            ['index1' => 'index1'] +
204
            ['index2' => 'index2'] +
205
            ['index3' => 'index3'] +
206
            ['index4' => 'index4'] +
207
            ['index5' => 'index5'];
208
    }
209
210
    public function MyAllFieldsWithBelongs()
211
    {
212
        return $this->myAllFieldsWithoutBelongs(true);
213
    }
214
215
    public function MyAllFieldsWithoutBelongs($includeBelongs = false)
216
    {
217
        $list = $this->MyDbFieldsWithDefaults();
218
        if($includeBelongs) {
219
            $list += $this->retrieveDBFields('belongs_to');
220
        }
221
        $list +=
222
            $this->retrieveDBFields('has_one') +
223
            $this->retrieveDBFields('has_many') +
224
            $this->retrieveDBFields('many_many');
225
        if($includeBelongs) {
226
            $list += $this->retrieveDBFields('belongs_many_many');
227
        }
228
229
        return $list;
230
    }
231
232
233
    public function IndexOptions()
234
    {
235
        return [
236
            'true' => 'true',
237
            'unique("<column-name>")' => 'unique',
238
            '[\'type\' => \'<type>\', \'value\' => \'"<column-name>"\']' => 'other'
239
        ];
240
    }
241
242
    public function RequiredOptions()
243
    {
244
        return [
245
            'true' => 'true',
246
            'unique' => 'unique'
247
        ];
248
    }
249
250
251
    public function PossibleRelationsWithBaseClass() {
252
        return
253
            [$this->myBaseClass => $this->myBaseClass] +
254
            $this->possibleRelations();
255
    }
256
257
    protected $_classesCache = [];
258
259
    public function PossibleRelations()
260
    {
261
        if(count($this->_classesCache) === 0) {
262
            $list = \ClassInfo::subclassesFor($this->myBaseClass);
263
            $newList = [];
0 ignored issues
show
Unused Code introduced by
$newList is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
264
            $newList = [];
265
            foreach($list as $class) {
0 ignored issues
show
Bug introduced by
The expression $list of type null|array is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
266
                if(
0 ignored issues
show
Unused Code introduced by
This if statement is empty and can be removed.

This check looks for the bodies of if statements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

These if bodies can be removed. If you have an empty if but statements in the else branch, consider inverting the condition.

if (rand(1, 6) > 3) {
//print "Check failed";
} else {
    print "Check succeeded";
}

could be turned into

if (rand(1, 6) <= 3) {
    print "Check succeeded";
}

This is much more concise to read.

Loading history...
267
                    $class == $this->myBaseClass ||
268
                    is_subclass_of($class, 'TestOnly') ||
269
                    in_array($class, $this->Config()->get('excluded_data_objects'))
270
                ) {
271
                    //do nothing
272
                } else {
273
                    $newList[$class] = $class;
274
                    $name = \Injector::inst()->get($class)->singular_name();
275
                    if($name !== $class) {
276
                        $newList[$class] .= ' ('.$name.')';
277
                    }
278
                }
279
            }
280
            $this->_classesCache = $newList;
281
        }
282
283
        return $this->_classesCache;
284
    }
285
286
    protected $_filtersCache = [];
287
288
    public function PossibleSearchFilters()
289
    {
290
        if(count($this->_filtersCache) === 0) {
291
            $list = \ClassInfo::subclassesFor('SearchFilter');
292
            $newList = [];
293
            foreach($list as $class) {
0 ignored issues
show
Bug introduced by
The expression $list of type null|array is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
294
                if($class !== 'SearchFilter') {
295
                    $newList[$class] = $class;
296
                }
297
            }
298
            $this->_filtersCache = $newList;
299
        }
300
301
        return $this->_filtersCache;
302
    }
303
304
    protected $_modelAdmins = [];
305
306
    public function ModelAdminOptions()
307
    {
308
        if(count($this->_modelAdmins) === 0) {
309
            $list = \ClassInfo::subclassesFor('ModelAdmin');
310
            $newList = [];
311
            foreach($list as $class) {
0 ignored issues
show
Bug introduced by
The expression $list of type null|array is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
312
                if(
0 ignored issues
show
Unused Code introduced by
This if statement is empty and can be removed.

This check looks for the bodies of if statements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

These if bodies can be removed. If you have an empty if but statements in the else branch, consider inverting the condition.

if (rand(1, 6) > 3) {
//print "Check failed";
} else {
    print "Check succeeded";
}

could be turned into

if (rand(1, 6) <= 3) {
    print "Check succeeded";
}

This is much more concise to read.

Loading history...
313
                    $class == 'ModelAdmin' ||
314
                    is_subclass_of($class, 'TestOnly')
315
                ) {
316
                    //do nothing
317
                } else {
318
                    $newList[$class] = $class;
319
                }
320
            }
321
            $newList['tba'] = 'tba';
322
            $this->_modelAdmins = $newList;
323
        }
324
325
        return $this->_modelAdmins;
326
    }
327
328
329
    public function SortOptions()
330
    {
331
        return [
332
            'ASC' => 'ASC',
333
            'DESC' => 'DESC'
334
        ];
335
    }
336
337
    protected $_canOptions = null;
338
339
    public function CanOptions()
0 ignored issues
show
Documentation introduced by
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
340
    {
341
        if(! $this->_canOptions) {
342
            $ar = [
343
                'true' => 'always',
344
                'false' => 'never',
345
                'parent' => 'use parent class',
346
            ];
347
            $permissions = \Permission::get()->column('Code');
348
            $ar = $ar + array_combine($permissions, $permissions);
349
350
            $this->_canOptions = $ar;
351
        }
352
353
        return $this->_canOptions;
354
    }
355
356
357
}
358