Completed
Pull Request — 1.1 (#29)
by
unknown
05:23
created

AbstractZohoDao::toXmlRelatedRecords()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 1
1
<?php
2
3
namespace Wabel\Zoho\CRM;
4
5
use Wabel\Zoho\CRM\Exception\ZohoCRMException;
6
use Wabel\Zoho\CRM\Exception\ZohoCRMResponseException;
7
use Wabel\Zoho\CRM\Exception\ZohoCRMUpdateException;
8
use Wabel\Zoho\CRM\Request\Response;
9
10
/**
11
 * Base class that provides access to Zoho through Zoho beans.
12
 */
13
abstract class AbstractZohoDao
14
{
15
    const ON_DUPLICATE_THROW = 1;
16
    const ON_DUPLICATE_MERGE = 2;
17
    const MAX_GET_RECORDS = 200;
18
    const MAX_GET_RECORDS_BY_ID = 100;
19
    const MAX_SIMULTANEOUS_SAVE = 100;
20
21
    /**
22
     * The class implementing API methods not directly related to a specific module.
23
     *
24
     * @var ZohoClient
25
     */
26
    protected $zohoClient;
27
28
    public function __construct(ZohoClient $zohoClient)
29
    {
30
        $this->zohoClient = $zohoClient;
31
    }
32
33
    abstract protected function getModule();
34
    abstract protected function getSingularModuleName();
35
    abstract protected function getPluralModuleName();
36
    abstract protected function getBeanClassName();
37
    abstract protected function getFields();
38
39
    protected $flatFields;
40
41
    /**
42
     * Returns a flat list of all fields.
43
     *
44
     * @return array The array of field names for a module
45
     */
46
    protected function getFlatFields()
47
    {
48
        if ($this->flatFields === null) {
49
            $this->flatFields = array();
50
            foreach ($this->getFields() as $cat) {
51
                $this->flatFields = array_merge($this->flatFields, $cat);
52
            }
53
        }
54
55
        return $this->flatFields;
56
    }
57
58
    protected $duplicateCheck = self::ON_DUPLICATE_MERGE;
59
60
    public function setDuplicateCheck($duplicateCheck)
61
    {
62
	$this->duplicateCheck = $duplicateCheck;
63
    }
64
65
    /**
66
     * Parse a Zoho Response in order to retrieve one or several ZohoBeans from it.
67
     *
68
     * @param Response $zohoResponse The response returned by the ZohoClient->call() method
69
     *
70
     * @return ZohoBeanInterface[] The array of Zoho Beans parsed from the response
71
     */
72
    protected function getBeansFromResponse(Response $zohoResponse)
73
    {
74
        $beanClass = $this->getBeanClassName();
75
        $fields = $this->getFlatFields();
76
77
        $beanArray = array();
78
79
        foreach ($zohoResponse->getRecords() as $record) {
80
81
            /** @var ZohoBeanInterface $bean */
82
            $bean = new $beanClass();
83
84
            // First, let's fill the ID.
85
            // The ID is CONTACTID or ACCOUNTID or Id depending on the Zoho type.
86
            $idName = strtoupper(rtrim($this->getModule(), 's'));
87
            if (isset($record[$idName.'ID'])) {
88
                $id = $record[$idName.'ID'];
89
            } elseif (isset($record[$idName.'_ID'])) {
90
                $id = $record[$idName.'_ID'];
91
            } else {
92
                $id = $record['Id'];
93
            }
94
            $bean->setZohoId($id);
95
            $bean->setCreatedTime(\DateTime::createFromFormat('Y-m-d H:i:s', $record['Created Time']));
0 ignored issues
show
Security Bug introduced by
It seems like \DateTime::createFromFor...record['Created Time']) targeting DateTime::createFromFormat() can also be of type false; however, Wabel\Zoho\CRM\ZohoBeanInterface::setCreatedTime() does only seem to accept object<DateTime>, did you maybe forget to handle an error condition?
Loading history...
96
            $bean->setModifiedTime(\DateTime::createFromFormat('Y-m-d H:i:s', $record['Modified Time']));
0 ignored issues
show
Security Bug introduced by
It seems like \DateTime::createFromFor...ecord['Modified Time']) targeting DateTime::createFromFormat() can also be of type false; however, Wabel\Zoho\CRM\ZohoBeanI...face::setModifiedTime() does only seem to accept object<DateTime>, did you maybe forget to handle an error condition?
Loading history...
97
98
            foreach ($record as $key => $value) {
99
                if (isset($fields[$key])) {
100
                    $setter = $fields[$key]['setter'];
101
102
                    switch ($fields[$key]['type']) {
103
                        case 'Date':
104
                            if ($dateObj = \DateTimeImmutable::createFromFormat('M/d/Y', $value)) {
105
                                $value = $dateObj;
106
                            } elseif ($dateObj = \DateTimeImmutable::createFromFormat('Y-m-d', $value)) {
107
                                $value = $dateObj;
108
                            } else {
109
                                throw new ZohoCRMException('Unable to convert the Date field "'.$key."\" into a DateTime PHP object from the the record $id of the module ".$this->getModule().'.');
110
                            }
111
                            break;
112
                        case 'DateTime':
113
                            $value = \DateTimeImmutable::createFromFormat('Y-m-d H:i:s', $value);
114
                            break;
115
                        case 'Boolean':
116
                            $value = ($value == 'true');
117
                            break;
118
                        default:
119
                            break;
120
                    }
121
                    $bean->$setter($value);
122
                }
123
            }
124
125
            $beanArray[] = $bean;
126
        }
127
128
        return $beanArray;
129
    }
130
131
    /**
132
     * Convert an array of ZohoBeans into a SimpleXMLElement for use when inserting/updating related records.
133
     *
134
     * @param $zohoBeans ZohoBeanInterface[]
135
     *
136
     * @return \SimpleXMLElement The SimpleXMLElement containing the XML for a request
137
     */
138
    public function toXmlRelatedRecords($zohoBeans)
139
    {
140
    	return $this->toXml($zohoBeans, 1);
141
    }
142
143
    /**
144
     * Convert an array of ZohoBeans into a SimpleXMLElement.
145
     *
146
     * @param $zohoBeans ZohoBeanInterface[]
147
     *
148
     * @return \SimpleXMLElement The SimpleXMLElement containing the XML for a request
149
     */
150
    public function toXml($zohoBeans, $isRelatedRecords = 0)
151
    {
152
        $module = $this->getModule();
153
154
        $no = 1;
155
        $module = new \SimpleXMLElement("<$module/>");
156
157
        foreach ($zohoBeans as $zohoBean) {
158
            if (!$zohoBean instanceof ZohoBeanInterface) {
159
                throw new ZohoCRMException('Zoho beans sent to save must implement the ZohoBeanInterface.');
160
            }
161
162
            $properties = $this->getFlatFields();
163
            $row = $module->addChild('row');
164
            $row->addAttribute('no', $no);
165
166
            $fl = $row->addChild('FL', $zohoBean->getZohoId());
167
            $id = 'Id';
168
            if ($isRelatedRecords) {
169
	            $idName = strtoupper(rtrim($this->getModule(), 's'));
170
	            $id = $idName . 'ID';
171
	    }
172
            $fl->addAttribute('val', $id);
173
174
            foreach ($properties as $name => $params) {
175
                $camelCaseName = $params['name'];
176
                $isDirty = $zohoBean->isDirty($camelCaseName);
177
                if (!$isDirty) {
178
                    continue;
179
                }
180
181
                $getter = $params['getter'];
182
                $value = $zohoBean->$getter();
183
184
                if (!empty($value) || is_bool($value)) {
185
186
                    // We convert the value back to a proper format if the Zoho Type is Date, DateTime or Boolean
187
                    switch ($params['type']) {
188
                        case 'Date':
189
                            /** @var \DateTime $value */
190
                            $value = $value->format('m/d/Y');
191
                            break;
192
                        case 'DateTime':
193
                            /** @var \DateTime $value */
194
                            $value = $value->format('Y-m-d H:i:s');
195
                            break;
196
                        case 'Boolean':
197
                            /** @var bool $value */
198
                            $value = $value ? 'true' : 'false';
199
                            break;
200
                        default:
201
                            break;
202
                    }
203
                }
204
205
                $fl = $row->addChild('FL', htmlspecialchars($value));
206
                $fl->addAttribute('val', $name);
207
            }
208
            ++$no;
209
        }
210
211
        return $module;
212
    }
213
214
    /**
215
     * Implements deleteRecords API method.
216
     *
217
     * @param string $id Zoho Id of the record to delete
218
     *
219
     * @throws ZohoCRMResponseException
220
     */
221
    public function delete($id)
222
    {
223
        $this->zohoClient->deleteRecords($this->getModule(), $id);
224
    }
225
226
    /**
227
     * Implements getRecordById API method.
228
     *
229
     * @param string|array $id Zoho Id of the record to retrieve OR an array of IDs
230
     *
231
     * @return ZohoBeanInterface[] The array of Zoho Beans parsed from the response
232
     *
233
     * @throws ZohoCRMResponseException
234
     */
235
    public function getById($id)
236
    {
237
        try {
238
            $module = $this->getModule();
239
            $beans = [];
240
241
            // If there's several IDs to process, we divide them by pools of 100 and implode them before requesting
242
            if (is_array($id)) {
243
                foreach (array_chunk($id, self::MAX_GET_RECORDS_BY_ID) as $pool) {
244
                    $idlist = implode(';', $pool);
245
                    $response = $this->zohoClient->getRecordById($module, $idlist);
246
                    $beans = array_merge($beans, $this->getBeansFromResponse($response));
247
                }
248
            }
249
            // if not, we simply request our record
250
            else {
251
                $response = $this->zohoClient->getRecordById($module, $id);
252
                $beans = $this->getBeansFromResponse($response);
253
                $beans = array_shift($beans);
254
            }
255
256
            return $beans;
257
        } catch (ZohoCRMResponseException $e) {
258
            // No records found? Let's return an empty array!
259
            if ($e->getCode() == 4422) {
260
                return array();
261
            } else {
262
                throw $e;
263
            }
264
        }
265
    }
266
267
    /**
268
     * Implements getRecords API method.
269
     *
270
     * @param $sortColumnString
271
     * @param $sortOrderString
272
     * @param \DateTime $lastModifiedTime
273
     * @param $selectColumns
274
     * @param $limit
275
     *
276
     * @return ZohoBeanInterface[] The array of Zoho Beans parsed from the response
277
     *
278
     * @throws ZohoCRMResponseException
279
     */
280 View Code Duplication
    public function getRecords($sortColumnString = null, $sortOrderString = null, \DateTime $lastModifiedTime = null, $selectColumns = null, $limit = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
281
    {
282
        $globalResponse = array();
283
284
        do {
285
            try {
286
                $fromIndex = count($globalResponse) + 1;
287
                $toIndex = $fromIndex + self::MAX_GET_RECORDS - 1;
288
289
                if ($limit) {
290
                    $toIndex = min($limit, $toIndex);
291
                }
292
293
                $response = $this->zohoClient->getRecords($this->getModule(), $sortColumnString, $sortOrderString, $lastModifiedTime, $selectColumns, $fromIndex, $toIndex);
294
                $beans = $this->getBeansFromResponse($response);
295
            } catch (ZohoCRMResponseException $e) {
296
                // No records found? Let's return an empty array!
297
                if ($e->getCode() == 4422) {
298
                    $beans = array();
299
                } else {
300
                    throw $e;
301
                }
302
            }
303
304
            $globalResponse = array_merge($globalResponse, $beans);
305
        } while (count($beans) == self::MAX_GET_RECORDS);
306
307
        return $globalResponse;
308
    }
309
310
    /**
311
     * Returns the list of deleted records.
312
     *
313
     * @param \DateTimeInterface|null $lastModifiedTime
314
     * @param int                     $limit
315
     *
316
     * @return array
317
     *
318
     * @throws ZohoCRMResponseException
319
     * @throws \Exception
320
     */
321 View Code Duplication
    public function getDeletedRecordIds(\DateTimeInterface $lastModifiedTime = null, $limit = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
322
    {
323
        $globalDeletedIDs = array();
324
325
        do {
326
            try {
327
                $fromIndex = count($globalDeletedIDs) + 1;
328
                $toIndex = $fromIndex + self::MAX_GET_RECORDS - 1;
329
330
                if ($limit) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $limit of type integer|null is loosely compared to true; this is ambiguous if the integer can be zero. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
331
                    $toIndex = min($limit - 1, $toIndex);
332
                }
333
334
                $response = $this->zohoClient->getDeletedRecordIds($this->getModule(), $lastModifiedTime, $fromIndex, $toIndex);
335
                $deletedIDs = $response->getDeletedIds();
336
            } catch (ZohoCRMResponseException $e) {
337
                // No records found? Let's return an empty array!
338
                if ($e->getZohoCode() == 4422) {
339
                    $deletedIDs = array();
340
                } else {
341
                    throw $e;
342
                }
343
            }
344
345
            $globalDeletedIDs = array_merge($globalDeletedIDs, $deletedIDs);
346
        } while (count($deletedIDs) == self::MAX_GET_RECORDS);
347
348
        return $globalDeletedIDs;
349
    }
350
351
    /**
352
     * Implements getRecords API method.
353
     *
354
     * @param string $id           Zoho Id of the record to delete
355
     * @param string $parentModule The parent module of the records
356
     * @param int    $limit        The max number of records to fetch
357
     *
358
     * @return ZohoBeanInterface[] The array of Zoho Beans parsed from the response
359
     *
360
     * @throws ZohoCRMResponseException
361
     */
362 View Code Duplication
    public function getRelatedRecords($id, $parentModule, $limit = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
363
    {
364
        $globalResponse = array();
365
366
        do {
367
            try {
368
                $fromIndex = count($globalResponse) + 1;
369
                $toIndex = $fromIndex + self::MAX_GET_RECORDS - 1;
370
371
                if ($limit) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $limit of type integer|null is loosely compared to true; this is ambiguous if the integer can be zero. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
372
                    $toIndex = min($limit - 1, $toIndex);
373
                }
374
375
                $response = $this->zohoClient->getRelatedRecords($this->getModule(), $id, $parentModule, $fromIndex, $toIndex);
376
                $beans = $this->getBeansFromResponse($response);
377
            } catch (ZohoCRMResponseException $e) {
378
                // No records found? Let's return an empty array!
379
                if ($e->getCode() == 4422) {
380
                    $beans = array();
381
                } else {
382
                    throw $e;
383
                }
384
            }
385
386
            $globalResponse = array_merge($globalResponse, $beans);
387
        } while (count($beans) == self::MAX_GET_RECORDS);
388
389
        return $globalResponse;
390
    }
391
392
    /**
393
     * Implements searchRecords API method.
394
     *
395
     * @param string    $searchCondition  The search criteria formatted like
396
     * @param int       $limit            The maximum number of beans returned from Zoho
397
     * @param \DateTime $lastModifiedTime
398
     * @param string    $selectColumns    The list
399
     *
400
     * @return ZohoBeanInterface[] The array of Zoho Beans parsed from the response
401
     *
402
     * @throws ZohoCRMResponseException
403
     */
404 View Code Duplication
    public function searchRecords($searchCondition = null, $limit = null, \DateTime $lastModifiedTime = null, $selectColumns = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
405
    {
406
        $globalResponse = array();
407
408
        do {
409
            try {
410
                $fromIndex = count($globalResponse) + 1;
411
                $toIndex = $fromIndex + self::MAX_GET_RECORDS - 1;
412
413
                if ($limit) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $limit of type integer|null is loosely compared to true; this is ambiguous if the integer can be zero. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
414
                    $toIndex = min($limit - 1, $toIndex);
415
                }
416
417
                $response = $this->zohoClient->searchRecords($this->getModule(), $searchCondition, $fromIndex, $toIndex, $lastModifiedTime, $selectColumns);
0 ignored issues
show
Bug introduced by
It seems like $selectColumns defined by parameter $selectColumns on line 404 can also be of type string; however, Wabel\Zoho\CRM\ZohoClient::searchRecords() does only seem to accept null, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
418
                $beans = $this->getBeansFromResponse($response);
419
            } catch (ZohoCRMResponseException $e) {
420
                // No records found? Let's return an empty array!
421
                if ($e->getCode() == 4422) {
422
                    $beans = array();
423
                } else {
424
                    throw $e;
425
                }
426
            }
427
428
            $globalResponse = array_merge($globalResponse, $beans);
429
        } while (count($beans) == self::MAX_GET_RECORDS);
430
431
        return $globalResponse;
432
    }
433
434
    /**
435
     * Implements insertRecords API method.
436
     *
437
     * WARNING : When setting wfTrigger to true, this method will use an API call per bean
438
     * passed in argument. This is caused by Zoho limitation which forbids triggering any
439
     * workflow when inserting several beans simultaneously.
440
     *
441
     * @param ZohoBeanInterface[] $beans          The Zoho Beans to insert in the CRM
442
     * @param bool                $wfTrigger      Whether or not the call should trigger the workflows related to a "created" event
443
     * @param int                 $duplicateCheck 1 : Throwing error when a duplicate is found; 2 : Merging with existing duplicate
444
     * @param bool                $isApproval     Whether or not to push the record into an approval sandbox first
445
     *
446
     * @throws ZohoCRMResponseException
447
     */
448
    public function insertRecords($beans, $wfTrigger = null, $duplicateCheck = null, $isApproval = null)
449
    {
450
        $records = [];
451
452
	// For duplicate check, use the setting passed or the object-wide setting
453
	$duplicateCheck = $duplicateCheck ?: $this->duplicateCheck;
454
455
        if ($wfTrigger) {
456
            // If we trigger workflows, we trigger the insert of beans one by one.
457
            foreach ($beans as $bean) {
458
                $xmlData = $this->toXml([$bean]);
459
                $response = $this->zohoClient->insertRecords($this->getModule(), $xmlData, $wfTrigger, $duplicateCheck, $isApproval);
460
                $records = array_merge($records, $response->getRecords());
461
            }
462 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...
463
            // We can't pass more than 100 records to Zoho, so we split the request into pieces of 100
464
            foreach (array_chunk($beans, 100) as $beanPool) {
465
                $xmlData = $this->toXml($beanPool);
466
                $response = $this->zohoClient->insertRecords($this->getModule(), $xmlData, $wfTrigger, $duplicateCheck, $isApproval);
467
                $records = array_merge($records, $response->getRecords());
468
            }
469
        }
470 View Code Duplication
        if (count($records) != count($beans)) {
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...
471
            throw new ZohoCRMException('Error while inserting beans in Zoho. '.count($beans).' passed in parameter, but '.count($records).' returned.');
472
        }
473
474
        foreach ($beans as $key => $bean) {
475
            $record = $records[$key];
476
477
            if ($wfTrigger && (!isset($record['Id']) || empty($record['Id']))) {
478
                // This field is probably in error!
479
                throw new ZohoCRMException('An error occurred while inserting records and triggering workflow: '.$record['message'], $record['code']);
480
            } elseif (!$wfTrigger && substr($record['code'], 0, 1) != '2') {
0 ignored issues
show
Bug Best Practice introduced by
The expression $wfTrigger of type boolean|null is loosely compared to false; this is ambiguous if the boolean can be false. You might want to explicitly use !== null instead.

If an expression can have both false, and null as possible values. It is generally a good practice to always use strict comparison to clearly distinguish between those two values.

$a = canBeFalseAndNull();

// Instead of
if ( ! $a) { }

// Better use one of the explicit versions:
if ($a !== null) { }
if ($a !== false) { }
if ($a !== null && $a !== false) { }
Loading history...
481
                // This field is probably in error!
482
                throw new ZohoCRMException('An error occurred while inserting records: '.$record['message'], $record['code']);
483
            }
484
485
            $bean->setZohoId($record['Id']);
486
            $bean->setCreatedTime(\DateTime::createFromFormat('Y-m-d H:i:s', $record['Created Time']));
0 ignored issues
show
Security Bug introduced by
It seems like \DateTime::createFromFor...record['Created Time']) targeting DateTime::createFromFormat() can also be of type false; however, Wabel\Zoho\CRM\ZohoBeanInterface::setCreatedTime() does only seem to accept object<DateTime>, did you maybe forget to handle an error condition?
Loading history...
487
            if ($record['Modified Time']) {
488
                $bean->setModifiedTime(\DateTime::createFromFormat('Y-m-d H:i:s', $record['Modified Time']));
0 ignored issues
show
Security Bug introduced by
It seems like \DateTime::createFromFor...ecord['Modified Time']) targeting DateTime::createFromFormat() can also be of type false; however, Wabel\Zoho\CRM\ZohoBeanI...face::setModifiedTime() does only seem to accept object<DateTime>, did you maybe forget to handle an error condition?
Loading history...
489
            }
490
        }
491
    }
492
493
    /**
494
     * Implements updateRecords API method.
495
     *
496
     * @param array $beans     The list of beans to update.
497
     * @param bool  $wfTrigger Set value as true to trigger the workflow rule in Zoho
498
     *
499
     * @return Response The Response object
500
     *
501
     * @throws ZohoCRMException
502
     */
503
    public function updateRecords(array $beans, $wfTrigger = null)
504
    {
505
        $records = [];
506
507
        if ($wfTrigger) {
508
            // If we trigger workflows, we trigger the insert of beans one by one.
509
            foreach ($beans as $bean) {
510
                $xmlData = $this->toXml([$bean]);
511
                $response = $this->zohoClient->updateRecords($this->getModule(), $xmlData, $bean->getZohoId(), $wfTrigger);
512
                $records = array_merge($records, $response->getRecords());
513
            }
514 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...
515
            // We can't pass more than 100 records to Zoho, so we split the request into pieces of 100
516
            foreach (array_chunk($beans, 100) as $beanPool) {
517
                $xmlData = $this->toXml($beanPool);
518
                $response = $this->zohoClient->updateRecords($this->getModule(), $xmlData, null, $wfTrigger);
519
                $records = array_merge($records, $response->getRecords());
520
            }
521
        }
522 View Code Duplication
        if (count($records) != count($beans)) {
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...
523
            throw new ZohoCRMException('Error while inserting beans in Zoho. '.count($beans).' passed in parameter, but '.count($records).' returned.');
524
        }
525
526
        $exceptions = new \SplObjectStorage();
527
528
        foreach ($beans as $key => $bean) {
529
            $record = $records[$key];
530
531
            if ($wfTrigger && (!isset($record['Id']) || empty($record['Id']))) {
532
                // This field is probably in error!
533
                throw new ZohoCRMException('An error occurred while updating records and triggering workflow: '.$record['message'], $record['code']);
534
            } elseif (!$wfTrigger && substr($record['code'], 0, 1) != '2') {
0 ignored issues
show
Bug Best Practice introduced by
The expression $wfTrigger of type boolean|null is loosely compared to false; this is ambiguous if the boolean can be false. You might want to explicitly use !== null instead.

If an expression can have both false, and null as possible values. It is generally a good practice to always use strict comparison to clearly distinguish between those two values.

$a = canBeFalseAndNull();

// Instead of
if ( ! $a) { }

// Better use one of the explicit versions:
if ($a !== null) { }
if ($a !== false) { }
if ($a !== null && $a !== false) { }
Loading history...
535
                // This field is probably in error!
536
                $exceptions->attach($bean, new ZohoCRMException('An error occurred while updating records. '.(isset($record['message']) ? $record['message'] : ''), $record['code']));
537
                continue;
538
            }
539
540
            if ($record['Id'] != $bean->getZohoId()) {
541
                // This field is probably in error!
542
                $exceptions->attach($bean, new ZohoCRMException('An error occurred while updating records. The Zoho ID to update was '.$bean->getZohoId().', returned '.$record['Id']));
543
                continue;
544
            }
545
546
            if ($record['Modified Time']) {
547
                $bean->setModifiedTime(\DateTime::createFromFormat('Y-m-d H:i:s', $record['Modified Time']));
548
            }
549
        }
550
        if ($exceptions->count() != 0) {
551
            throw new ZohoCRMUpdateException($exceptions);
552
        }
553
    }
554
555
    /**
556
     * Implements uploadFile API method.
557
     *
558
     * @param string $id      Zoho Id of the record to retrieve
559
     * @param string $content The string containing the file
560
     *
561
     * @return Response The Response object
562
     *
563
     * @throws ZohoCRMResponseException
564
     */
565
    public function uploadFile($id, $content)
566
    {
567
        return $this->zohoClient->uploadFile($this->getModule(), $id, $content);
568
    }
569
570
    /**
571
     * Implements downloadFile API method.
572
     *
573
     * @param string $id unique ID of the attachment
574
     *
575
     * @return Response The Response object
576
     */
577
    public function downloadFile($id)
578
    {
579
        return $this->zohoClient->downloadFile($this->getModule(), $id);
580
    }
581
582
    /**
583
     * Saves the bean or array of beans passed in Zoho.
584
     * It will perform an insert if the bean has no ZohoID or an update if the bean has a ZohoID.
585
     *
586
     * @param array|object $beans A bean or an array of beans.
587
     *
588
     * TODO: isApproval is not used by each module.
589
     * TODO: wfTrigger only usable for a single record update/insert.
590
     */
591
    public function save($beans, $wfTrigger = false, $duplicateCheck = null, $isApproval = false)
592
    {
593
	// For duplicate check, use the setting passed or the object-wide setting
594
	$duplicateCheck = $duplicateCheck ?: $this->duplicateCheck;
595
596
        if (!is_array($beans)) {
597
            $beans = [$beans];
598
        }
599
600
        $toInsert = [];
601
        $toUpdate = [];
602
603
        foreach ($beans as $bean) {
604
            if ($bean->getZohoId()) {
605
                $toUpdate[] = $bean;
606
            } else {
607
                $toInsert[] = $bean;
608
            }
609
        }
610
611
        if ($toInsert) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $toInsert of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

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.

Loading history...
612
            $this->insertRecords($toInsert, $wfTrigger, $duplicateCheck, $isApproval);
613
        }
614
        if ($toUpdate) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $toUpdate of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

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.

Loading history...
615
            $this->updateRecords($toUpdate, $wfTrigger);
616
        }
617
    }
618
}
619