Issues (34)

src/Observers/CustomerObserver.php (1 issue)

Severity
1
<?php
2
3
/**
4
 * TechDivision\Import\Customer\Observers\CustomerObserver
5
 *
6
 * PHP version 7
7
 *
8
 * @author    Tim Wagner <[email protected]>
9
 * @copyright 2018 TechDivision GmbH <[email protected]>
10
 * @license   https://opensource.org/licenses/MIT
11
 * @link      https://github.com/techdivision/import-customer
12
 * @link      http://www.techdivision.com
13
 */
14
15
namespace TechDivision\Import\Customer\Observers;
16
17
use TechDivision\Import\Observers\CleanUpEmptyColumnsTrait;
18
use TechDivision\Import\Utils\RegistryKeys;
19
use TechDivision\Import\Utils\EntityTypeCodes;
20
use TechDivision\Import\Observers\StateDetectorInterface;
21
use TechDivision\Import\Customer\Utils\GenderKeys;
22
use TechDivision\Import\Customer\Utils\ColumnKeys;
23
use TechDivision\Import\Customer\Utils\MemberNames;
24
use TechDivision\Import\Customer\Services\CustomerBunchProcessorInterface;
25
26
/**
27
 * Observer that create's the customer itself.
28
 *
29
 * @author    Tim Wagner <[email protected]>
30
 * @copyright 2018 TechDivision GmbH <[email protected]>
31
 * @license   https://opensource.org/licenses/MIT
32
 * @link      https://github.com/techdivision/import-customer
33
 * @link      http://www.techdivision.com
34
 */
35
class CustomerObserver extends AbstractCustomerImportObserver
36
{
37
38
    use CleanUpEmptyColumnsTrait;
39
    /**
40
     * The customer bunch processor instance.
41
     *
42
     * @var \TechDivision\Import\Customer\Services\CustomerBunchProcessorInterface
43
     */
44
    protected $customerBunchProcessor;
45
46
    /**
47
     * The array with the available gender keys.
48
     *
49
     * @var array
50
     */
51
    protected $availableGenders = array(
52
        'Male'          => GenderKeys::GENDER_MALE,
53
        'Female'        => GenderKeys::GENDER_FEMALE,
54
        'Not Specified' => GenderKeys::GENDER_NOT_SPECIFIED
55
    );
56
57
    /**
58
     * Initializes the observer with the state detector instance.
59
     *
60
     * @param \TechDivision\Import\Customer\Services\CustomerBunchProcessorInterface $customerBunchProcessor The customer bunch processor instance
61
     * @param \TechDivision\Import\Observers\StateDetectorInterface                  $stateDetector          The state detector instance
62
     */
63
    public function __construct(
64
        CustomerBunchProcessorInterface $customerBunchProcessor,
65
        StateDetectorInterface $stateDetector = null
66
    ) {
67
68
        // set the customer processor and the raw entity loader
69
        $this->customerBunchProcessor = $customerBunchProcessor;
70
71
        // pass the state detector to the parent constructor
72
        parent::__construct($stateDetector);
73
    }
74
75
    /**
76
     * Return's the customer bunch processor instance.
77
     *
78
     * @return \TechDivision\Import\Customer\Services\CustomerBunchProcessorInterface The customer bunch processor instance
79
     */
80
    protected function getCustomerBunchProcessor()
81
    {
82
        return $this->customerBunchProcessor;
83
    }
84
85
    /**
86
     * Process the observer's business logic.
87
     *
88
     * @return void
89
     */
90
    protected function process()
91
    {
92
93
        // load email and website code
94
        $email = $this->getValue(ColumnKeys::EMAIL);
95
        $website = $this->getValue(ColumnKeys::WEBSITE);
96
97
        // query whether or not, we've found a new SKU => means we've found a new customer
98
        if ($this->hasBeenProcessed(array($email, $website))) {
99
            return;
100
        }
101
102
        // prepare the static entity values
103
        $customer = $this->initializeCustomer($this->prepareAttributes());
104
        if ($this->hasChanges($customer)) {
105
            try {
106
                // insert the entity and set the entity ID
107
                $this->setLastEntityId($this->persistCustomer($customer));
108
            } catch (\Exception $e) {
109
                if (!$this->isStrictMode()) {
110
                    $message = sprintf(
111
                        'can\'t import customer with email %s! Error: %s',
112
                        $this->getValue(ColumnKeys::EMAIL),
113
                        $e->getMessage()
114
                    );
115
                    $this->mergeStatus(
116
                        array(
117
                            RegistryKeys::NO_STRICT_VALIDATIONS => array(
118
                                basename($this->getFilename()) => array(
119
                                    $this->getLineNumber() => array(
120
                                        ColumnKeys::EMAIL => $message,
121
                                    ),
122
                                ),
123
                            ),
124
                        )
125
                    );
126
                    $this->skipRow();
127
                } else {
128
                    throw $e;
129
                }
130
            }
131
        } else {
132
            // set the entity ID
133
            $this->setLastEntityId($customer[MemberNames::ENTITY_ID]);
134
        }
135
    }
136
137
    /**
138
     * Prepare the attributes of the entity that has to be persisted.
139
     *
140
     * @return array The prepared attributes
141
     */
142
    protected function prepareAttributes()
143
    {
144
        // initialize the customer values
145
        $email = $this->getValue(ColumnKeys::EMAIL);
146
        $groupId = $this->getValue(ColumnKeys::GROUP_ID);
147
        $storeId = $this->getValue(ColumnKeys::STORE_ID);
148
        $disableAutoGroupChange = $this->getValue(ColumnKeys::DISABLE_AUTO_GROUP_CHANGE);
149
        $prefix = $this->getValue(ColumnKeys::PREFIX);
150
        $firstname = $this->getValue(ColumnKeys::FIRSTNAME);
151
        $middlename = $this->getValue(ColumnKeys::MIDDLENAME);
152
        $lastname = $this->getValue(ColumnKeys::LASTNAME);
153
        $suffix = $this->getValue(ColumnKeys::SUFFIX);
154
        $passwordHash = $this->getValue(ColumnKeys::PASSWORD_HASH);
155
        $rpToken = $this->getValue(ColumnKeys::RP_TOKEN);
156
        $defaultShipping = $this->getValue(ColumnKeys::ADDRESS_DEFAULT_SHIPPING);
157
        $defaultBilling = $this->getValue(ColumnKeys::ADDRESS_DEFAULT_BILLING);
158
        $taxvat = $this->getValue(ColumnKeys::TAXVAT);
159
        $confirmation = $this->getValue(ColumnKeys::CONFIRMATION);
160
        $gender = $this->getGenderByValue($this->getValue(ColumnKeys::GENDER));
161
162
        // load the store id if a store code is present in the current row
163
        if (($storeCode = $this->getValue(ColumnKeys::STORE))) {
164
            $storeId = $this->getStoreIdByCode($storeCode);
165
        }
166
167
        // throw exception if neither the store id nor the store code are available
168
        if ($storeId === null) {
169
            throw new \Exception(
170
                sprintf(
171
                    'Expected value for either _store or store_id, none found in file %s on line %d',
172
                    $this->getFilename(),
173
                    $this->getLineNumber()
174
                )
175
            );
176
        }
177
178
        // load the customer's additional attributes
179
        $createdIn = $this->getValue(ColumnKeys::CREATED_IN);
180
        $isActive = $this->getValue(ColumnKeys::IS_ACTIVE);
181
        $failuresNum = 0;
182
        $firstFailure = null;
183
        $lockExpires = null;
184
185
        // prepare the date format for the created at/updated at dates
186
        $websiteId = $this->getStoreWebsiteIdByCode($this->getValue(ColumnKeys::WEBSITE));
187
        $incrementId = $this->getValue(ColumnKeys::INCREMENT_ID);
188
        $dob = $this->getValue(ColumnKeys::DOB, null, array($this, 'formatDobDate'));
189
        $createdAt = $this->getValue(ColumnKeys::CREATED_AT, date('Y-m-d H:i:s'), array($this, 'formatDate'));
190
        $updatedAt = $this->getValue(ColumnKeys::UPDATED_AT, date('Y-m-d H:i:s'), array($this, 'formatDate'));
191
        $rpTokenCreatedAt = $this->getValue(ColumnKeys::RP_TOKEN_CREATED_AT, null, array($this, 'formatDate'));
192
        $sessionCutoff = $this->getValue(ColumnKeys::SESSION_CUTOFF, null, array($this, 'formatDate'));
193
194
        // return the prepared customer
195
        return $this->initializeEntity(
196
            $this->loadRawEntity(
197
                array(
198
                    MemberNames::WEBSITE_ID                => $websiteId,
199
                    MemberNames::EMAIL                     => $email,
200
                    MemberNames::GROUP_ID                  => $groupId,
201
                    MemberNames::INCREMENT_ID              => $incrementId,
202
                    MemberNames::STORE_ID                  => $storeId,
203
                    MemberNames::CREATED_AT                => $createdAt,
204
                    MemberNames::UPDATED_AT                => $updatedAt,
205
                    MemberNames::IS_ACTIVE                 => $isActive,
206
                    MemberNames::DISABLE_AUTO_GROUP_CHANGE => $disableAutoGroupChange,
207
                    MemberNames::CREATED_IN                => $createdIn,
208
                    MemberNames::PREFIX                    => $prefix,
209
                    MemberNames::FIRSTNAME                 => $firstname,
210
                    MemberNames::MIDDLENAME                => $middlename,
211
                    MemberNames::LASTNAME                  => $lastname,
212
                    MemberNames::SUFFIX                    => $suffix,
213
                    MemberNames::DOB                       => $dob,
214
                    MemberNames::PASSWORD_HASH             => $passwordHash,
215
                    MemberNames::RP_TOKEN                  => $rpToken,
216
                    MemberNames::RP_TOKEN_CREATED_AT       => $rpTokenCreatedAt,
217
                    MemberNames::DEFAULT_BILLING           => $defaultBilling,
218
                    MemberNames::DEFAULT_SHIPPING          => $defaultShipping,
219
                    MemberNames::TAXVAT                    => $taxvat,
220
                    MemberNames::CONFIRMATION              => $confirmation,
221
                    MemberNames::GENDER                    => $gender,
222
                    MemberNames::FAILURES_NUM              => $failuresNum,
223
                    MemberNames::FIRST_FAILURE             => $firstFailure,
224
                    MemberNames::LOCK_EXPIRES              => $lockExpires,
225
                    MemberNames::SESSION_CUTOFF           => $sessionCutoff
226
                )
227
            )
228
        );
229
    }
230
231
    /**
232
     * Load's and return's a raw customer entity without primary key but the mandatory members only and nulled values.
233
     *
234
     * @param array $data An array with data that will be used to initialize the raw entity with
235
     *
236
     * @return array The initialized entity
237
     */
238
    protected function loadRawEntity(array $data = array())
239
    {
240
        return $this->getCustomerBunchProcessor()->loadRawEntity(EntityTypeCodes::CUSTOMER, $data);
241
    }
242
243
    /**
244
     * Initialize the customer with the passed attributes and returns an instance.
245
     *
246
     * @param array $attr The customer attributes
247
     *
248
     * @return array The initialized customer
249
     */
250
    protected function initializeCustomer(array $attr)
251
    {
252
253
        // load the customer with the passed email and website ID and merge it with the attributes
254
        if ($entity = $this->loadCustomerByEmailAndWebsiteId($attr[MemberNames::EMAIL], $attr[MemberNames::WEBSITE_ID])) {
255
            // clear row elements that are not allowed to be updated
256
            $attr = $this->clearRowData($attr, true);
257
258
            // remove the created at date from the attributes, when we update the entity
259
            unset($attr[MemberNames::CREATED_AT]);
260
261
            return $this->mergeEntity($entity, $attr);
262
263
            // try to load the customer with the given increment ID and the website id
264
        } elseif (!empty($attr[MemberNames::INCREMENT_ID]) && $entity = $this->loadCustomerByWebsiteIdAndIncrementId($attr[MemberNames::WEBSITE_ID], $attr[MemberNames::INCREMENT_ID])) {
265
            // clear row elements that are not allowed to be updated
266
            $attr = $this->clearRowData($attr, true);
267
268
            // remove the created at date from the attributes, when we update the entity
269
            unset($attr[MemberNames::CREATED_AT]);
270
271
            return $this->mergeEntity($entity, $attr);
272
        } else {
273
            // cleanup __EMPTY__VALUE__ entries, don't remove array elements
274
            $attr = $this->clearRowData($attr, false);
275
        }
276
277
        // New Customer always active
278
        if ($attr[MemberNames::IS_ACTIVE] == null) {
279
            $attr[MemberNames::IS_ACTIVE] = 1;
280
        }
281
282
        // otherwise simply return the attributes
283
        return $attr;
284
    }
285
286
    /**
287
     * Return's the gender ID for the passed value.
288
     *
289
     * @param string $value The value to return the gender ID for
290
     *
291
     * @return integer The gender ID
292
     * @throws \Exception Is thrown, if the gender ID with the requested value is not available
293
     */
294
    protected function getGenderByValue($value)
295
    {
296
297
        // query whether or not, the requested gender ID is available
298
        if (isset($this->availableGenders[$value])) {
299
            return (integer) $this->availableGenders[$value];
300
        }
301
302
        // allow null values and empty strings
303
        if ($value === null || $value === '') {
304
            return null;
305
        }
306
307
        // if no there is a different gender in the import file than available in magento,
308
        // import the customer without gender if strict mode is disabled
309
        $message = sprintf(
310
            'Found invalid gender "%s"',
311
            $value,
312
        );
313
        if (!$this->isStrictMode()) {
314
            $this->mergeStatus(
315
                array(
316
                    RegistryKeys::NO_STRICT_VALIDATIONS => array(
317
                        basename($this->getFilename()) => array(
318
                            $this->getLineNumber() => array(
319
                                ColumnKeys::GENDER => $message,
320
                            ),
321
                        ),
322
                    ),
323
                )
324
            );
325
            return null;
326
        }
327
        // throw an exception, if not
328
        throw new \Exception(
329
            $this->appendExceptionSuffix(
330
                $message
331
            )
332
        );
333
    }
334
335
    /**
336
     * Returns the store id for the specified store code.
337
     *
338
     * @param string $code The store code
339
     *
340
     * @return integer The store id
341
     */
342
    protected function getStoreIdByCode($code)
343
    {
344
        return $this->getSubject()->getStoreId($code);
345
    }
346
347
    /**
348
     * Return's the store website for the passed code.
349
     *
350
     * @param string $code The code of the store website to return the ID for
351
     *
352
     * @return integer The store website ID
353
     */
354
    protected function getStoreWebsiteIdByCode($code)
355
    {
356
        return $this->getSubject()->getStoreWebsiteIdByCode($code);
357
    }
358
359
    /**
360
     * Return's the customer with the passed email and website ID.
361
     *
362
     * @param string $email     The email of the customer to return
363
     * @param string $websiteId The website ID of the customer to return
364
     *
365
     * @return array|null The customer
366
     */
367
    protected function loadCustomerByEmailAndWebsiteId($email, $websiteId)
368
    {
369
        return $this->getCustomerBunchProcessor()->loadCustomerByEmailAndWebsiteId($email, $websiteId);
370
    }
371
372
    /**
373
     * Return's the customer with the passed increment ID and website ID.
374
     *
375
     * @param string $websiteId   The website ID of the customer to return
376
     * @param string $incrementId The increment ID of the customer to return
377
     *
378
     * @return array|null The customer
379
     */
380
    protected function loadCustomerByWebsiteIdAndIncrementId($websiteId, $incrementId)
381
    {
382
        return $this->getCustomerBunchProcessor()->loadCustomerByWebsiteIdAndIncrementId($websiteId, $incrementId);
383
    }
384
385
    /**
386
     * Persist's the passed customer data and return's the ID.
387
     *
388
     * @param array $customer The customer data to persist
389
     *
390
     * @return string The ID of the persisted entity
391
     */
392
    protected function persistCustomer($customer)
393
    {
394
        return $this->getCustomerBunchProcessor()->persistCustomer($customer);
395
    }
396
397
    /**
398
     * Return's the attribute set of the customer that has to be created.
399
     *
400
     * @return array The attribute set
401
     */
402
    protected function getAttributeSet()
403
    {
404
        return $this->getSubject()->getAttributeSet();
405
    }
406
407
    /**
408
     * Set's the ID of the customer that has been created recently.
409
     *
410
     * @param string $lastEntityId The entity ID
411
     *
412
     * @return void
413
     */
414
    protected function setLastEntityId($lastEntityId)
415
    {
416
        $this->getSubject()->setLastEntityId($lastEntityId);
417
    }
418
419
    /**
420
     * @param string $value DOB value
421
     *
422
     * @return null|string
423
     */
424
    protected function formatDobDate($value)
425
    {
426
        // try to format the date according to the configured date format
427
        $formattedDate = $this->getSubject()->getDateConverter()->convert($value);
428
429
        $format = 'Y-m-d H:i:s';
430
        $dateTime = \DateTime::createFromFormat($format, $formattedDate);
431
432
        if (!$dateTime) {
0 ignored issues
show
$dateTime is of type DateTime, thus it always evaluated to true.
Loading history...
433
            return null;
434
        }
435
436
        // return the formatted date for birthday
437
        return $dateTime->format('Y-m-d');
438
    }
439
}
440