Completed
Push — master ( d7178b...69eb04 )
by Nicolaas
01:33
created

BuildController   F

Complexity

Total Complexity 104

Size/Duplication

Total Lines 641
Duplicated Lines 6.4 %

Coupling/Cohesion

Components 1
Dependencies 17

Importance

Changes 0
Metric Value
wmc 104
lcom 1
cbo 17
dl 41
loc 641
rs 1.4494
c 0
b 0
f 0

32 Methods

Rating   Name   Duplication   Size   Complexity  
primaryThingsToBuild() 0 1 ?
secondaryThingsToBuild() 0 1 ?
A Link() 0 10 2
A LoadTemplateLink() 0 4 1
A Title() 0 4 1
A jQueryLink() 0 4 1
A startover() 0 6 1
A loadtemplate() 0 19 2
A turnStaticsIntoSessionData() 0 20 4
A index() 0 4 1
A primaryformstart() 8 8 1
A PrimaryForm() 0 6 1
A doprimaryform() 0 6 1
A secondaryformstart() 9 9 1
A SecondaryForm() 0 6 1
A dosecondaryform() 0 6 1
A results() 0 10 1
A debug() 0 7 1
A Form() 0 4 1
A FinalData() 0 4 1
A PrevLink() 0 4 1
A ClassNameForObject() 0 8 2
B MyCanMethodBuilder() 0 15 5
F createForm() 24 244 37
A additionalPrimaryFields() 0 4 1
A saveData() 0 19 4
B retrieveData() 0 21 8
C processedFormData() 0 56 17
A resultsTemplateForBuilder() 0 4 1
A prependNullOption() 0 6 1
A myAPI() 0 6 1
A addKeysToThingsToBuild() 0 18 3

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

1
<?php
2
3
4
namespace SunnySideUp\BuildDataObject;
5
6
7
8
abstract class BuildController extends \Controller {
9
10
11
    private static $form_data_session_variable = 'SunnySideUp\BuildDataObject\DataObjectBuildController';
0 ignored issues
show
Unused Code introduced by
The property $form_data_session_variable 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...
12
13
    private static $url_segment = 'build';
0 ignored issues
show
Unused Code introduced by
The property $url_segment 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...
14
15
    private static $allowed_actions = [
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
Unused Code introduced by
The property $allowed_actions 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...
16
        'primaryformstart' => true,
17
        'PrimaryForm' => true,
18
        'doprimaryform' => true,
19
        'secondaryformstart' => true,
20
        'SecondaryForm' => true,
21
        'dosecondaryform' => true,
22
        'results' => true,
23
        'startover' => true,
24
        'loadtemplate' => true,
25
        'debug' => true
26
    ];
27
28
    protected $myBaseClass = 'DataObject';
29
30
    protected $apiProvider = 'SunnySideUp\BuildDataObject\API';
31
32
    abstract protected function primaryThingsToBuild();
0 ignored issues
show
Documentation introduced by
For interfaces and abstract methods it is generally a good practice to add a @return annotation even if it is just @return void or @return null, so that implementors know what to do in the overridden method.

For interface and abstract methods, it is impossible to infer the return type from the immediate code. In these cases, it is generally advisible to explicitly annotate these methods with a @return doc comment to communicate to implementors of these methods what they are expected to return.

Loading history...
33
34
    abstract protected function secondaryThingsToBuild();
0 ignored issues
show
Documentation introduced by
For interfaces and abstract methods it is generally a good practice to add a @return annotation even if it is just @return void or @return null, so that implementors know what to do in the overridden method.

For interface and abstract methods, it is impossible to infer the return type from the immediate code. In these cases, it is generally advisible to explicitly annotate these methods with a @return doc comment to communicate to implementors of these methods what they are expected to return.

Loading history...
35
36
    public function Link($action = null)
37
    {
38
        if($action) {
39
            $action .= '/';
40
        }
41
        return
42
            '/'.$this->Config()->get('url_segment').
43
            '/'.strtolower($this->myBaseClass).
44
            '/'.$action;
45
    }
46
47
    public function LoadTemplateLink($className = '')
48
    {
49
        return $this->Link('loadtemplate').'?classname='.$className;
50
    }
51
52
    public function Title()
53
    {
54
        return 'Build a '.$this->myBaseClass.' - Step '.$this->step.' of 2';
55
    }
56
57
58
    public function jQueryLink()
59
    {
60
        return \Director::absoluteURL('/framework/thirdparty/jquery/jquery.js');
61
    }
62
63
    public function startover()
64
    {
65
        $this->saveData('_PrimaryForm', null);
66
        $this->saveData('_SecondaryForm', null);
67
        return $this->redirect($this->link('primaryformstart'));
68
    }
69
70
    public function loadtemplate($request)
71
    {
72
        $className = $request->getVar('classname');
73
        if(class_exists($className)) {
74
            $obj = \Injector::inst()->get($className);
75
            $primaryData = $this->turnStaticsIntoSessionData('primaryThingsToBuild', $className);
76
            $primaryData['Name'] = $className;
77
            $extends = get_parent_class($className);
78
            $primaryData['Extends'] = $extends;
79
            $primaryData['singular_name'] = $obj->i18n_singular_name();
80
            $primaryData['plural_name'] = $obj->i18n_plural_name();
81
            $this->saveData('_PrimaryForm', $primaryData);
82
83
            $secondaryData = $this->turnStaticsIntoSessionData('secondaryThingsToBuild', $className);
84
            $this->saveData('_SecondaryForm', $secondaryData);
85
86
            return $this->redirect($this->link('primaryformstart'));
87
        }
88
    }
89
90
    protected function turnStaticsIntoSessionData($method, $className) {
91
        $data = [];
92
        $thingsToBuild = $this->$method();
93
        foreach($thingsToBuild as $static) {
94
            $varName = $static['Name'];
95
            $varValue = \Config::inst()->get($className, $varName);
96
            if(is_array($varValue)) {
97
                $count = 0;
98
                foreach($varValue as $varInnerKey => $varInnerValue) {
99
                    $count++;
100
                    $data[$varName.'__KEY__'.$count] = $varInnerKey;
101
                    $data[$varName.'__VALUE__'.$count] = trim(preg_replace("/\([^)]+\)/","",$varInnerValue));
102
                }
103
            } else {
104
                $data[$varName] = $varValue;
105
            }
106
        }
107
108
        return $data;
109
    }
110
111
    /**
112
     *
113
     * @var Form
114
     */
115
    protected $step = 1;
116
117
    /**
118
     *
119
     * @var Form
120
     */
121
    protected $form = null;
122
123
    /**
124
     *
125
     * @var Form
126
     */
127
    protected $prevLink = null;
128
129
    /**
130
     *
131
     * @var ArrayList
132
     */
133
    protected $finalData = null;
134
135
    function index()
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
136
    {
137
        return $this->redirect($this->Link('primaryformstart'));
138
    }
139
140 View Code Duplication
    function primaryformstart()
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
Duplication introduced by
This method seems to be duplicated in 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...
141
    {
142
        $this->PrimaryForm();
143
        $this->prevLink = $this->Link('startover');
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->Link('startover') of type string is incompatible with the declared type object<SunnySideUp\BuildDataObject\Form> of property $prevLink.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
144
        \SSViewer::set_source_file_comments(false);
0 ignored issues
show
Deprecated Code introduced by
The method SSViewer::set_source_file_comments() has been deprecated with message: 4.0 Use the "SSViewer.source_file_comments" config setting instead

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
145
146
        return $this->renderWith('BuildControllerForm');
147
    }
148
149
    function PrimaryForm()
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
150
    {
151
        $this->form = $this->createForm('PrimaryForm', 'Build Model');
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->createForm('PrimaryForm', 'Build Model') of type object<Form> is incompatible with the declared type object<SunnySideUp\BuildDataObject\Form> of property $form.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
152
153
        return $this->form;
154
    }
155
156
    function doprimaryform($data, $form)
0 ignored issues
show
Unused Code introduced by
The parameter $form is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
157
    {
158
        $this->saveData('_PrimaryForm', $data);
159
160
        return $this->redirect($this->Link('secondaryformstart'));
161
    }
162
163
164 View Code Duplication
    function secondaryformstart()
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
Duplication introduced by
This method seems to be duplicated in 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...
165
    {
166
        $this->step = 2;
0 ignored issues
show
Documentation Bug introduced by
It seems like 2 of type integer is incompatible with the declared type object<SunnySideUp\BuildDataObject\Form> of property $step.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
167
        $this->SecondaryForm();
168
        $this->prevLink = $this->Link('primaryformstart');
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->Link('primaryformstart') of type string is incompatible with the declared type object<SunnySideUp\BuildDataObject\Form> of property $prevLink.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
169
        \SSViewer::set_source_file_comments(false);
0 ignored issues
show
Deprecated Code introduced by
The method SSViewer::set_source_file_comments() has been deprecated with message: 4.0 Use the "SSViewer.source_file_comments" config setting instead

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
170
171
        return $this->renderWith('BuildControllerForm');
172
    }
173
174
    function SecondaryForm()
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
175
    {
176
        $this->form = $this->createForm('SecondaryForm', 'Download Example Class');
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->createForm('Secon...ownload Example Class') of type object<Form> is incompatible with the declared type object<SunnySideUp\BuildDataObject\Form> of property $form.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
177
178
        return $this->form;
179
    }
180
181
182
    function dosecondaryform($data, $form)
0 ignored issues
show
Unused Code introduced by
The parameter $form is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
183
    {
184
        $this->saveData('_SecondaryForm', $data);
185
186
        return $this->redirect($this->Link('results'));
187
    }
188
189
    function results()
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
190
    {
191
        $this->finalData = $this->processedFormData($this->retrieveData());
192
        \SSViewer::set_source_file_comments(false);
0 ignored issues
show
Deprecated Code introduced by
The method SSViewer::set_source_file_comments() has been deprecated with message: 4.0 Use the "SSViewer.source_file_comments" config setting instead

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
193
194
        return \SS_HTTPRequest::send_file(
195
            $this->renderWith($this->resultsTemplateForBuilder()),
196
            $this->finalData->Name.'.php'
197
        );
198
    }
199
200
    function debug()
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
201
    {
202
        $this->finalData = $this->processedFormData($this->retrieveData());
203
        print_r($this->CanMethodBuilder('canEdit'));
0 ignored issues
show
Bug introduced by
The method CanMethodBuilder() does not exist on SunnySideUp\BuildDataObject\BuildController. Did you maybe mean MyCanMethodBuilder()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
204
        print_r($this->finalData);
205
        die('-----------------------------');
0 ignored issues
show
Coding Style Compatibility introduced by
The method debug() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
206
    }
207
208
    function Form()
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
209
    {
210
        return $this->form;
211
    }
212
213
    function FinalData()
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
214
    {
215
        return $this->finalData;
216
    }
217
218
    function PrevLink()
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
219
    {
220
        return $this->prevLink;
221
    }
222
223
    protected function ClassNameForObject()
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...
224
    {
225
        if(isset($this->finalData->Name)) {
226
            return $this->finalData->Name;
227
        } else {
228
            return 'self::$class';
229
        }
230
    }
231
232
    function MyCanMethodBuilder($type, $value) {
0 ignored issues
show
Unused Code introduced by
The parameter $type is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
233
        if($value === 'parent') {
234
            return null;
235
        } elseif($value === 'one') {
236
            $str = 'DataObject::get_one($this->class) ? false : true;';
237
        } elseif($value === 'true') {
238
            $str = 'true;';
239
        } elseif($value === 'false') {
240
            $str = 'false;';
241
        } else {
242
            $str = 'Permission::check(\''.$value.'\', \'any\', $member);';
243
        }
244
245
        return \DBField::create_field('Varchar', $str);
246
    }
247
248
249
    protected function createForm($formName, $actionTitle)
250
    {
251
        if($formName === 'PrimaryForm') {
252
            $isPrimary = true;
253
            $isSecond = false;
0 ignored issues
show
Unused Code introduced by
$isSecond 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...
254
        } elseif($formName === 'SecondaryForm') {
255
            $isPrimary = false;
256
            $isSecond = true;
0 ignored issues
show
Unused Code introduced by
$isSecond 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...
257
        } else {
258
            user_error('Set right form type: '.$formName.' is not valid');
259
        }
260
261
        $finalFields = \FieldList::create();
262
263
        if($isPrimary) {
0 ignored issues
show
Bug introduced by
The variable $isPrimary does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
264
            $toBuild = $this->primaryThingsToBuild();
265
            $possibleExtensions = $this->myAPI()->PossibleRelationsWithBaseClass($this->myBaseClass);
266
            $finalFields->push(\HeaderField::create('Based On (OPTIONAL) ...'));
267
            $possibleBasedOn = $possibleExtensions;
268
            unset($possibleBasedOn['DataObject']);
269
            $possibleBasedOn = ['' => '--- PRELOAD VALUES FROM ---'] + $possibleBasedOn;
270
            $finalFields->push(\DropdownField::create('Template', '', $possibleBasedOn));
271
            $finalFields->push(\HeaderField::create('Name your '.$this->myBaseClass));
272
            $finalFields->push(\TextField::create('Name', ''));
273
            $finalFields->push(\HeaderField::create('Extends'));
274
            asort($possibleExtensions);
275
            $finalFields->push(
276
                \DropdownField::create(
277
                    'Extends',
278
                    '',
279
                    $possibleExtensions
280
                )
281
            );
282
            $additionalFields = $this->additionalPrimaryFields();
283
            foreach($additionalFields as $additionalField) {
284
                $finalFields->push($additionalField);
285
            }
286
        } else {
287
            $toBuild = $this->secondaryThingsToBuild();
288
        }
289
        $formFields = [];
290
        $formFieldsWithMultiple = [];
291
292
        $count = 0;
293
        //build fields ...
294
        foreach($toBuild as $item) {
295
            $name = $item['Name'];
296
            $sourceMethod1 = $item['SourceMethod1'];
297
            $sourceMethod2 = $item['SourceMethod2'];
298
            $isMultiple = $item['IsMultiple'];
299
300
301
            //work out style
302
            $hasKeyAndValue = false;
303
            if($sourceMethod1 && $sourceMethod2) {
304
                $hasKeyAndValue = true;
305
            }
306
            $formFields[$count] = [];
307
            if($isMultiple) {
308
                $max = 12;
309
            } else {
310
                $max = 1;
311
            }
312
            $formFields[$count][0] = [
313
                $name.'_HEADER',
314
                'HeaderField',
315
                $name
316
            ];
317
318
319
            //work out sources
320
            if($sourceMethod1 && $this->myAPI()->hasMethod($sourceMethod1)) {
321
                $source1 = $this->myAPI()->$sourceMethod1();
322
            } else {
323
                $source1 = null;
324
            }
325
            if($sourceMethod2 && $this->myAPI()->hasMethod($sourceMethod2)) {
326
                $source2 = $this->myAPI()->$sourceMethod2();
327
            } elseif($sourceMethod2) {
328
                $source2 = null;
329
            } else {
330
                $source2 = 'ignore';
331
            }
332
333
            //work out field names
334
335
            for($i = 1; $i <= $max; $i++) {
336
                if($hasKeyAndValue) {
337
                    if($isMultiple) {
338
                        $nameKey = $name.'__KEY__'.$i;
339
                        $nameValue = $name.'__VALUE__'.$i;
340
                        $formFieldsWithMultiple[$nameKey] = $nameKey;
341
                    } else {
342
                        $nameKey = $name.'__KEY__';
343
                        $nameValue = $name.'__VALUE__';
344
                    }
345
                } else {
346
                    if($isMultiple) {
347
                        $nameKey = $name.$i;
348
                        $nameValue = '';
349
                        $formFieldsWithMultiple[$nameKey] = $nameKey;
350
                    } else {
351
                        $nameKey = $name;
352
                        $nameValue = '';
353
                    }
354
                }
355
                if($hasKeyAndValue) {
356
                    //key field
357 View Code Duplication
                    if($source1) {
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...
358
                        $formFields[$count][$i]['KEY'] = [
359
                            $nameKey,
360
                            'DropdownField',
361
                            $source1
362
                        ];
363
                    } else {
364
                        $formFields[$count][$i]['KEY'] = [
365
                            $nameKey,
366
                            'TextField'
367
                        ];
368
                    }
369
370
                    //value field
371 View Code Duplication
                    if($source2) {
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...
372
                        $formFields[$count][$i]['VALUE'] = [
373
                            $nameValue,
374
                            'DropdownField',
375
                            $source2
376
                        ];
377
                    } else {
378
                        $formFields[$count][$i]['VALUE'] = [
379
                            $nameValue,
380
                            'TextField'
381
                        ];
382
                    }
383
                } else {
384
                    //keys only!
385
                    if($source1) {
386
                        $formFields[$count][$i] = [
387
                            $nameKey,
388
                            'DropdownField',
389
                            $source1
390
                        ];
391
                    } else {
392
                        $formFields[$count][$i] = [
393
                            $nameKey,
394
                            'TextField'
395
                        ];
396
                    }
397
                }
398
            }
399
            if($i > 2) {
400
                $formFields[$count][$i + 1] = [
401
                    $name.'_ADD_'.$i,
402
                    'LiteralField',
403
                    '
404
                    <div class="CompositeField add-and-remove">
405
                        <a href="#" class="add first-add"><i class="material-icons">add_circle_outline</i></a>
406
                        <a href="#" class="remove"><i class="material-icons">remove_circle_outline</i></a>
407
                    </div>
408
                    '
409
                ];
410
            }
411
            $count++;
412
        }
413
        //create fields ...
414
        $count = 0;
415
        foreach($formFields as $outerCount => $subFieldList) {
416
            $count++;
417
            $compositeField = \CompositeField::create();
418
            $compositeField->addExtraClass('OuterComposite pos'.$count);
419
            $innerCount = 0;
420
            foreach($subFieldList as $innerCount => $fieldDetails) {
421
                $innerCount++;
422
                if(isset($fieldDetails['KEY']) && isset($fieldDetails['VALUE'])) {
423
                    $subCompositeField = \CompositeField::create();
424
                    $subCompositeField->addExtraClass('InnerComposite pos'.$innerCount);
425
                    foreach($fieldDetails as $fieldDetailsInner) {
426
                        $fieldName = $fieldDetailsInner[0];
427
                        $fieldType = $fieldDetailsInner[1];
428
                        $additionalClasses = [];
429
                        if(strpos($fieldName, '__KEY__')) {
430
                            $additionalClasses[] = 'mykey';
431
                        }
432
                        if(strpos($fieldName, '__VALUE__')) {
433
                            $additionalClasses[] = 'myvalue';
434
                        }
435
                        if(isset($fieldDetailsInner[2])) {
436
                            $source = $fieldDetailsInner[2];
437
                            asort($source);
438
                            $source = $this->prependNullOption( $source );
439
                            $tempField = $fieldType::create($fieldName, '', $source);
440
                        } else {
441
                            $tempField = $fieldType::create($fieldName, '');
442
                        }
443
                        if(count($additionalClasses)) {
444
                            $classes = implode(' ', $additionalClasses);
445
                            $tempField->addExtraClass($classes);
446
                        }
447
                        $subCompositeField->push($tempField);
448
                    }
449
                    $compositeField->push($subCompositeField);
450
                } else {
451
                    $fieldName = $fieldDetails[0];
452
                    if(isset($formFieldsWithMultiple[$fieldName])) {
453
                        $subCompositeField = \CompositeField::create();
454
                        $subCompositeField->addExtraClass('InnerComposite pos'.$innerCount);
455
                    } else {
456
                        $subCompositeField = null;
457
                    }
458
                    $fieldType = $fieldDetails[1];
459
                    if($fieldType === 'DropdownField') {
460
                        $source = $fieldDetails[2];
461
                        asort($source);
462
                        $source = $this->prependNullOption($source);
463
                        $myTempfield = $fieldType::create($fieldName, '', $source);
464
                    } elseif($fieldType === 'HeaderField') {
465
                        $title = str_replace('_', ' ', $fieldDetails[2]);
466
                        $myTempfield = $fieldType::create($fieldName, $title);
467
                    } elseif($fieldType === 'LiteralField') {
468
                        $title = $fieldDetails[2];
469
                        $myTempfield = $fieldType::create($fieldName, $title);
470
                    } else {
471
                        $myTempfield = $fieldType::create($fieldName, '');
472
                    }
473
                    if($subCompositeField) {
474
                        $subCompositeField->push($myTempfield);
475
                        $compositeField->push($subCompositeField);
476
                    } else {
477
                        $compositeField->push($myTempfield);
478
                    }
479
                }
480
            }
481
            $finalFields->push($compositeField);
482
        }
483
        $actions = \FieldList::create(
484
            [\FormAction::create('do'.strtolower($formName), $actionTitle)]
485
        );
486
487
        $form = \Form::create($this, $formName, $finalFields, $actions);
488
        $form->setFormAction($this->Link($formName));
489
        $form->loadDataFrom($this->retrieveData());
490
491
        return $form;
492
    }
493
494
    /**
495
     * returns an array of fields
496
     * @return array
497
     */
498
    protected function additionalPrimaryFields()
499
    {
500
        return [];
501
    }
502
503
    protected function saveData($name, $data)
504
    {
505
        unset($data['url']);
506
        unset($data['SecurityID']);
507
        if(is_array($data)) {
508
            foreach($data as $key => $value) {
509
                if (strpos($key, 'action_') === 0) {
510
                    unset($data[$key]);
511
                }
512
            }
513
        }
514
        $var = $this->Config()->get('form_data_session_variable');
515
        \Session::clear($var.$name);
516
        \Session::save();
517
        \Session::set($var.$name, null);
518
        \Session::save();
519
        \Session::set($var.$name, $data);
520
        \Session::save();
521
    }
522
523
    private $_data = null;
524
525
    protected function retrieveData()
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...
526
    {
527
        if(! $this->_data) {
528
            $var = $this->Config()->get('form_data_session_variable');
529
            $retrieveDataPrimary = \Session::get($var.'_PrimaryForm');
530
            if ($retrieveDataPrimary && (is_array($retrieveDataPrimary) || is_object($retrieveDataPrimary))) {
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...
531
                //do nothing
532
            } else {
533
                $retrieveDataPrimary = [];
534
            }
535
            $retrieveDataSecondary = \Session::get($var.'_SecondaryForm');
536
            if ($retrieveDataSecondary && (is_array($retrieveDataSecondary) || is_object($retrieveDataSecondary))) {
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...
537
                //do nothing
538
            } else {
539
                $retrieveDataSecondary = [];
540
            }
541
            $this->_data = $retrieveDataPrimary + $retrieveDataSecondary;
542
        }
543
544
        return $this->_data;
545
    }
546
547
    private $_processed_data = null;
548
549
    protected function processedFormData($data = null) {
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...
550
        if(! $this->_processed_data) {
551
            if(! $data) {
552
                $data = $this->retrieveData();
553
            }
554
            $array = [];
555
            foreach($data as $key => $value) {
556
                if($key && $value) {
557
                    if(
558
                        strpos($key, '__KEY__') ||
559
                        strpos($key, '__VALUE__')
560
                    ) {
561
                        $parts = explode('__', $key);
562
                        if(!isset($array[$parts[0]])) {
563
                            $array[$parts[0]] = [];
564
                        }
565
                        if(! isset($array[$parts[0]][$parts[2]])) {
566
                            $array[$parts[0]][$parts[2]] = [];
567
                        }
568
                        $array[$parts[0]][$parts[2]][$parts[1]] = $value;
569
                    } elseif(substr($key, 0, 3) === 'can') {
570
                        $array[$key] = $this->MyCanMethodBuilder($key, $value);
571
                    } else {
572
                        $array[$key] = $value;
573
                    }
574
                }
575
            }
576
            foreach($array as $field => $values) {
577
                $alInner = \ArrayList::create();
578
                if(is_array($values)) {
579
                    foreach($values as $key => $valuePairs) {
580
                        if(isset($valuePairs['KEY']) && isset($valuePairs['VALUE'])) {
581
                            if($valuePairs['VALUE'] == 'true') {
582
                                $valuePairArray = [
583
                                    'Key' => $valuePairs['KEY'],
584
                                    'UnquotedValue' => $valuePairs['VALUE'],
585
                                ];
586
                            } else {
587
                                $valuePairArray = [
588
                                    'Key' => $valuePairs['KEY'],
589
                                    'Value' => $valuePairs['VALUE'],
590
                                ];
591
                            }
592
                            $alInner->push(\ArrayData::create($valuePairArray));
593
                        }
594
                    }
595
                    $array[$field] = $alInner;
596
                } else {
0 ignored issues
show
Unused Code introduced by
This else statement is empty and can be removed.

This check looks for the else branches 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 else branches can be removed.

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

could be turned into

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

This is much more concise to read.

Loading history...
597
                    //do nothing
598
                }
599
            }
600
            $this->_processed_data = \ArrayData::create($array);
601
        }
602
603
        return $this->_processed_data;
604
    }
605
606
607
    protected function resultsTemplateForBuilder()
608
    {
609
        return str_replace(__NAMESPACE__ .'\\', '', $this->class).'Results';
610
    }
611
612
613
    protected function prependNullOption($source)
614
    {
615
        $source = ['' => '--- Please Select ---'] + $source;
616
617
        return $source;
618
    }
619
620
621
    protected function myAPI()
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...
622
    {
623
        $class = $this->apiProvider;
624
625
        return $class::inst($this->myBaseClass, $this->processedFormData());
626
    }
627
628
    protected function addKeysToThingsToBuild($array)
629
    {
630
        $newArray = [];
631
        $fields = [
632
            'Name',
633
            'SourceMethod1',
634
            'SourceMethod2',
635
            'IsMultiple'
636
        ];
637
        foreach($array as $arrayRowKey => $arrayRowValues){
638
            $newArray[$arrayRowKey] = [];
639
            foreach($arrayRowValues as $arrayColumnKey => $arrayColumnValue) {
640
                $newArray[$arrayRowKey][$fields[$arrayColumnKey]] = $arrayColumnValue;
641
            }
642
        }
643
644
        return $newArray;
645
    }
646
647
648
}
649