1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace Wabel\Zoho\CRM\Copy; |
4
|
|
|
|
5
|
|
|
use Mouf\Utils\Common\Lock; |
6
|
|
|
use Mouf\Utils\Common\LockException; |
7
|
|
|
use Symfony\Component\Console\Command\Command; |
8
|
|
|
use Symfony\Component\Console\Input\InputInterface; |
9
|
|
|
use Symfony\Component\Console\Logger\ConsoleLogger; |
10
|
|
|
use Symfony\Component\Console\Output\OutputInterface; |
11
|
|
|
use Symfony\Component\Console\Input\InputOption; |
12
|
|
|
use Wabel\Zoho\CRM\AbstractZohoDao; |
13
|
|
|
use Wabel\Zoho\CRM\Service\EntitiesGeneratorService; |
14
|
|
|
use Wabel\Zoho\CRM\ZohoClient; |
15
|
|
|
use Logger\Formatters\DateTimeFormatter; |
16
|
|
|
use Mouf\Utils\Log\Psr\MultiLogger; |
17
|
|
|
use Wabel\Zoho\CRM\Request\Response; |
18
|
|
|
|
19
|
|
|
class ZohoSyncDatabaseCommand extends Command |
20
|
|
|
{ |
21
|
|
|
/** |
22
|
|
|
* The list of Zoho DAOs to copy. |
23
|
|
|
* |
24
|
|
|
* @var AbstractZohoDao[] |
25
|
|
|
*/ |
26
|
|
|
private $zohoDaos; |
27
|
|
|
|
28
|
|
|
/** |
29
|
|
|
* @var ZohoDatabaseModelSync |
30
|
|
|
*/ |
31
|
|
|
private $zohoDatabaseModelSync; |
32
|
|
|
|
33
|
|
|
/** |
34
|
|
|
* @var ZohoDatabaseCopier |
35
|
|
|
*/ |
36
|
|
|
private $zohoDatabaseCopier; |
37
|
|
|
|
38
|
|
|
/** |
39
|
|
|
* @var ZohoDatabasePusher |
40
|
|
|
*/ |
41
|
|
|
private $zohoDatabaseSync; |
42
|
|
|
|
43
|
|
|
/** |
44
|
|
|
* |
45
|
|
|
* @var MultiLogger |
46
|
|
|
*/ |
47
|
|
|
private $logger; |
48
|
|
|
|
49
|
|
|
/** |
50
|
|
|
* @var Lock |
51
|
|
|
*/ |
52
|
|
|
private $lock; |
53
|
|
|
|
54
|
|
|
/** |
55
|
|
|
* The Zoho Dao and Beans generator |
56
|
|
|
* |
57
|
|
|
* @var EntitiesGeneratorService |
58
|
|
|
*/ |
59
|
|
|
private $zohoEntitiesGenerator; |
60
|
|
|
|
61
|
|
|
/** |
62
|
|
|
* |
63
|
|
|
* @var ZohoClient |
64
|
|
|
*/ |
65
|
|
|
private $zohoClient; |
66
|
|
|
|
67
|
|
|
private $pathZohoDaos; |
68
|
|
|
|
69
|
|
|
private $namespaceZohoDaos; |
70
|
|
|
|
71
|
|
|
/** |
72
|
|
|
* |
73
|
|
|
* @var Response |
74
|
|
|
*/ |
75
|
|
|
private $usersResponse; |
76
|
|
|
|
77
|
|
|
/** |
78
|
|
|
* @var string[] |
79
|
|
|
*/ |
80
|
|
|
private $excludedZohoDao; |
81
|
|
|
|
82
|
|
|
|
83
|
|
|
/** |
84
|
|
|
* @param ZohoDatabaseModelSync $zohoDatabaseModelSync |
85
|
|
|
* @param ZohoDatabaseCopier $zohoDatabaseCopier |
86
|
|
|
* @param ZohoDatabasePusher $zohoDatabaseSync |
87
|
|
|
* @param EntitiesGeneratorService $zohoEntitiesGenerator The Zoho Dao and Beans generator |
88
|
|
|
* @param ZohoClient $zohoClient |
89
|
|
|
* @param string $pathZohoDaos Tht path where we need to generate the Daos. |
90
|
|
|
* @param string $namespaceZohoDaos Daos namespace |
91
|
|
|
* @param MultiLogger $logger |
92
|
|
|
* @param Lock $lock A lock that can be used to avoid running the same command (copy) twice at the same time |
93
|
|
|
* @param string[] $excludedZohoDao To exclude Dao and or solve Dao which can create ZohoResponse Error |
94
|
|
|
*/ |
95
|
|
|
public function __construct(ZohoDatabaseModelSync $zohoDatabaseModelSync, ZohoDatabaseCopier $zohoDatabaseCopier, ZohoDatabasePusher $zohoDatabaseSync, |
96
|
|
|
EntitiesGeneratorService $zohoEntitiesGenerator, ZohoClient $zohoClient, |
97
|
|
|
$pathZohoDaos, $namespaceZohoDaos, MultiLogger $logger, Lock $lock = null, $excludedZohoDao = [] |
98
|
|
|
) |
99
|
|
|
{ |
100
|
|
|
parent::__construct(); |
101
|
|
|
$this->zohoDatabaseModelSync = $zohoDatabaseModelSync; |
102
|
|
|
$this->zohoDatabaseCopier = $zohoDatabaseCopier; |
103
|
|
|
$this->zohoDatabaseSync = $zohoDatabaseSync; |
104
|
|
|
$this->zohoEntitiesGenerator = $zohoEntitiesGenerator; |
105
|
|
|
$this->zohoClient = $zohoClient; |
106
|
|
|
$this->pathZohoDaos = $pathZohoDaos; |
107
|
|
|
$this->namespaceZohoDaos = $namespaceZohoDaos; |
108
|
|
|
$this->logger = $logger; |
109
|
|
|
$this->lock = $lock; |
110
|
|
|
$this->excludedZohoDao = $excludedZohoDao; |
111
|
|
|
} |
112
|
|
|
|
113
|
|
|
protected function configure() |
114
|
|
|
{ |
115
|
|
|
$this |
116
|
|
|
->setName('zoho:sync') |
117
|
|
|
->setDescription('Synchronize the Zoho CRM data in a local database.') |
118
|
|
|
->addOption('reset', 'r', InputOption::VALUE_NONE, 'Get a fresh copy of Zoho (rather than doing incremental copy)') |
119
|
|
|
->addOption('skip-trigger', 's', InputOption::VALUE_NONE, 'Do not create or update the trigger') |
120
|
|
|
->addOption('fetch-only', 'f', InputOption::VALUE_NONE, 'Fetch only the Zoho data in local database') |
121
|
|
|
->addOption('push-only', 'p', InputOption::VALUE_NONE, 'Push only the local data to Zoho') |
122
|
|
|
->addOption('limit', 'l', InputOption::VALUE_NONE, 'use defined memory limit or unlimited memory limit') |
123
|
|
|
->addOption('log-path', null, InputOption::VALUE_OPTIONAL, 'Set the path of logs file') |
124
|
|
|
->addOption('clear-logs', null, InputOption::VALUE_NONE, 'Clear logs file at startup') |
125
|
|
|
->addOption('dump-logs', null, InputOption::VALUE_NONE, 'Dump logs into console when command finishes') |
126
|
|
|
->addOption('continue-on-error', null, InputOption::VALUE_NONE, 'Don\'t stop the command on errors') |
127
|
|
|
->addOption('fetch-bulk', null, InputOption::VALUE_NONE, 'Fetch the data module by module using bulk read API') |
128
|
|
|
->addOption('modified-since', null, InputOption::VALUE_OPTIONAL, 'Fetch the data since a particular date (ISO 8601)'); |
129
|
|
|
} |
130
|
|
|
|
131
|
|
|
protected function execute(InputInterface $input, OutputInterface $output) |
132
|
|
|
{ |
133
|
|
|
$this->logger->addLogger(new DateTimeFormatter(new ConsoleLogger($output))); |
134
|
|
|
$this->logger->notice('Command ZohoSyncDatabase started'); |
135
|
|
|
try { |
136
|
|
|
if ($this->lock) { |
137
|
|
|
$this->lock->acquireLock(); |
138
|
|
|
} |
139
|
|
|
|
140
|
|
|
// TODO: find a better way when zohocrm/php-sdk:src/crm/utility/Logger.php will allow to get the filename, delete, etc. |
141
|
|
|
if ($input->getOption('log-path') && $input->getOption('clear-logs')) { |
142
|
|
|
$this->logger->notice('Clearing logs...'); |
143
|
|
|
$path = $input->getOption('log-path'); |
144
|
|
|
$logFile = $path . '/ZCRMClientLibrary.log'; |
145
|
|
|
if (file_exists($logFile)) { |
146
|
|
|
if (is_writable($logFile)) { |
147
|
|
|
if (file_put_contents($logFile, '') === false) { |
148
|
|
|
$this->logger->error(sprintf('Error when clearing log file in %s', $logFile)); |
149
|
|
|
} |
150
|
|
|
} else { |
151
|
|
|
$this->logger->warning(sprintf('Cannot write into log file in %s', $logFile)); |
152
|
|
|
} |
153
|
|
|
} else { |
154
|
|
|
$this->logger->warning(sprintf('Cannot find log file in %s', $logFile)); |
155
|
|
|
} |
156
|
|
|
} |
157
|
|
|
|
158
|
|
|
if (!$input->getOption('limit')) { |
159
|
|
|
ini_set('memory_limit', '-1'); |
160
|
|
|
} |
161
|
|
|
|
162
|
|
|
if ($input->getOption('fetch-only') && $input->getOption('push-only')) { |
163
|
|
|
$this->logger->error('Options fetch-only and push-only are mutually exclusive.'); |
164
|
|
|
} |
165
|
|
|
|
166
|
|
|
$this->syncUserModel(); |
167
|
|
|
|
168
|
|
|
$this->regenerateZohoDao(); |
169
|
|
|
|
170
|
|
|
$this->syncModel($input); |
171
|
|
|
|
172
|
|
|
if ($input->getOption('fetch-bulk')) { |
173
|
|
|
$this->fetchUserDb(); |
174
|
|
|
$this->fetchDbInBulk($input); |
175
|
|
|
} else { |
176
|
|
|
if (!$input->getOption('push-only')) { |
177
|
|
|
$this->fetchUserDb(); |
178
|
|
|
$this->fetchDb($input); |
179
|
|
|
} |
180
|
|
|
if (!$input->getOption('fetch-only')) { |
181
|
|
|
$this->pushDb(); |
182
|
|
|
} |
183
|
|
|
} |
184
|
|
|
|
185
|
|
|
if ($input->getOption('log-path') && $input->getOption('dump-logs')) { |
186
|
|
|
$this->logger->info('Dumping logs...'); |
187
|
|
|
$path = $input->getOption('log-path'); |
188
|
|
|
$logFile = $path . '/ZCRMClientLibrary.log'; |
189
|
|
|
if (file_exists($logFile)) { |
190
|
|
|
if (is_readable($logFile)) { |
191
|
|
|
$this->logger->info(file_get_contents($logFile)); |
192
|
|
|
} else { |
193
|
|
|
$this->logger->warning(sprintf('Cannot read into log file in %s', $logFile)); |
194
|
|
|
} |
195
|
|
|
} else { |
196
|
|
|
$this->logger->warning(sprintf('Cannot find log file in %s', $logFile)); |
197
|
|
|
} |
198
|
|
|
} |
199
|
|
|
|
200
|
|
|
if ($this->lock) { |
201
|
|
|
$this->lock->releaseLock(); |
202
|
|
|
} |
203
|
|
|
} catch (LockException $e) { |
204
|
|
|
$this->logger->error('Could not start zoho:copy-db copy command. Another zoho:copy-db copy command is already running.'); |
205
|
|
|
} |
206
|
|
|
$this->logger->notice('Command ZohoSyncDatabase finished'); |
207
|
|
|
} |
208
|
|
|
|
209
|
|
|
/** |
210
|
|
|
* Sychronizes the model of the database with Zoho records. |
211
|
|
|
* |
212
|
|
|
* @param InputInterface $input |
213
|
|
|
*/ |
214
|
|
|
private function syncModel(InputInterface $input) |
215
|
|
|
{ |
216
|
|
|
$twoWaysSync = !$input->getOption('fetch-only'); |
217
|
|
|
$skipCreateTrigger = $input->getOption('skip-trigger'); |
218
|
|
|
|
219
|
|
|
$this->logger->notice('Starting to synchronize Zoho DB model with the local database...'); |
220
|
|
|
foreach ($this->zohoDaos as $zohoDao) { |
221
|
|
|
$this->zohoDatabaseModelSync->synchronizeDbModel($zohoDao, $twoWaysSync, $skipCreateTrigger); |
222
|
|
|
} |
223
|
|
|
$this->logger->notice('Zoho DB model successfully synchronized.'); |
224
|
|
|
} |
225
|
|
|
|
226
|
|
|
/** |
227
|
|
|
* Sychronizes the model of the database with Zoho Users records. |
228
|
|
|
*/ |
229
|
|
|
private function syncUserModel() |
230
|
|
|
{ |
231
|
|
|
$this->logger->notice('Starting to synchronize Zoho users DB model with the local database...'); |
232
|
|
|
$this->zohoDatabaseModelSync->synchronizeUserDbModel(); |
233
|
|
|
$this->logger->notice('Zoho users DB model successfully synchronized.'); |
234
|
|
|
} |
235
|
|
|
|
236
|
|
|
/** |
237
|
|
|
* Regerate Zoho Daos |
238
|
|
|
*/ |
239
|
|
|
private function regenerateZohoDao() |
240
|
|
|
{ |
241
|
|
|
$this->logger->notice('Starting to generate all the Zoho daos...'); |
242
|
|
|
$zohoModules = $this->zohoEntitiesGenerator->generateAll($this->pathZohoDaos, $this->namespaceZohoDaos); |
243
|
|
|
foreach ($zohoModules as $daoFullClassName) { |
244
|
|
|
/* @var $zohoDao AbstractZohoDao */ |
245
|
|
|
$zohoDao = new $daoFullClassName($this->zohoClient); |
246
|
|
|
//To have more module which is use time of modification (createdTime or lastActivityTime). |
247
|
|
|
//use an array of Excluded Dao by full namespace |
248
|
|
|
if ($this->excludedZohoDao && in_array(get_class($zohoDao), $this->excludedZohoDao)) { |
|
|
|
|
249
|
|
|
$this->logger->debug(sprintf('%s has been excluded from generation', get_class($zohoDao))); |
250
|
|
|
continue; |
251
|
|
|
} |
252
|
|
|
$this->zohoDaos [] = $zohoDao; |
253
|
|
|
$this->logger->info(sprintf('%s has been created for module %s', get_class($zohoDao), $zohoDao->getPluralModuleName())); |
254
|
|
|
} |
255
|
|
|
$this->logger->notice('Finished to create all the Zoho daos.'); |
256
|
|
|
} |
257
|
|
|
|
258
|
|
|
/** |
259
|
|
|
* Run the fetch User Db command. |
260
|
|
|
*/ |
261
|
|
|
private function fetchUserDb() |
262
|
|
|
{ |
263
|
|
|
$this->logger->notice('Starting to copy Zoho users data into local database...'); |
264
|
|
|
$this->zohoDatabaseCopier->fetchUserFromZoho(); |
265
|
|
|
$this->logger->notice('Zoho users data successfully copied.'); |
266
|
|
|
} |
267
|
|
|
|
268
|
|
|
|
269
|
|
|
/** |
270
|
|
|
* Run the fetch Db command. |
271
|
|
|
* |
272
|
|
|
* @param InputInterface $input |
273
|
|
|
*/ |
274
|
|
|
private function fetchDb(InputInterface $input) |
275
|
|
|
{ |
276
|
|
|
$throwErrors = true; |
277
|
|
|
if ($input->getOption('continue-on-error')) { |
278
|
|
|
$throwErrors = false; |
279
|
|
|
} |
280
|
|
|
|
281
|
|
|
if ($input->getOption('reset')) { |
282
|
|
|
$incremental = false; |
283
|
|
|
} else { |
284
|
|
|
$incremental = true; |
285
|
|
|
} |
286
|
|
|
|
287
|
|
|
$modifiedSince = null; |
288
|
|
|
if ($input->getOption('modified-since')) { |
289
|
|
|
$modifiedSince = $input->getOption('modified-since'); |
290
|
|
|
} |
291
|
|
|
|
292
|
|
|
$twoWaysSync = !$input->getOption('fetch-only'); |
293
|
|
|
|
294
|
|
|
$this->logger->notice('Starting to fetch Zoho data into local database...'); |
295
|
|
|
foreach ($this->zohoDaos as $zohoDao) { |
296
|
|
|
$this->logger->notice(sprintf('Copying data into local for module %s...', $zohoDao->getPluralModuleName())); |
297
|
|
|
$this->zohoDatabaseCopier->fetchFromZoho($zohoDao, $incremental, $twoWaysSync, $throwErrors, $modifiedSince); |
298
|
|
|
} |
299
|
|
|
$this->logger->notice('Zoho data successfully fetched.'); |
300
|
|
|
} |
301
|
|
|
|
302
|
|
|
|
303
|
|
|
/** |
304
|
|
|
* Run the fetch Db command. |
305
|
|
|
* |
306
|
|
|
* @param InputInterface $input |
307
|
|
|
*/ |
308
|
|
|
private function fetchDbInBulk(InputInterface $input) |
|
|
|
|
309
|
|
|
{ |
310
|
|
|
$this->logger->notice('Starting to bulk fetch Zoho data into local database...'); |
311
|
|
|
foreach ($this->zohoDaos as $zohoDao) { |
312
|
|
|
$this->logger->notice(sprintf('Copying data into local for module %s...', $zohoDao->getPluralModuleName())); |
313
|
|
|
$this->zohoDatabaseCopier->fetchFromZohoInBulk($zohoDao); |
314
|
|
|
} |
315
|
|
|
$this->logger->notice('Zoho data successfully fetched.'); |
316
|
|
|
} |
317
|
|
|
|
318
|
|
|
/** |
319
|
|
|
* Run the push Db command. |
320
|
|
|
*/ |
321
|
|
|
private function pushDb() |
322
|
|
|
{ |
323
|
|
|
$this->logger->notice('Starting to push data from local database into Zoho CRM...'); |
324
|
|
|
foreach ($this->zohoDaos as $zohoDao) { |
325
|
|
|
if ($zohoDao->getFieldFromFieldName('createdTime')) { |
326
|
|
|
$this->zohoDatabaseSync->pushToZoho($zohoDao); |
327
|
|
|
} |
328
|
|
|
} |
329
|
|
|
$this->logger->notice('Zoho data successfully pushed.'); |
330
|
|
|
} |
331
|
|
|
} |
332
|
|
|
|
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.