Completed
Push — master ( 0b205b...bde6a0 )
by
unknown
10:16
created

CartStrategy   B

Complexity

Total Complexity 42

Size/Duplication

Total Lines 281
Duplicated Lines 9.61 %

Coupling/Cohesion

Components 1
Dependencies 14

Importance

Changes 2
Bugs 0 Features 0
Metric Value
wmc 42
lcom 1
cbo 14
dl 27
loc 281
rs 8.295
c 2
b 0
f 0

11 Methods

Rating   Name   Duplication   Size   Complexity  
A beforeProcessEntity() 0 11 2
A process() 13 13 2
B afterProcessEntity() 0 27 4
C updateRemovedCartItems() 14 23 8
A updateCustomer() 0 9 3
A updateCartItems() 0 9 2
B updateAddresses() 0 24 5
A hasContactInfo() 0 16 4
A updateCartStatus() 0 14 2
B findExistingEntity() 0 26 3
C isProcessingAllowed() 0 31 7

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

1
<?php
2
3
namespace OroCRM\Bundle\MagentoBundle\ImportExport\Strategy;
4
5
use Doctrine\Common\Collections\ArrayCollection;
6
7
use Oro\Bundle\AddressBundle\Entity\Region;
8
9
use OroCRM\Bundle\MagentoBundle\Entity\Cart;
10
use OroCRM\Bundle\MagentoBundle\Entity\CartAddress;
11
use OroCRM\Bundle\MagentoBundle\Entity\CartStatus;
12
use OroCRM\Bundle\MagentoBundle\Entity\MagentoSoapTransport;
13
use OroCRM\Bundle\MagentoBundle\ImportExport\Converter\GuestCustomerDataConverter;
14
use OroCRM\Bundle\MagentoBundle\Provider\Reader\ContextCustomerReader;
15
16
class CartStrategy extends AbstractImportStrategy
17
{
18
    /**
19
     * @var Cart
20
     */
21
    protected $existingEntity;
22
23
    /**
24
     * @var array
25
     */
26
    protected $existingCartItems;
27
28
    /**
29
     * @param Cart $entity
30
     *
31
     * {@inheritdoc}
32
     */
33
    protected function beforeProcessEntity($entity)
34
    {
35
        $this->existingEntity = $this->databaseHelper->findOneByIdentity($entity);
36
        if ($this->existingEntity) {
37
            $this->existingCartItems = $this->existingEntity->getCartItems()->toArray();
38
        } else {
39
            $this->existingEntity = $entity;
40
        }
41
42
        return parent::beforeProcessEntity($entity);
43
    }
44
45
    /**
46
     * @param Cart $entity
47
     *
48
     * {@inheritdoc}
49
     */
50 View Code Duplication
    public function process($entity)
0 ignored issues
show
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...
51
    {
52
        if (!$this->isProcessingAllowed($entity)) {
53
            $this->appendDataToContext(
54
                CartWithExistingCustomerStrategy::CONTEXT_CART_POST_PROCESS,
55
                $this->context->getValue('itemData')
56
            );
57
58
            return null;
59
        }
60
61
        return parent::process($entity);
62
    }
63
64
    /**
65
     * @param Cart $cart
66
     * @return bool
67
     */
68
    protected function isProcessingAllowed(Cart $cart)
69
    {
70
        $isProcessingAllowed = true;
71
72
        if ($cart->getCustomer()) {
73
            $customer = $this->findExistingEntity($cart->getCustomer());
74
75
            $customerOriginId = $cart->getCustomer()->getOriginId();
76
            if (!$customer && $customerOriginId) {
77
                $this->appendDataToContext(ContextCustomerReader::CONTEXT_POST_PROCESS_CUSTOMERS, $customerOriginId);
78
79
                $isProcessingAllowed = false;
80
            }
81
82
            if (!$customer && $cart->getIsGuest()) {
83
                /** @var MagentoSoapTransport $transport */
84
                $channel = $this->databaseHelper->findOneByIdentity($cart->getChannel());
85
                $transport = $channel->getTransport();
86
                if ($transport->getGuestCustomerSync()) {
87
                    $this->appendDataToContext(
88
                        'postProcessGuestCustomers',
89
                        GuestCustomerDataConverter::extractCustomersValues((array)$this->context->getValue('itemData'))
90
                    );
91
92
                    $isProcessingAllowed = false;
93
                }
94
            }
95
        }
96
97
        return $isProcessingAllowed;
98
    }
99
100
    /**
101
     * @param Cart $entity
102
     *
103
     * {@inheritdoc}
104
     */
105
    protected function afterProcessEntity($entity)
106
    {
107
        if ($this->existingEntity->getStatus()->getName() === CartStatus::STATUS_OPEN) {
108
            $this->updateRemovedCartItems($entity);
109
        }
110
111
        if (!$this->hasContactInfo($entity)) {
112
            return null;
113
        }
114
115
        $this
116
            ->updateCustomer($entity)
117
            ->updateAddresses($entity)
118
            ->updateCartItems($entity)
119
            ->updateCartStatus($entity);
120
121
        $now = new \DateTime('now', new \DateTimeZone('UTC'));
122
        if (!$entity->getImportedAt()) {
123
            $entity->setImportedAt($now);
124
        }
125
        $entity->setSyncedAt($now);
126
127
        $this->existingEntity = null;
128
        $this->existingCartItems = null;
0 ignored issues
show
Documentation Bug introduced by
It seems like null of type null is incompatible with the declared type array of property $existingCartItems.

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...
129
130
        return parent::afterProcessEntity($entity);
131
    }
132
133
    /**
134
     * Update removed cart items - set `removed` field to true if cart item was removed from a cart
135
     *
136
     * @param Cart $entity
137
     */
138
    protected function updateRemovedCartItems(Cart $entity)
139
    {
140
        if ((int)$entity->getItemsQty() === 0) {
141 View Code Duplication
            foreach ($entity->getCartItems() as $cartItem) {
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...
142
                if (!$cartItem->isRemoved()) {
143
                    $cartItem->setUpdatedAt(new \DateTime('now'), new \DateTimeZone('UTC'));
144
                    $cartItem->setRemoved(true);
145
                }
146
            }
147
        } elseif ($this->existingCartItems) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->existingCartItems of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
148
            $existingCartItems = new ArrayCollection($this->existingCartItems);
149
            $newCartItems = $entity->getCartItems();
150
151 View Code Duplication
            foreach ($existingCartItems as $existingCartItem) {
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...
152
                if (!$newCartItems->contains($existingCartItem)) {
153
                    if (!$existingCartItem->isRemoved()) {
154
                        $existingCartItem->setUpdatedAt(new \DateTime('now'), new \DateTimeZone('UTC'));
155
                        $existingCartItem->setRemoved(true);
156
                    }
157
                }
158
            }
159
        }
160
    }
161
162
    /**
163
     * Update Customer email
164
     *
165
     * @param Cart $cart
166
     *
167
     * @return CartStrategy
168
     */
169
    protected function updateCustomer(Cart $cart)
170
    {
171
        $customer = $cart->getCustomer();
172
        if ($customer && !$customer->getEmail()) {
173
            $customer->setEmail($cart->getEmail());
174
        }
175
176
        return $this;
177
    }
178
179
    /**
180
     * @param Cart $cart
181
     *
182
     * @return CartStrategy
183
     */
184
    protected function updateCartItems(Cart $cart)
185
    {
186
        foreach ($cart->getCartItems() as $cartItem) {
187
            $cartItem->setOwner($cart->getOrganization());
188
            $cartItem->setCart($cart);
189
        }
190
191
        return $this;
192
    }
193
194
    /**
195
     * @param Cart $entity
196
     *
197
     * @return CartStrategy
198
     */
199
    protected function updateAddresses(Cart $entity)
200
    {
201
        $addresses = ['shippingAddress', 'billingAddress'];
202
203
        foreach ($addresses as $addressName) {
204
            /** @var CartAddress $address */
205
            $address = $this->getPropertyAccessor()->getValue($entity, $addressName);
206
207
            if (!$address) {
208
                continue;
209
            }
210
211
            // at this point imported address region have code equal to region_id in magento db field
212
            $mageRegionId = $address->getRegion() ? $address->getRegion()->getCode() : null;
213
            $this->addressHelper->updateAddressCountryRegion($address, $mageRegionId);
214
            if ($address->getCountry()) {
215
                $this->getPropertyAccessor()->setValue($entity, $addressName, $address);
216
            } else {
217
                $this->getPropertyAccessor()->setValue($entity, $addressName, null);
218
            }
219
        }
220
221
        return $this;
222
    }
223
224
    /**
225
     * @param Cart $entity
226
     * @return null
227
     */
228
    protected function hasContactInfo(Cart $entity)
229
    {
230
        $hasContactInfo = ($entity->getBillingAddress() && $entity->getBillingAddress()->getPhone())
231
            || $entity->getEmail();
232
233
        if (!$hasContactInfo) {
234
            $this->context->incrementErrorEntriesCount();
235
            $this->logger->debug(
236
                sprintf('Cart ID: %d was skipped because lack of contact info', $entity->getOriginId())
237
            );
238
239
            return false;
240
        }
241
242
        return true;
243
    }
244
245
    /**
246
     * Update cart status
247
     *
248
     * @param Cart $cart
249
     *
250
     * @return CartStrategy
251
     */
252
    protected function updateCartStatus(Cart $cart)
253
    {
254
        // allow to modify status only for "open" carts
255
        // because magento can only expire cart, so for different statuses this useless
256
        if ($this->existingEntity->getStatus()->getName() !== CartStatus::STATUS_OPEN) {
257
            $status = $this->existingEntity->getStatus();
258
        } else {
259
            $status = $cart->getStatus();
260
        }
261
262
        $cart->setStatus($status);
263
264
        return $this;
265
    }
266
267
    /**
268
     * {@inheritdoc}
269
     */
270
    protected function findExistingEntity($entity, array $searchContext = [])
271
    {
272
        $existingEntity = null;
273
274
        if ($entity instanceof Region) {
275
            /** @var \OroCRM\Bundle\MagentoBundle\Entity\Region $magentoRegion */
276
            $magentoRegion = $this->databaseHelper->findOneBy(
277
                'OroCRM\Bundle\MagentoBundle\Entity\Region',
278
                [
279
                    'regionId' => $entity->getCode()
280
                ]
281
            );
282
            if ($magentoRegion) {
283
                $existingEntity = $this->databaseHelper->findOneBy(
284
                    'Oro\Bundle\AddressBundle\Entity\Region',
285
                    [
286
                        'combinedCode' => $magentoRegion->getCombinedCode()
287
                    ]
288
                );
289
            }
290
        } else {
291
            $existingEntity = parent::findExistingEntity($entity, $searchContext);
292
        }
293
294
        return $existingEntity;
295
    }
296
}
297