Completed
Pull Request — master (#434)
by Mark
26:49
created

AddressCheckoutComponent   A

Complexity

Total Complexity 27

Size/Duplication

Total Lines 192
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 8

Test Coverage

Coverage 84.42%

Importance

Changes 4
Bugs 2 Features 0
Metric Value
wmc 27
c 4
b 2
f 0
lcom 1
cbo 8
dl 0
loc 192
ccs 65
cts 77
cp 0.8442
rs 10

10 Methods

Rating   Name   Duplication   Size   Complexity  
A getFormFields() 0 8 1
A getRequiredFields() 0 4 1
A validateData() 0 3 1
B getData() 0 36 4
D setData() 0 51 13
A setAddToAddressBook() 0 5 1
A getAddToAddressBook() 0 8 2
A useFormFieldDescriptions() 0 8 2
A setFormFieldDescriptions() 0 5 1
A getAddress() 0 4 1
1
<?php
2
3
abstract class AddressCheckoutComponent extends CheckoutComponent
4
{
5
    /** @var string - Shipping or Billing */
6
    protected $addresstype;
7
8
    /** @var bool */
9
    private static $form_field_descriptions = true;
10
11 2
    /** @var bool */
12
    protected $formfielddescriptions = true;
13 2
14
    /** @var bool - allows this to be overridden by config */
15 2
    private static $add_to_addressbook = true;
16
17 2
    /** @var bool - allows this to be overridden at runtime */
18
    protected $addtoaddressbook;
19
20 4
    public function getFormFields(Order $order)
21
    {
22 4
        return $this->getAddress($order)->getFrontEndFields(
23
            array(
24
                'addfielddescriptions' => $this->useFormFieldDescriptions(),
25 2
            )
26
        );
27 2
    }
28
29 2
    public function getRequiredFields(Order $order)
30
    {
31 2
        return $this->getAddress($order)->getRequiredFields();
32
    }
33
34 2
    public function validateData(Order $order, array $data)
35 2
    {
36 2
    }
37
38 2
    public function getData(Order $order)
39
    {
40 2
        $data = $this->getAddress($order)->toMap();
41
42
        //merge data from multiple sources
43 2
        $data = array_merge(
44 2
            ShopUserInfo::singleton()->getLocation(),
45
            $data,
46
            array(
47
                $this->addresstype . "AddressID" => $order->{$this->addresstype . "AddressID"},
48
            )
49
        );
50
51
        //merge in default address if an address isn't available
52
        $member = Member::currentUser();
53
        if(!$order->{$this->addresstype . "AddressID"}) {
54 2
            $data = array_merge(
55 2
                ShopUserInfo::singleton()->getLocation(),
56 2
                $member ? $member->{"Default" . $this->addresstype . "Address"}()->toMap() : array(),
57
                array(
58
                    $this->addresstype . "AddressID" => $order->{$this->addresstype . "AddressID"},
59 2
                )
60 2
            );
61 2
        }
62
63 2
        unset($data['ID']);
64
        unset($data['ClassName']);
65
        unset($data['RecordClassName']);
66
67
        //ensure country is restricted if there is only one allowed country
68
        if ($country = SiteConfig::current_site_config()->getSingleCountry()) {
69
            $data['Country'] = $country;
70
        }
71
72
        return $data;
73 2
    }
74
75 2
    /**
76
     * Create a new address if the existing address has changed, or is not yet
77
     * created.
78 2
     *
79 2
     * @param Order $order order to get addresses from
80 2
     * @param array $data  data to set
81 2
     *
82 2
     * @return Order
83 2
     */
84 2
    public function setData(Order $order, array $data)
0 ignored issues
show
Complexity introduced by
This operation has 600 execution paths which exceeds the configured maximum of 200.

A high number of execution paths generally suggests many nested conditional statements and make the code less readible. This can usually be fixed by splitting the method into several smaller methods.

You can also find more information in the “Code” section of your repository.

Loading history...
85
    {
86 2
        $address = $this->getAddress($order);
87 2
        //if the value matches the current address then unset
88 2
        //this is to fix issues with blank fields & the readonly Country field
89
        $addressfields = Address::database_fields(get_class($address));
90 2
        foreach($data as $key => $value) {
91 2
            if(!isset($addressfields[$key]) || (!$value && !$address->{$key})) {
92 2
                unset($data[$key]);
93 1
            }
94 1
        }
95
        $address->update($data);
96 2
        //if only one country is available, then set it
97 2
        if ($country = SiteConfig::current_site_config()->getSingleCountry()) {
98 1
            $address->Country = $country;
0 ignored issues
show
Documentation introduced by
The property Country does not exist on object<Address>. 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...
99 1
        }
100 2
        //write new address, or duplicate if changed
101
        if (!$address->isInDB()) {
102 2
            $address->write();
103 2
        } elseif ($address->isChanged()) {
104 2
            $address = $address->duplicate();
105 2
        }
106
        //set billing address, if not already set
107 2
        $order->{$this->addresstype . "AddressID"} = $address->ID;
108 2
        if (!$order->BillingAddressID) {
0 ignored issues
show
Documentation introduced by
The property BillingAddressID does not exist on object<Order>. 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...
109
            $order->BillingAddressID = $address->ID;
0 ignored issues
show
Documentation introduced by
The property BillingAddressID does not exist on object<Order>. 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...
110 2
        }
111 2
        $order->write();
112 2
        //update user info based on shipping address
113 2
        if ($this->addresstype === "Shipping") {
114 2
            ShopUserInfo::singleton()->setAddress($address);
0 ignored issues
show
Compatibility introduced by
$address of type object<DataObject> is not a sub-type of object<Address>. 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...
115
            Zone::cache_zone_ids($address);
0 ignored issues
show
Compatibility introduced by
$address of type object<DataObject> is not a sub-type of object<Address>. 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...
116
        }
117 2
        //associate member to address
118
        if ($member = Member::currentUser()) {
119 2
            $default = $member->{"Default" . $this->addresstype . "Address"}();
120 2
            //set default address
121
            if (!$default->exists()) {
122
                $member->{"Default" . $this->addresstype . "AddressID"} = $address->ID;
123
                $member->write();
124
            }
125
            if ($this->getAddToAddressBook()) {
126
                $member->AddressBook()->add($address);
127
            }
128
        }
129
130
        //extension hooks
131
        $order->extend('onSet' . $this->addresstype . 'Address', $address);
132
133 4
        return $order;
134
    }
135
136 4
    /**
137
     * Add new addresses to the address book.
138 4
     *
139
     * @param bool $add
140 4
     *
141
     * @return $this
142
     */
143
    public function setAddToAddressBook($add = true)
144
    {
145
        $this->addtoaddressbook = $add;
146
        return $this;
147
    }
148
149
    /**
150
     * @return bool
151
     */
152
    public function getAddToAddressBook()
153 1
    {
154
        if (isset($this->addtoaddressbook)) {
155
            return $this->addtoaddressbook;
156
        } else {
157
            return $this->config()->add_to_addressbook;
158
        }
159
    }
160
161
    /**
162
     * @return boolean
163
     */
164
    public function useFormFieldDescriptions()
165
    {
166
        if (isset($this->formfielddescriptions)) {
167
            return $this->formfielddescriptions;
168
        } else {
169
            return $this->config()->form_field_descriptions;
170
        }
171
    }
172
173
    /**
174
     * @param boolean $formfielddescriptions
175
     *
176
     * @return $this
177
     */
178
    public function setFormFieldDescriptions($formfielddescriptions)
179
    {
180
        $this->formfielddescriptions = $formfielddescriptions;
181
        return $this;
182
    }
183
184
185
    /**
186
     * @param Order $order
187
     *
188
     * @return Address
189
     */
190
    public function getAddress(Order $order)
191
    {
192
        return $order->{$this->addresstype . "Address"}();
193
    }
194
}
195
196
class ShippingAddressCheckoutComponent extends AddressCheckoutComponent
197
{
198
    protected $addresstype = "Shipping";
199
}
200
201
class BillingAddressCheckoutComponent extends AddressCheckoutComponent
202
{
203
    protected $addresstype = "Billing";
204
}
205