Completed
Push — master ( d5645f...b278b8 )
by
unknown
01:20
created

RepeatOrderForm::doSave()   F

Complexity

Conditions 23
Paths 651

Size

Total Lines 109

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 109
rs 0.3877
c 0
b 0
f 0
cc 23
nc 651
nop 3

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
4
class RepeatOrderForm extends Form
5
{
6
7
    private static $number_of_product_alternatives = 0;
8
9
    public function __construct($controller, $name, $repeatOrderID = 0, $originatingOrder = 0)
10
    {
11
12
        if ($repeatOrderID) {
13
            $repeatOrder = DataObject::get_one('RepeatOrder', "ID = ".$repeatOrderID);
14
            $items = $repeatOrder->OrderItems();
0 ignored issues
show
Unused Code introduced by
$items 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...
15
        } else {
16
            $repeatOrder = null;
17
        }
18
19
        $fields = RepeatOrderForm::repeatOrderFormFields($repeatOrderID, $originatingOrder);
20
21
        $actions = RepeatOrderForm::repeatOrderFormActions($repeatOrder);
0 ignored issues
show
Documentation introduced by
$repeatOrder is of type object<DataObject>|null, 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...
22
23
        //required fields
24
        $requiredArray = array('Start', 'Period');
25
        $requiredFields = RequiredFields::create($requiredArray);
26
27
        //make form
28
        parent::__construct($controller, $name, $fields, $actions, $requiredFields);
29
        //load data
30
        if ($repeatOrder) {
31
            $this->loadDataFrom(
32
                [
33
                    'Start' => $repeatOrder->Start,
34
                    'End' => $repeatOrder->End,
35
                    'Period' => $repeatOrder->Period,
36
                    'Notes' => $repeatOrder->Notes,
37
                    'PaymentMethod' => $repeatOrder->PaymentMethod,
38
                ]
39
            );
40
        }
41
    }
42
43
    public static function repeatOrderFormFields($repeatOrderID = 0, $originatingOrder = 0)
44
    {
45
        $order = null;
46
        //create vs edit
47
        if ($repeatOrderID) {
48
            $repeatOrder = DataObject::get_one('RepeatOrder', "ID = ".$repeatOrderID);
49
            $items = $repeatOrder->OrderItems();
50
        } else {
51
            $repeatOrder = null;
52
            if ($originatingOrder) {
53
                $order = Order::get_by_id_if_can_view($originatingOrder);
54
            }
55
            if (!$order) {
56
                $order = ShoppingCart::current_order();
57
            }
58
            if ($order) {
59
                $items = $order->Items();
0 ignored issues
show
Bug introduced by
The method Items does only exist in Order, but not in DataObject.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
60
            } else {
61
                $items = null;
62
            }
63
        }
64
65
        //build fields
66
        $fields = FieldList::create();
67
68
        //products!
69
        if ($items) {
70
            // $fields->push(HeaderField::create('ProductsHeader', 'Products'));
71
            $products = Product::get()->filter(["AllowPurchase" => 1]);
72
            $productsMap = $products->map('ID', 'Title')->toArray();
73
            $arr1 = [0 => "--- Please select ---"] + $productsMap;
0 ignored issues
show
Unused Code introduced by
$arr1 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...
74
            foreach ($productsMap as $id => $title) {
75
                if ($product = Product::get()->byID($id)) {
76
                    if (!$product->canPurchase()) {
77
                        unset($productsMap[$id]);
78
                    }
79
                }
80
            }
81
            $j = 0;
82
            foreach ($items as $key => $item) {
83
                $j++;
84
                $alternativeItemsMap = $productsMap;
85
                $defaultProductID =  $item->ProductID ? $item->ProductID : $item->BuyableID;
86
                $itemID = $defaultProductID;
87
                unset($alternativeItemsMap[$defaultProductID]);
88
                $fields->push(
89
                    HiddenField::create(
90
                        'Product[ID]['.$itemID.']',
91
                        "Preferred Product #$j",
92
                        $defaultProductID
93
                    )
94
                );
95
                $fields->push(
96
                    HiddenField::create(
97
                        'Product[Quantity]['.$itemID.']',
98
                        " ... quantity",
99
                        $item->Quantity
100
                    )
101
                );
102
                $altCount = Config::inst()->get('RepeatOrderForm', 'number_of_product_alternatives');
103
                if($altCount > 9) {
104
                    user_error('You can only have up to nine alternatives buyables');
105
                } elseif($altCount > 0) {
106
                    for ($i = 1; $i <= $altCount; $i++) {
107
                        $alternativeField = "Alternative".$i."ID";
108
                        $fields->push(
109
                            DropdownField::create(
110
                                'Product['.$alternativeField.']['.$itemID.']',
111
                                " ... alternative $i",
112
                                $alternativeItemsMap,
113
                                (isset($item->$alternativeField) ? $item->$alternativeField : 0)
114
                            )
115
                        );
116
                    }
117
                }
118
            }
119
        } else {
120
            $fields->push(HeaderField::create('items', 'There are no products in this repeating order'));
121
        }
122
123
        //other details
124
        $fields->push(
125
            HeaderField::create(
126
                'DetailsHeader',
127
                'Repeat Order Details'
128
            )
129
        );
130
        $fields->push(
131
            DropdownField::create(
132
                'PaymentMethod',
133
                'Payment Method',
134
                Config::inst()->get('RepeatOrder', 'payment_methods')
135
            )
136
        );
137
        $startField = DateField::create(
138
            'Start',
139
            'Start Date',
140
            date('d-m-Y')
141
        );
142
        $startField->setAttribute('autocomplete', 'off');
143
        $startField->setConfig('showcalendar', true);
144
        $fields->push($startField);
145
146
        $endField = DateField::create('End', 'End Date');
147
        $endField->setAttribute('autocomplete', 'off');
148
        $endField->setConfig('showcalendar', true);
149
        $fields->push($endField);
150
151
        $fields->push(
152
            DropdownField::create(
153
                'Period',
154
                'Period',
155
                Config::inst()->get('RepeatOrder', 'period_fields')
156
            )
157
        );
158
        $fields->push(
159
            TextareaField::create('Notes', 'Notes')
160
        );
161
162
        //hidden field
163
        if (isset($order->ID)) {
164
            $fields->push(
165
                HiddenField::create('OrderID', 'OrderID', $order->ID)
166
            );
167
        }
168
        if ($repeatOrder) {
169
            $fields->push(
170
                HiddenField::create('RepeatOrderID', 'RepeatOrderID', $repeatOrder->ID)
171
            );
172
        }
173
        return $fields;
174
    }
175
176
    public static function repeatOrderFormActions($label = '', $repeatOrder = null)
177
    {
178
        //actions
179
        $actions = FieldList::create();
180
        if ($repeatOrder) {
181
            if(!$label){
182
                $label = 'Save';
183
            }
184
            $actions->push(FormAction::create('doSave', $label));
185
        } else {
186
            if(!$label){
187
                $label = 'Create';
188
            }
189
            $actions->push(FormAction::create('doCreate', $label));
190
        }
191
        return $actions;
192
    }
193
194
    /**
195
     * same as save!
196
     * @param  [type] $data    [description]
0 ignored issues
show
Documentation introduced by
The doc-type [type] could not be parsed: Unknown type name "" at position 0. [(view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
197
     * @param  [type] $form    [description]
0 ignored issues
show
Documentation introduced by
The doc-type [type] could not be parsed: Unknown type name "" at position 0. [(view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
198
     * @param  [type] $request [description]
0 ignored issues
show
Documentation introduced by
The doc-type [type] could not be parsed: Unknown type name "" at position 0. [(view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
199
     * @return [type]          [description]
0 ignored issues
show
Documentation introduced by
The doc-type [type] could not be parsed: Unknown type name "" at position 0. [(view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
200
     */
201
    public function doCreate($data, $form, $request)
202
    {
203
        return $this->doSave($data, $form, $request);
204
    }
205
206
    /**
207
     * Save the changes
208
     */
209
    public function doSave($data, $form, $request)
210
    {
211
        $data = Convert::raw2sql($data);
212
        $member = Member::currentUser();
213
        $allowNonMembers = Config::inst()->get('RepeatOrder', 'allow_non_members');
214
        if (!$member && !$allowNonMembers) {
215
            $form->sessionMessage('Could not find customer details.', 'bad');
216
            $this->controller->redirectBack();
217
218
            return false;
219
        }
220
        if ($member && $member->IsShopAdmin()) {
221
            $form->sessionMessage('Repeat orders can not be created by Shop Administrators.  Only customers can create repeat orders.', 'bad');
222
            $this->controller->redirectBack();
223
224
            return false;
225
        }
226
        if (isset($data['OrderID'])) {
227
228
            $orderID = intval($data['OrderID']);
229
            if($orderID) {
230
231
                if ($member) {
232
                    $order = DataObject::get_one('Order', 'Order.ID = \''.$orderID.'\' AND MemberID = \''.$member->ID.'\'');
233
                }
234
                else {
235
                    $order = DataObject::get_one('Order', 'Order.ID = \''.$orderID.'\'');
236
                }
237
238
                if ($order) {
239
                    $repeatOrder = RepeatOrder::create_repeat_order_from_order($order);
0 ignored issues
show
Compatibility introduced by
$order of type object<DataObject> is not a sub-type of object<Order>. It seems like you assume a child class of the class DataObject to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
240
                    if($repeatOrder) {
241
                        $repeatOrder->OriginatingOrderID = $order->ID;
0 ignored issues
show
Documentation introduced by
The property OriginatingOrderID does not exist on object<RepeatOrder>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
242
                        $repeatOrder->write();
243
                    } else {
244
                        $form->sessionMessage('Sorry, an error occured - we could not create your subscribtion order. Please try again.', 'bad');
245
                        $this->controller->redirectBack();
246
247
                        return false;
248
                    }
249
                } else {
250
                    $form->sessionMessage('Could not find originating order.', 'bad');
251
                    $this->controller->redirectBack();
252
253
                    return false;
254
                }
255
            }
256
        } else {
257
            $repeatOrderID = intval($data['RepeatOrderID']);
258
            if ($member) {
259
                $repeatOrder = DataObject::get_one('RepeatOrder', 'RepeatOrder.ID = \''.$repeatOrderID.'\' AND MemberID = \''.$member->ID.'\'');
260
            }
261
            else {
262
                $repeatOrder = DataObject::get_one('RepeatOrder', 'RepeatOrder.ID = \''.$repeatOrderID.'\'');
263
            }
264
265
        }
266
        if ($repeatOrder) {
267
            if ($repeatOrderItems = $repeatOrder->OrderItems()) {
0 ignored issues
show
Bug introduced by
The variable $repeatOrder 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...
268
                foreach ($repeatOrderItems as $repeatOrderItem) {
269
                    $repeatOrderItem->ProductID = $data["Product"]["ID"][$repeatOrderItem->ProductID];
270
                    $repeatOrderItem->Quantity = $data["Product"]["Quantity"][$repeatOrderItem->ProductID];
271
                    $altCount = Config::inst()->get('RepeatOrderForm', 'number_of_product_alternatives');
272
                    for ($i = 1; $i <= $altCount; $i++) {
273
                        $alternativeField = "Alternative".$i."ID";
274
                        $repeatOrderItem->$alternativeField = $data["Product"][$alternativeField][$repeatOrderItem->ProductID];
275
                    }
276
                    $repeatOrderItem->write();
277
                }
278
            }
279
            $params = [];
280
            if (isset($data['Start']) && strtotime($data['Start']) > strtotime(Date("Y-m-d"))) {
281
                $params['Start'] = $data['Start'];
282
            } else {
283
                $params["Start"] = Date("Y-m-d");
284
            }
285
            if (isset($data['End'])  && strtotime($data['End']) > strtotime($params["Start"])) {
286
                $params['End'] = $data['End'];
287
            }
288
            if (isset($data['Period'])) {
289
                $params['Period'] = $data['Period'];
290
            } else {
291
                $data['Period'] = RepeatOrder::default_period_key();
292
            }
293
            if (isset($data['PaymentMethod'])) {
294
                $params['PaymentMethod'] = $data['PaymentMethod'];
295
            } else {
296
                $data['PaymentMethod'] = RepeatOrder::default_payment_method_key();
297
            }
298
            if (isset($data['Notes'])) {
299
                $params['Notes'] = $data['Notes'];
300
            }
301
            $repeatOrder->update($params);
302
            $repeatOrder->Status = 'Pending';
303
            $repeatOrder->write();
304
        } else {
305
            $form->sessionMessage('Could not find repeat order.', 'bad');
306
            $this->controller->redirectBack();
307
308
            return false;
309
        }
310
311
        if(!$request->isAjax()){
312
            $this->controller->redirect(
313
                RepeatOrdersPage::get_repeat_order_link('view', $repeatOrder->ID)
314
            );
315
        }
316
        return true;
317
    }
318
319
    /**
320
     * add item to the beginning of array
321
     * @param  array $arr [description]
322
     * @param  mixed $key [description]
323
     * @param  mixed $val [description]
324
325
     * @return array
326
     */
327
    private function array_unshift_assoc(&$arr, $key, $val)
328
    {
329
        $arr = array_reverse($arr, true);
330
        $arr[$key] = $val;
331
        $arr = array_reverse($arr, true);
332
333
        return $arr;
334
    }
335
}
336