Completed
Push — master ( 613b2e...a2195f )
by Nicolaas
03:28
created

OrderAddress::getMemberFromOrder()   B

Complexity

Conditions 5
Paths 5

Size

Total Lines 12
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
dl 0
loc 12
rs 8.8571
c 0
b 0
f 0
eloc 6
nc 5
nop 0
1
<?php
2
3
4
/**
5
 * @description: each order has an address: a Shipping and a Billing address
6
 * This is a base-class for both.
7
 *
8
 *
9
 * @authors: Nicolaas [at] Sunny Side Up .co.nz
10
 * @package: ecommerce
11
 * @sub-package: address
12
 * @inspiration: Silverstripe Ltd, Jeremy
13
 **/
14
class OrderAddress extends DataObject implements EditableEcommerceObject
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
15
{
16
    /**
17
     * standard SS static definition.
18
     */
19
    private static $singular_name = 'Order Address';
0 ignored issues
show
Unused Code introduced by
The property $singular_name 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...
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
20
    public function i18n_singular_name()
21
    {
22
        return _t('OrderAddress.ORDERADDRESS', 'Order Address');
23
    }
24
25
    /**
26
     * standard SS static definition.
27
     */
28
    private static $plural_name = 'Order Addresses';
0 ignored issues
show
Unused Code introduced by
The property $plural_name 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...
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
29
    public function i18n_plural_name()
30
    {
31
        return _t('OrderAddress.ORDERADDRESSES', 'Order Addresses');
32
    }
33
34
    /**
35
     * standard SS static definition.
36
     */
37
    private static $casting = array(
0 ignored issues
show
Unused Code introduced by
The property $casting 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...
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
38
        'FullName' => 'Text',
39
        'FullString' => 'Text',
40
        'JSONData' => 'Text',
41
    );
42
43
    /**
44
     * returns the id of the MAIN country field for template manipulation.
45
     * Main means the one that is used as the primary one (e.g. for tax purposes).
46
     *
47
     * @see EcommerceConfig::get("OrderAddress", "use_shipping_address_for_main_region_and_country")
48
     *
49
     * @return string
50
     */
51
    public static function get_country_field_ID()
52
    {
53
        if (EcommerceConfig::get('OrderAddress', 'use_shipping_address_for_main_region_and_country')) {
54
            return 'ShippingCountry';
55
        } else {
56
            return 'Country';
57
        }
58
    }
59
60
    /**
61
     * returns the id of the MAIN region field for template manipulation.
62
     * Main means the one that is used as the primary one (e.g. for tax purposes).
63
     *
64
     * @return string
65
     */
66
    public static function get_region_field_ID()
67
    {
68
        if (EcommerceConfig::get('OrderAddress', 'use_shipping_address_for_main_region_and_country')) {
69
            return 'ShippingRegion';
70
        } else {
71
            return 'Region';
72
        }
73
    }
74
75
    /**
76
     * There might be times when a modifier needs to make an address field read-only.
77
     * In that case, this is done here.
78
     *
79
     * @var array
80
     */
81
    protected $readOnlyFields = array();
82
83
    /**
84
     * sets a field to readonly state
85
     * we use this when modifiers have been set that require a field to be a certain value
86
     * for example - a PostalCode field maybe set in the modifier.
87
     *
88
     * @param string $fieldName
89
     */
90
    public function addReadOnlyField($fieldName)
91
    {
92
        $this->readOnlyFields[$fieldName] = $fieldName;
93
    }
94
95
    /**
96
     * removes a field from the readonly state.
97
     *
98
     * @param string $fieldName
99
     */
100
    public function removeReadOnlyField($fieldName)
101
    {
102
        unset($this->readOnlyFields[$fieldName]);
103
    }
104
105
    /**
106
     * link to edit the record.
107
     *
108
     * @param string | Null $action - e.g. edit
109
     *
110
     * @return string
111
     */
112
    public function CMSEditLink($action = null)
113
    {
114
        return Controller::join_links(
115
            Director::baseURL(),
116
            '/admin/sales/'.$this->ClassName.'/EditForm/field/'.$this->ClassName.'/item/'.$this->ID.'/',
117
            $action
118
        );
119
    }
120
121
    /**
122
     * save edit status for speed's sake.
123
     *
124
     * @var bool
125
     */
126
    protected $_canEdit = null;
127
128
    /**
129
     * save view status for speed's sake.
130
     *
131
     * @var bool
132
     */
133
    protected $_canView = null;
134
135
    public function canCreate($member = null)
136
    {
137
        if (! $member) {
138
            $member = Member::currentUser();
139
        }
140
        $extended = $this->extendedCan(__FUNCTION__, $member);
141
        if ($extended !== null) {
142
            return $extended;
143
        }
144
        if (Permission::checkMember($member, Config::inst()->get('EcommerceRole', 'admin_permission_code'))) {
145
            return true;
146
        }
147
148
        return parent::canEdit($member);
0 ignored issues
show
Comprehensibility Bug introduced by
It seems like you call parent on a different method (canEdit() instead of canCreate()). Are you sure this is correct? If so, you might want to change this to $this->canEdit().

This check looks for a call to a parent method whose name is different than the method from which it is called.

Consider the following code:

class Daddy
{
    protected function getFirstName()
    {
        return "Eidur";
    }

    protected function getSurName()
    {
        return "Gudjohnsen";
    }
}

class Son
{
    public function getFirstName()
    {
        return parent::getSurname();
    }
}

The getFirstName() method in the Son calls the wrong method in the parent class.

Loading history...
149
    }
150
151
    /**
152
     * Standard SS method
153
     * This is an important method.
154
     *
155
     * @param Member $member
0 ignored issues
show
Documentation introduced by
Should the type for parameter $member not be Member|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
156
     *
157
     * @return bool
0 ignored issues
show
Documentation introduced by
Should the return type not be boolean|string|null?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
158
     **/
159
    public function canView($member = null)
160
    {
161
        if (! $member) {
162
            $member = Member::currentUser();
163
        }
164
        $extended = $this->extendedCan(__FUNCTION__, $member);
0 ignored issues
show
Documentation introduced by
$member is of type object<DataObject>|null, but the function expects a object<Member>|integer.

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...
165
        if ($extended !== null) {
166
            return $extended;
167
        }
168
        if (!$this->exists()) {
169
            return $this->canCreate($member);
170
        }
171
        if ($this->_canView === null) {
172
            $this->_canView = false;
173
            if ($this->Order()) {
0 ignored issues
show
Documentation Bug introduced by
The method Order does not exist on object<OrderAddress>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
174
                if ($this->Order()->exists()) {
0 ignored issues
show
Documentation Bug introduced by
The method Order does not exist on object<OrderAddress>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
175
                    if ($this->Order()->canView($member)) {
0 ignored issues
show
Documentation Bug introduced by
The method Order does not exist on object<OrderAddress>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
176
                        $this->_canView = true;
177
                    }
178
                }
179
            }
180
        }
181
182
        return $this->_canView;
183
    }
184
185
    /**
186
     * Standard SS method
187
     * This is an important method.
188
     *
189
     * @param Member $member
0 ignored issues
show
Documentation introduced by
Should the type for parameter $member not be Member|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
190
     *
191
     * @return bool
0 ignored issues
show
Documentation introduced by
Should the return type not be boolean|string|null?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
192
     **/
193
    public function canEdit($member = null)
194
    {
195
        if (! $member) {
196
            $member = Member::currentUser();
197
        }
198
        $extended = $this->extendedCan(__FUNCTION__, $member);
0 ignored issues
show
Documentation introduced by
$member is of type object<DataObject>|null, but the function expects a object<Member>|integer.

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...
199
        if ($extended !== null) {
200
            return $extended;
201
        }
202
        if (! $this->exists()) {
203
            return $this->canCreate($member);
204
        }
205
        if ($this->_canEdit === null) {
206
            $this->_canEdit = false;
207
            if ($this->Order()) {
0 ignored issues
show
Documentation Bug introduced by
The method Order does not exist on object<OrderAddress>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
208
                if ($this->Order()->exists()) {
0 ignored issues
show
Documentation Bug introduced by
The method Order does not exist on object<OrderAddress>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
209
                    if ($this->Order()->canEdit($member)) {
0 ignored issues
show
Documentation Bug introduced by
The method Order does not exist on object<OrderAddress>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
210
                        if (! $this->Order()->IsSubmitted()) {
0 ignored issues
show
Documentation Bug introduced by
The method Order does not exist on object<OrderAddress>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
211
                            $this->_canEdit = true;
212
                        }
213
                    }
214
                }
215
            }
216
        }
217
218
        return $this->_canEdit;
219
    }
220
221
    public function canDelete($member = null)
222
    {
223
        if (! $member) {
224
            $member = Member::currentUser();
225
        }
226
        $extended = $this->extendedCan(__FUNCTION__, $member);
227
        if ($extended !== null) {
228
            return $extended;
229
        }
230
231
        return false;
232
    }
233
234
    /**
235
     * Determine which properties on the DataObject are
236
     * searchable, and map them to their default {@link FormField}
237
     * representations. Used for scaffolding a searchform for {@link ModelAdmin}.
238
     *
239
     * Some additional logic is included for switching field labels, based on
240
     * how generic or specific the field type is.
241
     *
242
     * Used by {@link SearchContext}.
243
     *
244
     * @param array $_params
0 ignored issues
show
Documentation introduced by
Should the type for parameter $_params not be array|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
245
     *                       'fieldClasses': Associative array of field names as keys and FormField classes as values
246
     *                       'restrictFields': Numeric array of a field name whitelist
247
     *
248
     * @return FieldList
249
     */
250
    public function scaffoldSearchFields($_params = null)
251
    {
252
        $fields = parent::scaffoldSearchFields($_params);
0 ignored issues
show
Unused Code introduced by
$fields 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...
253
        $fields = parent::scaffoldSearchFields();
254
        $fields->replaceField('OrderID', new NumericField('OrderID', 'Order Number'));
255
256
        return $fields;
257
    }
258
259
    /**
260
     * @return FieldList
261
     */
262
    protected function getEcommerceFields()
263
    {
264
        return new FieldList();
265
    }
266
267
    /**
268
     * put together a textfield for a postal code field.
269
     *
270
     * @param string $name - name of the field
271
     *
272
     * @return TextField
273
     **/
274
    protected function getPostalCodeField($name)
275
    {
276
        $field = new TextField($name, _t('OrderAddress.POSTALCODE', 'Postal Code'));
277
        $postalCodeURL = EcommerceDBConfig::current_ecommerce_db_config()->PostalCodeURL;
0 ignored issues
show
Documentation introduced by
The property PostalCodeURL does not exist on object<EcommerceDBConfig>. 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...
278
        $postalCodeLabel = EcommerceDBConfig::current_ecommerce_db_config()->PostalCodeLabel;
0 ignored issues
show
Documentation introduced by
The property PostalCodeLabel does not exist on object<EcommerceDBConfig>. 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...
279
        if ($postalCodeURL && $postalCodeLabel) {
280
            $prefix = EcommerceConfig::get('OrderAddress', 'field_class_and_id_prefix');
281
            $field->setRightTitle('<a href="'.$postalCodeURL.'" id="'.$prefix.$name.'Link" class="'.$prefix.'postalCodeLink">'.$postalCodeLabel.'</a>');
282
        }
283
284
        return $field;
285
    }
286
287
    /**
288
     * put together a dropdown for the region field.
289
     *
290
     * @param string $name - name of the field
291
     *
292
     * @return DropdownField
293
     **/
294
    protected function getRegionField($name, $freeTextName = '')
295
    {
296
        if (EcommerceRegion::show()) {
297
            $nameWithoutID = str_replace('ID', '', $name);
298
            $title = _t('OrderAddress.'.strtoupper($nameWithoutID), 'Region / Province / State');
299
            $regionsForDropdown = EcommerceRegion::list_of_allowed_entries_for_dropdown();
300
            $count = count($regionsForDropdown);
301
            if ($count < 1) {
302
                if (!$freeTextName) {
303
                    $freeTextName = $nameWithoutID.'Code';
304
                }
305
                $regionField = new TextField($freeTextName, $title);
306
            } else {
307
                $regionField = new DropdownField($name, $title, $regionsForDropdown);
308
                if ($count < 2) {
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...
309
                    //readonly shows as number (ID), rather than title
310
                    //$regionField = $regionField->performReadonlyTransformation();
0 ignored issues
show
Unused Code Comprehensibility introduced by
60% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
311
                } else {
312
                    $regionField->setEmptyString(_t('OrderAdress.PLEASE_SELECT_REGION', '--- Select Region ---'));
313
                }
314
            }
315
        } else {
316
            //adding region field here as hidden field to make the code easier below...
317
            $regionField = new HiddenField($name, '', 0);
318
        }
319
        $prefix = EcommerceConfig::get('OrderAddress', 'field_class_and_id_prefix');
320
        $regionField->addExtraClass($prefix.'ajaxRegionField');
321
322
        return $regionField;
323
    }
324
325
    /**
326
     * put together a dropdown for the country field.
327
     *
328
     * @param string $name - name of the field
329
     *
330
     * @return DropdownField
331
     **/
332
    protected function getCountryField($name)
333
    {
334
        $countriesForDropdown = EcommerceCountry::list_of_allowed_entries_for_dropdown();
335
        $title = _t('OrderAddress.'.strtoupper($name), 'Country');
336
        $countryField = new DropdownField($name, $title, $countriesForDropdown, EcommerceCountry::get_country(false, $this->OrderID));
0 ignored issues
show
Documentation introduced by
The property OrderID does not exist on object<OrderAddress>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read 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.");
        }
    }

}

If the property has read access only, you can use the @property-read 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...
337
        $countryField->setRightTitle(_t('OrderAddress.'.strtoupper($name).'_RIGHT', ''));
338
        if (count($countriesForDropdown) < 2) {
339
            $countryField = $countryField->performReadonlyTransformation();
340
            if (count($countriesForDropdown) < 1) {
341
                $countryField = new HiddenField($name, '', 'not available');
342
            }
343
        }
344
        $prefix = EcommerceConfig::get('OrderAddress', 'field_class_and_id_prefix');
345
        $countryField->addExtraClass($prefix.'ajaxCountryField');
346
        //important, otherwise loadData will override the default value....
347
        $this->$name = EcommerceCountry::get_country(false, $this->OrderID);
0 ignored issues
show
Documentation introduced by
The property OrderID does not exist on object<OrderAddress>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read 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.");
        }
    }

}

If the property has read access only, you can use the @property-read 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...
348
349
        return $countryField;
350
    }
351
352
    /**
353
     * makes selected fields into read only using the $this->readOnlyFields array.
354
     *
355
     * @param FieldList | Composite $fields
356
     *
357
     * @return FieldList
358
     */
359
    protected function makeSelectedFieldsReadOnly($fields)
360
    {
361
        $this->extend('augmentMakeSelectedFieldsReadOnly', $fields);
362
        if (is_array($this->readOnlyFields) && count($this->readOnlyFields)) {
363
            foreach ($this->readOnlyFields as $readOnlyField) {
364
                if ($oldField = $fields->fieldByName($readOnlyField)) {
365
                    $fields->replaceField($readOnlyField, $oldField->performReadonlyTransformation());
366
                }
367
            }
368
        }
369
370
        return $fields;
371
    }
372
373
    /**
374
     * Saves region - both shipping and billing fields are saved here for convenience sake (only one actually gets saved)
375
     * NOTE: do not call this method SetCountry as this has a special meaning! *.
376
     *
377
     * @param int -  RegionID
378
     **/
379
    public function SetRegionFields($regionID)
380
    {
381
        $regionField = $this->fieldPrefix().'RegionID';
382
        $this->$regionField = $regionID;
383
        $this->write();
384
    }
385
386
    /**
387
     * Saves country - both shipping and billing fields are saved here for convenience sake (only one actually gets saved)
388
     * NOTE: do not call this method SetCountry as this has a special meaning!
389
     *
390
     * @param string - CountryCode - e.g. NZ
391
     */
392
    public function SetCountryFields($countryCode)
393
    {
394
        $countryField = $this->fieldPrefix().'Country';
395
        $this->$countryField = $countryCode;
396
        $this->write();
397
    }
398
399
    /**
400
     * Casted variable
401
     * returns the full name of the person, e.g. "John Smith".
402
     *
403
     * @return string
404
     */
405
    public function getFullName()
406
    {
407
        $fieldNameField = $this->fieldPrefix().'FirstName';
408
        $fieldFirst = $this->$fieldNameField;
409
        $lastNameField = $this->fieldPrefix().'Surname';
410
        $fieldLast = $this->$lastNameField;
411
412
        return $fieldFirst.' '.$fieldLast;
413
    }
414
    public function FullName()
415
    {
416
        return $this->getFullName();
417
    }
418
419
    /**
420
     * Casted variable
421
     * returns the full strng of the record.
422
     *
423
     * @return string
0 ignored issues
show
Documentation introduced by
Should the return type not be HTMLText?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
424
     */
425
    public function FullString()
426
    {
427
        return $this->getFullString();
428
    }
429
    public function getFullString()
430
    {
431
        Config::nest();
432
        Config::inst()->update('SSViewer', 'theme_enabled', true);
433
        $html = $this->renderWith('Order_Address'.str_replace('Address', '', $this->ClassName).'FullString');
434
        Config::unnest();
435
436
        return $html;
437
    }
438
439
    /**
440
     * returns a string that can be used to find out if two addresses are the same.
441
     *
442
     * @return string
443
     */
444
    public function comparisonString()
445
    {
446
        $comparisonString = '';
447
        $excludedFields = array('ID', 'OrderID');
448
        $fields = $this->stat('db');
449
        $regionFieldName = $this->fieldPrefix().'RegionID';
450
        $fields[$regionFieldName] = $regionFieldName;
451
        if ($fields) {
452
            foreach ($fields as $field => $useless) {
0 ignored issues
show
Bug introduced by
The expression $fields of type array|integer|double|string|boolean 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...
453
                if (!in_array($field, $excludedFields)) {
454
                    $comparisonString .= preg_replace('/\s+/', '', $this->$field);
455
                }
456
            }
457
        }
458
459
        return strtolower(trim($comparisonString));
460
    }
461
462
    /**
463
     * returns the field prefix string for shipping addresses.
464
     *
465
     * @return string
0 ignored issues
show
Documentation introduced by
Should the return type not be string|null?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
466
     **/
467
    protected function baseClassLinkingToOrder()
468
    {
469
        if (is_a($this, Object::getCustomClass('BillingAddress'))) {
470
            return 'BillingAddress';
471
        } elseif (is_a($this, Object::getCustomClass('ShippingAddress'))) {
472
            return 'ShippingAddress';
473
        }
474
    }
475
476
    /**
477
     * returns the field prefix string for shipping addresses.
478
     *
479
     * @return string
480
     **/
481
    protected function fieldPrefix()
482
    {
483
        if ($this->baseClassLinkingToOrder() == Object::getCustomClass('BillingAddress')) {
484
            return '';
485
        } else {
486
            return 'Shipping';
487
        }
488
    }
489
490
    /**
491
     *@todo: are there times when the Shipping rather than the Billing address should be linked?
492
     * Copies the last address used by the member.
493
     *
494
     * @param object (Member) $member
495
     * @param bool            $write  - should the address be written
496
     *
497
     * @return OrderAddress | ShippingAddress | BillingAddress
498
     **/
499
    public function FillWithLastAddressFromMember(Member $member, $write = false)
500
    {
501
        $excludedFields = array('ID', 'OrderID');
502
        $fieldPrefix = $this->fieldPrefix();
503
        if ($member && $member->exists()) {
504
            $oldAddress = $member->previousOrderAddress($this->baseClassLinkingToOrder(), $this->ID);
505
            if ($oldAddress) {
506
                $fieldNameArray = array_keys($this->Config()->get('db')) + array_keys($this->Config()->get('has_one'));
507
                foreach ($fieldNameArray as $field) {
508
                    if (in_array($field, $excludedFields)) {
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...
509
                        //do nothing
510
                    } elseif ($this->$field) {
0 ignored issues
show
Unused Code introduced by
This elseif statement is empty, and could be removed.

This check looks for the bodies of elseif 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 elseif bodies can be removed. If you have an empty elseif but statements in the else branch, consider inverting the condition.

Loading history...
511
                        //do nothing
512
                    } elseif (isset($oldAddress->$field)) {
513
                        $this->$field = $oldAddress->$field;
514
                    }
515
                }
516
            }
517
            //copy data from  member
518
            if (is_a($this, Object::getCustomClass('BillingAddress'))) {
519
                $this->Email = $member->Email;
0 ignored issues
show
Documentation introduced by
The property Email does not exist on object<OrderAddress>. 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...
520
            }
521
            $fieldNameArray = array('FirstName' => $fieldPrefix.'FirstName', 'Surname' => $fieldPrefix.'Surname');
522
            foreach ($fieldNameArray as $memberField => $fieldName) {
523
                //NOTE, we always override the Billing Address (which does not have a fieldPrefix)
524
                if (!$this->$fieldName || (is_a($this, Object::getCustomClass('BillingAddress')))) {
525
                    $this->$fieldName = $member->$memberField;
526
                }
527
            }
528
        }
529
        if ($write) {
530
            $this->write();
531
        }
532
533
        return $this;
534
    }
535
536
    /**
537
     * find the member associated with the current Order and address.
538
     *
539
     * @Note: this needs to be public to give DODS (extensions access to this)
540
     * @todo: can wre write $this->Order() instead????
541
     *
542
     * @return DataObject (Member) | Null
0 ignored issues
show
Documentation introduced by
Should the return type not be DataObject|null?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
543
     **/
544
    public function getMemberFromOrder()
545
    {
546
        if ($this->exists()) {
547
            if ($order = $this->Order()) {
0 ignored issues
show
Documentation Bug introduced by
The method Order does not exist on object<OrderAddress>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
548
                if ($order->exists()) {
549
                    if ($order->MemberID) {
550
                        return Member::get()->byID($order->MemberID);
551
                    }
552
                }
553
            }
554
        }
555
    }
556
557
    /**
558
     * make an address obsolete and include all the addresses that are identical.
559
     *
560
     * @param Member $member
0 ignored issues
show
Documentation introduced by
Should the type for parameter $member not be null|Member?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
561
     */
562
    public function MakeObsolete(Member $member = null)
563
    {
564
        $addresses = $member->previousOrderAddresses($this->baseClassLinkingToOrder(), $this->ID, $onlyLastRecord = false, $keepDoubles = true);
565
        $comparisonString = $this->comparisonString();
566
        if ($addresses->count()) {
567
            foreach ($addresses as $address) {
568
                if ($address->comparisonString() == $comparisonString) {
569
                    $address->Obsolete = 1;
570
                    $address->write();
571
                }
572
            }
573
        }
574
        $this->Obsolete = 1;
0 ignored issues
show
Documentation introduced by
The property Obsolete does not exist on object<OrderAddress>. 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...
575
        $this->write();
576
    }
577
578
    /**
579
     * standard SS method
580
     * We "hackishly" ensure that the OrderID is set to the right value.
581
     */
582
    public function onAfterWrite()
583
    {
584
        parent::onAfterWrite();
585
        if ($this->exists()) {
586
            $order = Order::get()
587
                ->filter(array($this->ClassName.'ID' => $this->ID))
588
                ->First();
589
            if ($order && $order->ID != $this->OrderID) {
0 ignored issues
show
Documentation introduced by
The property OrderID does not exist on object<OrderAddress>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read 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.");
        }
    }

}

If the property has read access only, you can use the @property-read 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...
590
                $this->OrderID = $order->ID;
0 ignored issues
show
Documentation introduced by
The property OrderID does not exist on object<OrderAddress>. 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...
591
                $this->write();
592
            }
593
        }
594
    }
595
596
    /**
597
     * returns the link that can be used to remove (make Obsolete) an address.
598
     *
599
     * @return string
600
     */
601
    public function RemoveLink()
602
    {
603
        return ShoppingCart_Controller::remove_address_link($this->ID, $this->ClassName);
604
    }
605
606
    /**
607
     * converts an address into JSON.
608
     *
609
     * @return string (JSON)
610
     */
611
    public function getJSONData()
612
    {
613
        return $this->JSONData();
614
    }
615
    public function JSONData()
616
    {
617
        $jsArray = array();
618
        if (!isset($fields)) {
0 ignored issues
show
Bug introduced by
The variable $fields seems only to be defined at a later point. As such the call to isset() seems to always evaluate to false.

This check marks calls to isset(...) or empty(...) that are found before the variable itself is defined. These will always have the same result.

This is likely the result of code being shifted around. Consider removing these calls.

Loading history...
619
            $fields = $this->stat('db');
620
            $regionFieldName = $this->fieldPrefix().'RegionID';
621
            $fields[$regionFieldName] = $regionFieldName;
622
        }
623
        if ($fields) {
624
            foreach ($fields as $name => $field) {
0 ignored issues
show
Bug introduced by
The expression $fields of type array|integer|double|string|boolean 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...
625
                $jsArray[$name] = $this->$name;
626
            }
627
        }
628
629
        return Convert::array2json($jsArray);
630
    }
631
632
    /**
633
     * returns the instance of EcommerceDBConfig.
634
     *
635
     * @return EcommerceDBConfig
636
     **/
637
    public function EcomConfig()
638
    {
639
        return EcommerceDBConfig::current_ecommerce_db_config();
640
    }
641
642
    /**
643
     * standard SS Method
644
     * saves the region code.
645
     */
646
    public function onBeforeWrite()
647
    {
648
        parent::onBeforeWrite();
649
        $fieldPrefix = $this->fieldPrefix();
650
        $idField = $fieldPrefix.'RegionID';
651
        if ($this->$idField) {
652
            $region = EcommerceRegion::get()->byID($this->$idField);
653
            if ($region) {
654
                $codeField = $fieldPrefix.'RegionCode';
655
                $this->$codeField = $region->Code;
656
            }
657
        }
658
    }
659
660
    public function debug()
661
    {
662
        return EcommerceTaskDebugCart::debug_object($this);
663
    }
664
}
665