Completed
Push — 3.2.1 ( a048e8...16e574 )
by
unknown
02:24 queued 20s
created

ZohoDatabasePusher::sendDataToZohoCleanLocal()   B

Complexity

Conditions 7
Paths 10

Size

Total Lines 51

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 51
rs 8.1357
c 0
b 0
f 0
cc 7
nc 10
nop 4

How to fix   Long Method   

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 Wabel\Zoho\CRM\Copy;
4
5
use Doctrine\DBAL\Connection;
6
use Psr\Log\LoggerInterface;
7
use Psr\Log\NullLogger;
8
use Wabel\Zoho\CRM\AbstractZohoDao;
9
use Wabel\Zoho\CRM\Service\EntitiesGeneratorService;
10
use Wabel\Zoho\CRM\ZohoBeanInterface;
11
use zcrmsdk\crm\api\response\EntityResponse;
12
13
/**
14
 * Description of ZohoDatabasePusher.
15
 *
16
 * @author rbergina
17
 */
18
class ZohoDatabasePusher
19
{
20
    /**
21
     * @var Connection
22
     */
23
    private $connection;
24
25
    /**
26
     * @var LoggerInterface
27
     */
28
    private $logger;
29
30
    /**
31
     * @var string
32
     */
33
    private $prefix;
34
35
    /**
36
     * @param Connection $connection
37
     * @param int $apiLimitInsertUpdateDelete
38
     * @param string $prefix
39
     * @param LoggerInterface $logger
40
     */
41
    public function __construct(Connection $connection, $apiLimitInsertUpdateDelete = 100, $prefix = 'zoho_', LoggerInterface $logger = null)
42
    {
43
        $this->connection = $connection;
44
        $this->prefix = $prefix;
45
        if ($logger === null) {
46
            $this->logger = new NullLogger();
47
        } else {
48
            $this->logger = $logger;
49
        }
50
        $this->apiLimitInsertUpdateDelete = $apiLimitInsertUpdateDelete;
51
        if ($apiLimitInsertUpdateDelete === null) {
52
            $this->apiLimitInsertUpdateDelete = 100;
53
        }
54
    }
55
56
    /**
57
     * @var int
58
     */
59
    private $apiLimitInsertUpdateDelete;
60
61
    /**
62
     * @param AbstractZohoDao $zohoDao
63
     * @param bool $update
64
     * @return int
65
     */
66
    private function countElementInTable(AbstractZohoDao $zohoDao, $update = false)
67
    {
68
        $tableName = ZohoDatabaseHelper::getTableName($zohoDao, $this->prefix);
69
        if ($update) {
70
            return (int)$this->connection->executeQuery('SELECT COUNT(DISTINCT uid) AS nb FROM `local_update` WHERE table_name LIKE :tableName AND error IS NULL', ['tableName' => $tableName])->fetchColumn();
71
        }
72
        return (int)$this->connection->executeQuery('SELECT COUNT(uid) AS nb FROM `local_insert` WHERE table_name LIKE :tableName AND error IS NULL', ['tableName' => $tableName])->fetchColumn();
73
    }
74
75
    /**
76
     * Insert or Update rows.
77
     *
78
     * @param AbstractZohoDao $zohoDao
79
     */
80
    public function pushDataToZoho(AbstractZohoDao $zohoDao, $update = false)
81
    {
82
        $localTable = $update ? 'local_update' : 'local_insert';
83
        $tableName = ZohoDatabaseHelper::getTableName($zohoDao, $this->prefix);
84
        $countToPush = $this->countElementInTable($zohoDao, $update);
85
        $this->logger->notice($countToPush . ' records to ' . ($update ? 'update' : 'insert') . ' into Zoho for module ' . $zohoDao->getPluralModuleName());
86
        if ($countToPush) {
87
            do {
88
                $rowsDeleted = [];
89
                $zohoBeans = [];
90
                //@see https://www.zoho.com/crm/help/api/v2/#ra-update-records
91
                //To optimize your API usage, get maximum 200 records with each request and insert, update or delete maximum 100 records with each request.
92
93
                if ($update) {
94
                    $recordsToUpdateQuery = $this->connection->createQueryBuilder();
95
                    $recordsToUpdateQuery
96
                        ->select('DISTINCT table_name, uid')
97
                        ->from($localTable)
98
                        ->where('error IS NULL')
99
                        ->andWhere('table_name = :table_name')
100
                        ->setMaxResults($this->apiLimitInsertUpdateDelete)
101
                        ->setParameters([
102
                            'table_name' => $tableName
103
                        ]);
104
                    $recordsToUpdateResults = $recordsToUpdateQuery->execute()->fetchAll();
105
                    $this->logger->info(sprintf('Processing %s records to update...', count($recordsToUpdateResults)));
106
                    foreach ($recordsToUpdateResults as $result) {
107
                        $recordQuery = $this->connection->createQueryBuilder();
108
                        $recordQuery
109
                            ->select('*')
110
                            ->from($tableName)
111
                            ->where('uid = :uid')
112
                            ->setParameters([
113
                                'uid' => $result['uid']
114
                            ]);
115
                        $record = $recordQuery->execute()->fetch();
116
117 View Code Duplication
                        if (!$record) {
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...
118
                            $errorMessage = sprintf('Impossible to find row with uid %s in the table %s', $result['uid'], $tableName);
119
                            $this->logger->warning($errorMessage);
120
                            $this->connection->update($localTable, [
121
                                'error' => $errorMessage,
122
                                'errorTime' => date('Y-m-d H:i:s'),
123
                            ], [
124
                                'uid' => $result['uid'],
125
                                'table_name' => $tableName
126
                            ]);
127
                            continue;
128
                        }
129
130 View Code Duplication
                        if (isset($zohoBeans[$record['uid']])) {
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...
131
                            $zohoBean = $zohoBeans[$record['uid']];
132
                        } else {
133
                            $zohoBean = $zohoDao->create();
134
                        }
135
136
                        if ($record['id'] && !$zohoBean->getZohoId()) {
137
                            $zohoBean->setZohoId($record['id']);
138
                        }
139
140
                        $fieldsUpdatedQuery = $this->connection->createQueryBuilder();
141
                        $fieldsUpdatedQuery
142
                            ->select('field_name')
143
                            ->from($localTable)
144
                            ->where('uid = :uid')
145
                            ->andWhere('table_name = :table_name')
146
                            ->andWhere('error IS NULL')
147
                            ->setParameters([
148
                                'uid' => $result['uid'],
149
                                'table_name' => $tableName,
150
                            ]);
151
                        $fieldsUpdatedResults = $fieldsUpdatedQuery->execute()->fetchAll();
152
153
                        foreach ($fieldsUpdatedResults as $fieldResults) {
154
                            $columnName = $fieldResults['field_name'];
155
                            if (array_key_exists($columnName, $record)) {
156
                                $this->updateDataZohoBean($zohoDao, $zohoBean, $columnName, $record[$columnName]);
157 View Code Duplication
                            } else {
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...
158
                                $errorMessage = sprintf('Impossible to find the column %s for row with uid %s in the table %s', $columnName, $result['uid'], $tableName);
159
                                $this->logger->warning($errorMessage);
160
                                $this->connection->update($localTable, [
161
                                    'error' => $errorMessage,
162
                                    'errorTime' => date('Y-m-d H:i:s'),
163
                                ], [
164
                                    'uid' => $result['uid'],
165
                                    'table_name' => $tableName,
166
                                    'field_name' => $columnName
167
                                ]);
168
                                continue;
169
                            }
170
                        }
171
172
                        $this->logger->debug(sprintf('Updated row %s (id: \'%s\') from table %s added in queue to be pushed.', $record['uid'], $record['id'], $tableName));
173
                        $zohoBeans[$record['uid']] = $zohoBean;
174
                        $rowsDeleted[] = $record['uid'];
175
                    }
176
                } else {
177
                    $recordsToInsertQuery = $this->connection->createQueryBuilder();
178
                    $recordsToInsertQuery
179
                        ->select('DISTINCT table_name, uid')
180
                        ->from($localTable)
181
                        ->where('error IS NULL')
182
                        ->andWhere('table_name = :table_name')
183
                        ->setMaxResults($this->apiLimitInsertUpdateDelete)
184
                        ->setParameters([
185
                            'table_name' => $tableName
186
                        ]);
187
                    $recordsToInsertResults = $recordsToInsertQuery->execute()->fetchAll();
188
                    $this->logger->info(sprintf('Processing %s records to insert...', count($recordsToInsertResults)));
189
                    foreach ($recordsToInsertResults as $result) {
190
                        $recordQuery = $this->connection->createQueryBuilder();
191
                        $recordQuery
192
                            ->select('*')
193
                            ->from($tableName)
194
                            ->where('uid = :uid')
195
                            ->setParameters([
196
                                'uid' => $result['uid']
197
                            ]);
198
                        $record = $recordQuery->execute()->fetch();
199
200 View Code Duplication
                        if (!$record) {
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...
201
                            $errorMessage = sprintf('Impossible to find row with uid %s in the table %s', $result['uid'], $tableName);
202
                            $this->logger->warning($errorMessage);
203
                            $this->connection->update($localTable, [
204
                                'error' => $errorMessage,
205
                                'errorTime' => date('Y-m-d H:i:s'),
206
                            ], [
207
                                'uid' => $result['uid'],
208
                                'table_name' => $tableName
209
                            ]);
210
                            continue;
211
                        }
212
213 View Code Duplication
                        if (isset($zohoBeans[$record['uid']])) {
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...
214
                            $zohoBean = $zohoBeans[$record['uid']];
215
                        } else {
216
                            $zohoBean = $zohoDao->create();
217
                        }
218
219
                        $this->logger->debug(sprintf('New row with uid %s from table %s added in queue to be pushed.', $record['uid'], $tableName));
220
                        $this->insertDataZohoBean($zohoDao, $zohoBean, $record);
221
                        $zohoBeans[$record['uid']] = $zohoBean;
222
                        $rowsDeleted[] = $record['uid'];
223
                    }
224
                }
225
                if (count($zohoBeans)) {
226
                    $this->sendDataToZohoCleanLocal($zohoDao, $zohoBeans, $rowsDeleted, $update);
227
                }
228
                $countToPush = $this->countElementInTable($zohoDao, $update);
229
            } while ($countToPush > 0);
230
        }
231
    }
232
233
    /**
234
     * @param AbstractZohoDao $zohoDao
235
     * @param ZohoBeanInterface[] $zohoBeans
236
     * @param string[] $rowsDeleted
237
     * @param bool $update
238
     */
239
    private function sendDataToZohoCleanLocal(AbstractZohoDao $zohoDao, array $zohoBeans, $rowsDeleted, $update = false)
0 ignored issues
show
Unused Code introduced by
The parameter $rowsDeleted is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
240
    {
241
        $local_table = $update ? 'local_update' : 'local_insert';
242
        $tableName = ZohoDatabaseHelper::getTableName($zohoDao, $this->prefix);
243
        $entityResponses = $zohoDao->save($zohoBeans);
244
        $responseKey = 0;
245
        foreach ($zohoBeans as $uid => $zohoBean) {
246
            $response = $entityResponses[$responseKey]->getResponseJSON();
247
            if (strtolower($response['code']) === 'success') {
248
                if ($update) {
249
                    $this->logger->debug(sprintf('Updated successfully the record with uid %s (id \'%s\') from the table %s', $uid, $zohoBean->getZohoId(), $tableName));
250
                    $this->connection->executeQuery(
251
                        'DELETE FROM local_update WHERE uid LIKE :uid AND table_name = :table_name AND error IS NULL',
252
                        [
253
                            'uid' => $uid,
254
                            'table_name' => $tableName
255
                        ]
256
                    );
257
                } else {
258
                    $countResult = (int)$this->connection->fetchColumn('select count(id) from ' . $tableName . ' where id = :id', ['id' => $zohoBean->getZohoId()]);
259
                    //If the sent data were duplicates Zoho can merged so we need to check if the Zoho ID already exist.
260
                    if ($countResult === 0) {
261
                        // ID not exist we can update the new row with the Zoho ID
262
                        $this->connection->beginTransaction();
263
                        $this->connection->update($tableName, ['id' => $zohoBean->getZohoId()], ['uid' => $uid]);
264
                        $this->connection->delete('local_insert', ['table_name' => $tableName, 'uid' => $uid]);
265
                        $this->connection->commit();
266
                        $this->logger->debug(sprintf('Inserted successfully the record with uid %s (id \'%s\') from the table %s', $uid, $zohoBean->getZohoId(), $tableName));
267
                    } else {
268
                        //ID already exist we need to delete the duplicate row.
269
                        $this->connection->beginTransaction();
270
                        $this->connection->delete($tableName, ['uid' => $uid]);
271
                        $this->connection->delete('local_insert', ['table_name' => $tableName, 'uid' => $uid]);
272
                        $this->connection->commit();
273
                        $this->logger->warning(sprintf('Duplicate record found when inserting record with uid %s from the table %s. ID updated: %s. UID deleted: %s', $uid, $tableName, $zohoBean->getZohoId(), $uid));
274
                    }
275
                }
276
            } else {
277
                $errorMessage = sprintf('An error occurred when %s record with uid %s from table %s into Zoho: %s', ($update ? 'updating' : 'inserting'), $uid, $tableName, json_encode($response));
278
                $this->logger->error($errorMessage);
279
                $this->connection->update($local_table, [
280
                    'error' => $errorMessage,
281
                    'errorTime' => date('Y-m-d H:i:s')
282
                ], [
283
                    'uid' => $uid,
284
                    'table_name' => $tableName
285
                ]);
286
            }
287
            $responseKey++;
288
        }
289
    }
290
    
291
    private function endsWith($haystack, $needle) {
292
        return substr_compare($haystack, $needle, -strlen($needle)) === 0;
293
    }
294
295
    /**
296
     * Insert data to bean in order to insert zoho records.
297
     *
298
     * @param AbstractZohoDao $dao
299
     * @param ZohoBeanInterface $zohoBean
300
     * @param array $row
301
     */
302
    private function insertDataZohoBean(AbstractZohoDao $dao, ZohoBeanInterface $zohoBean, array $row)
303
    {
304
        foreach ($row as $columnName => $columnValue) {
305
            $fieldMethod = $dao->getFieldFromFieldName($columnName);
306 View Code Duplication
            if (!in_array($columnName, EntitiesGeneratorService::$defaultDateFields) && $fieldMethod
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...
307
                && (!in_array($columnName, ['id', 'uid'])) && !is_null($columnValue)
308
            ) {
309
                // Changing only Name doesn't work properly on Zoho
310
                if ($this->endsWith($columnName, '_OwnerName') || $this->endsWith($columnName, '_Name')) {
311
                    continue;
312
                }
313
                $type = $fieldMethod->getType();
314
                $value = $this->formatValueToBeans($type, $columnValue);
315
                $setterMethod = $fieldMethod->getSetter();
316
                $zohoBean->{$setterMethod}($value);
317
            }
318
        }
319
    }
320
321
    /**
322
     * Insert data to bean in order to update zoho records.
323
     *
324
     * @param ZohoBeanInterface $zohoBean
325
     * @param array $fieldsMatching
0 ignored issues
show
Bug introduced by
There is no parameter named $fieldsMatching. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
326
     * @param type $columnName
327
     * @param type $valueDb
328
     */
329
    private function updateDataZohoBean(AbstractZohoDao $dao, ZohoBeanInterface $zohoBean, $columnName, $valueDb)
330
    {
331
        $fieldMethod = $dao->getFieldFromFieldName($columnName);
332 View Code Duplication
        if (!in_array($columnName, EntitiesGeneratorService::$defaultDateFields) && $fieldMethod
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...
333
            && !in_array($columnName, ['id', 'uid'])
334
        ) {
335
            // Changing only Name doesn't work properly on Zoho
336
            if ($this->endsWith($columnName, '_OwnerName') || $this->endsWith($columnName, '_Name')) {
337
                return;
338
            }
339
            $type = $fieldMethod->getType();
340
            $value = is_null($valueDb) ? $valueDb : $this->formatValueToBeans($type, $valueDb);
341
            $setterMethod = $fieldMethod->getSetter();
342
            $zohoBean->{$setterMethod}($value);
343
        }
344
    }
345
346
    /**
347
     * Change the value to the good format.
348
     *
349
     * @param string $type
350
     * @param mixed $value
351
     *
352
     * @return mixed
353
     */
354
    private function formatValueToBeans($type, $value)
355
    {
356
        switch ($type) {
357
            case 'date':
358
                $value = \DateTime::createFromFormat('Y-m-d', $value) ?: null;
359
                break;
360
            case 'datetime':
361
                $value = \DateTime::createFromFormat('Y-m-d H:i:s', $value) ?: null;
362
                break;
363
            case 'boolean' :
364
                $value = (bool)$value;
365
                break;
366
            case 'percent' :
367
                $value = (int)$value;
368
                break;
369
            case 'double' :
370
                $value = number_format($value, 2);
371
                break;
372
            case 'multiselectlookup':
373
            case 'multiuserlookup':
374
            case 'multiselectpicklist':
375
                $value = explode(';', $value);
376
                break;
377
        }
378
379
        return $value;
380
    }
381
382
    /**
383
     * Run deleted rows to Zoho : local_delete.
384
     *
385
     * @param AbstractZohoDao $zohoDao
386
     */
387
    public function pushDeletedRows(AbstractZohoDao $zohoDao)
388
    {
389
        $localTable = 'local_delete';
390
        $tableName = ZohoDatabaseHelper::getTableName($zohoDao, $this->prefix);
391
        $statement = $this->connection->createQueryBuilder();
392
        $statement->select('l.id')
393
            ->from($localTable, 'l')
394
            ->where('l.table_name=:table_name')
395
            ->setParameters(
396
                [
397
                    'table_name' => $tableName,
398
                ]
399
            );
400
        $results = $statement->execute();
401
        while ($row = $results->fetch()) {
402
            $zohoDao->delete($row['id']);
403
            $this->connection->delete($localTable, ['table_name' => $tableName, 'id' => $row['id']]);
404
        }
405
    }
406
407
    /**
408
     * Run inserted rows to Zoho : local_insert.
409
     *
410
     * @param AbstractZohoDao $zohoDao
411
     */
412
    public function pushInsertedRows(AbstractZohoDao $zohoDao)
413
    {
414
        $this->pushDataToZoho($zohoDao);
415
    }
416
417
    /**
418
     * Run updated rows to Zoho : local_update.
419
     *
420
     * @param AbstractZohoDao $zohoDao
421
     */
422
    public function pushUpdatedRows(AbstractZohoDao $zohoDao)
423
    {
424
        $this->pushDataToZoho($zohoDao, true);
425
    }
426
427
    /**
428
     * Push data from db to Zoho.
429
     *
430
     * @param AbstractZohoDao $zohoDao
431
     */
432
    public function pushToZoho(AbstractZohoDao $zohoDao)
433
    {
434
        $this->logger->info(sprintf('Pushing inserted rows for module %s into Zoho...', $zohoDao->getPluralModuleName()));
435
        $this->pushInsertedRows($zohoDao);
436
        $this->logger->info(sprintf('Pushing updated rows for module %s into Zoho...', $zohoDao->getPluralModuleName()));
437
        $this->pushUpdatedRows($zohoDao);
438
        $this->logger->info(sprintf('Pushing deleted rows for module %s into Zoho...', $zohoDao->getPluralModuleName()));
439
        $this->pushDeletedRows($zohoDao);
440
    }
441
}
442