Completed
Push — master ( c2123c...66812a )
by
unknown
12:37
created

OrderStrategy::findExistingEntity()   C

Complexity

Conditions 11
Paths 12

Size

Total Lines 39
Code Lines 21

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 39
rs 5.2653
c 0
b 0
f 0
cc 11
eloc 21
nc 12
nop 2

How to fix   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
namespace OroCRM\Bundle\MagentoBundle\ImportExport\Strategy;
4
5
use Oro\Bundle\AddressBundle\Entity\Region;
6
7
use OroCRM\Bundle\MagentoBundle\Entity\CartStatus;
8
use OroCRM\Bundle\MagentoBundle\Entity\Customer;
9
use OroCRM\Bundle\MagentoBundle\Entity\Order;
10
use OroCRM\Bundle\MagentoBundle\Entity\OrderAddress;
11
use OroCRM\Bundle\MagentoBundle\Provider\MagentoConnectorInterface;
12
use OroCRM\Bundle\MagentoBundle\Entity\OrderItem;
13
14
class OrderStrategy extends AbstractImportStrategy
15
{
16
    const CONTEXT_ORDER_POST_PROCESS_IDS = 'postProcessOrderIds';
17
18
    /**
19
     * @var Order
20
     */
21
    protected $existingEntity;
22
23
    /**
24
     * @param Order $entity
25
     *
26
     * {@inheritdoc}
27
     */
28
    protected function beforeProcessEntity($entity)
29
    {
30
        $this->existingEntity = $this->databaseHelper->findOneByIdentity($entity);
31
        if (!$this->existingEntity) {
32
            $this->existingEntity = $entity;
33
        }
34
35
        return parent::beforeProcessEntity($entity);
36
    }
37
38
    /**
39
     * @param Order $entity
40
     *
41
     * {@inheritdoc}
42
     */
43
    protected function afterProcessEntity($entity)
44
    {
45
        if (!$entity->getUpdatedAt() && $entity->getCreatedAt()) {
46
            $entity->setUpdatedAt($entity->getCreatedAt());
47
        }
48
49
        $now = new \DateTime('now', new \DateTimeZone('UTC'));
50
        if (!$entity->getImportedAt()) {
51
            $entity->setImportedAt($now);
52
        }
53
        $entity->setSyncedAt($now);
54
55
        /** @var Order $order */
56
        $this->processCart($entity);
57
        $this->processItems($entity);
58
        $this->processAddresses($entity);
59
        $this->processCustomer($entity, $entity->getCustomer());
0 ignored issues
show
Documentation introduced by
$entity->getCustomer() is of type object<Oro\Bundle\Busine...ndle\Entity\BasePerson>, but the function expects a null|object<OroCRM\Bundl...Bundle\Entity\Customer>.

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...
60
61
        $this->existingEntity = null;
62
63
        $this->appendDataToContext(self::CONTEXT_ORDER_POST_PROCESS_IDS, $entity->getIncrementId());
64
65
        return parent::afterProcessEntity($entity);
66
    }
67
68
    /**
69
     * @param Order $order
70
     * @param Customer $customer
71
     */
72
    protected function processCustomer(Order $order, Customer $customer = null)
73
    {
74
        if (!$customer || !$customer->getId()) {
75
            $customer = $this->findExistingCustomerByContext($order);
76
        }
77
78
        if ($customer instanceof Customer) {
79
            // now customer orders subtotal calculation support only one currency.
80
            // also we do not take into account order refunds due to magento does not bring subtotal data
81
            // customer currency needs on customer's grid to format lifetime value.
82
            $customer->setCurrency($order->getCurrency());
83
        }
84
        $order->setCustomer($customer);
0 ignored issues
show
Documentation introduced by
$customer is of type object|null, but the function expects a object<Oro\Bundle\Busine...ndle\Entity\BasePerson>.

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...
85
86
        if ($order->getCart()) {
87
            $order->getCart()->setCustomer($customer);
0 ignored issues
show
Bug introduced by
It seems like $customer defined by $this->findExistingCustomerByContext($order) on line 75 can also be of type object; however, OroCRM\Bundle\MagentoBun...ity\Cart::setCustomer() does only seem to accept null|object<OroCRM\Bundl...Bundle\Entity\Customer>, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
88
        }
89
    }
90
91
    /**
92
     * Add order customer email to customer search context
93
     *
94
     * {@inheritdoc}
95
     */
96
    protected function getEntityCustomerSearchContext($order)
97
    {
98
        /** @var Order $order */
99
        $searchContext = parent::getEntityCustomerSearchContext($order);
100
        $searchContext['email'] = $order->getCustomerEmail();
101
102
        return $searchContext;
0 ignored issues
show
Best Practice introduced by
The expression return $searchContext; seems to be an array, but some of its elements' types (string) are incompatible with the return type of the parent method OroCRM\Bundle\MagentoBun...tyCustomerSearchContext of type array<string,object>.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
103
    }
104
105
    /**
106
     * If cart exists then add relation to it,
107
     * do nothing otherwise
108
     *
109
     * @param Order $entity
110
     */
111
    protected function processCart(Order $entity)
112
    {
113
        $cart = $entity->getCart();
114
115
        if ($cart) {
116
            $statusClass = MagentoConnectorInterface::CART_STATUS_TYPE;
117
            /** @var CartStatus $purchasedStatus */
118
            $purchasedStatus = $this->databaseHelper
119
                ->findOneBy($statusClass, ['name' => CartStatus::STATUS_PURCHASED]);
120
            if ($purchasedStatus) {
121
                $cart->setStatus($purchasedStatus);
122
            }
123
        }
124
125
        $entity->setCart($cart);
126
    }
127
128
    /**
129
     * @param Order $order
130
     *
131
     * @return OrderStrategy
132
     */
133
    protected function processItems(Order $order)
134
    {
135
        foreach ($order->getItems() as $item) {
136
            $item->setOwner($order->getOrganization());
137
            $item->setOrder($order);
138
        }
139
140
        return $this;
141
    }
142
143
    /**
144
     * @param Order $order
145
     *
146
     * @return OrderStrategy
147
     */
148
    protected function processAddresses(Order $order)
149
    {
150
        /** @var OrderAddress $address */
151
        foreach ($order->getAddresses() as $address) {
152
            $address->setOwner($order);
153
        }
154
155
        return $this;
156
    }
157
158
    /**
159
     * BC layer to find existing collection items by old identity filed values
160
     *
161
     * {@inheritdoc}
162
     */
163
    protected function findExistingEntity($entity, array $searchContext = [])
164
    {
165
        $existingEntity = parent::findExistingEntity($entity, $searchContext);
166
167
        if (!$existingEntity && $entity instanceof OrderAddress) {
168
            /** @var OrderAddress $existingEntity */
169
            $existingEntity = $this->existingEntity->getAddresses()
170
                ->filter(
171
                    function (OrderAddress $address) use ($entity) {
172
                        $isMatched = true;
173
                        $fieldsToMatch = ['street', 'city', 'postalCode', 'country', 'region'];
174
175
                        foreach ($fieldsToMatch as $fieldToMatch) {
176
                            $addressValue = $this->getPropertyAccessor()->getValue($address, $fieldToMatch);
177
                            $entityValue = $this->getPropertyAccessor()->getValue($entity, $fieldToMatch);
178
                            $isMatched = $isMatched && ($addressValue === $entityValue);
179
                        }
180
181
                        return $isMatched;
182
                    }
183
                )
184
                ->first();
185
186
            if ($existingEntity && $entity->getOriginId()) {
187
                $existingEntity->setOriginId($entity->getOriginId());
188
            }
189
        }
190
191
        if ($entity instanceof OrderItem && is_null($entity->getName())) {
192
            //name can't be null, so to avoid import job failing empty string is used
193
            $entity->setName('');
194
        }
195
196
        if (!$existingEntity && $entity instanceof Region) {
197
            $existingEntity = $this->findRegionEntity($entity);
198
        }
199
200
        return $existingEntity;
201
    }
202
203
    /**
204
     * Add special identifier for entities not existing in Magento
205
     * Add customer Email to search context for processing related entity Guest Customer for Order
206
     *
207
     * @param string $entityName
208
     * @param array $identityValues
209
     * @return null|object
210
     */
211
    protected function findEntityByIdentityValues($entityName, array $identityValues)
212
    {
213
        if (is_a($entityName, 'OroCRM\Bundle\MagentoBundle\Entity\Customer', true)
214
            && empty($identityValues['originId'])
215
            && $this->existingEntity
216
        ) {
217
            $identityValues['email'] = $this->existingEntity->getCustomerEmail();
218
        }
219
220
        return parent::findEntityByIdentityValues($entityName, $identityValues);
221
    }
222
223
    /**
224
     * Add special search context for entities not existing in Magento
225
     * Add customer Email to search context for Order related entity Guest Customer
226
     *
227
     * @param object $entity
228
     * @param string $entityClass
229
     * @param array $searchContext
230
     * @return array|null
231
     */
232
    protected function combineIdentityValues($entity, $entityClass, array $searchContext)
233
    {
234
        if ($entity instanceof Customer && !$entity->getOriginId() && $this->existingEntity) {
235
            $searchContext['email'] = $this->existingEntity->getCustomerEmail();
236
        }
237
238
        return parent::combineIdentityValues($entity, $entityClass, $searchContext);
239
    }
240
}
241