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
introduced
by
![]() |
|||
433 | return null; |
||
434 | } |
||
435 | |||
436 | // return the formatted date for birthday |
||
437 | return $dateTime->format('Y-m-d'); |
||
438 | } |
||
439 | } |
||
440 |