Completed
Push — master ( 3407e1...79fe03 )
by Nate
07:26
created

Objects::syncFromSalesforce()   B

Complexity

Conditions 7
Paths 12

Size

Total Lines 52

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 56

Importance

Changes 0
Metric Value
dl 0
loc 52
ccs 0
cts 42
cp 0
rs 8.1138
c 0
b 0
f 0
cc 7
nc 12
nop 3
crap 56

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
/**
4
 * @copyright  Copyright (c) Flipbox Digital Limited
5
 * @license    https://flipboxfactory.com/software/force/license
6
 * @link       https://www.flipboxfactory.com/software/force/
7
 */
8
9
namespace flipbox\craft\salesforce\fields;
10
11
use Craft;
12
use craft\base\Element;
13
use craft\base\ElementInterface;
14
use craft\helpers\ArrayHelper;
15
use craft\helpers\Json;
16
use craft\helpers\StringHelper;
17
use flipbox\craft\ember\helpers\SiteHelper;
18
use flipbox\craft\integration\fields\Integrations;
19
use flipbox\craft\integration\queries\IntegrationAssociationQuery;
20
use flipbox\craft\salesforce\criteria\ObjectCriteria;
21
use flipbox\craft\salesforce\fields\actions\SyncItemFrom;
22
use flipbox\craft\salesforce\fields\actions\SyncItemTo;
23
use flipbox\craft\salesforce\fields\actions\SyncTo;
24
use flipbox\craft\salesforce\Force;
25
use flipbox\craft\salesforce\helpers\TransformerHelper;
26
use flipbox\craft\salesforce\records\ObjectAssociation;
27
use flipbox\craft\salesforce\transformers\PopulateElementErrorsFromResponse;
28
use flipbox\craft\salesforce\transformers\PopulateElementErrorsFromUpsertResponse;
29
use Flipbox\Salesforce\Connections\ConnectionInterface;
30
use Psr\Http\Message\ResponseInterface;
31
use Psr\SimpleCache\CacheInterface;
32
33
/**
34
 * @author Flipbox Factory <[email protected]>
35
 * @since 1.0.0
36
 */
37
class Objects extends Integrations
38
{
39
    /**
40
     * The Plugin's translation category
41
     */
42
    const TRANSLATION_CATEGORY = 'salesforce';
43
44
    /**
45
     * @inheritdoc
46
     */
47
    const INPUT_TEMPLATE_PATH = 'salesforce/_components/fieldtypes/Objects/input';
48
49
    /**
50
     * @inheritdoc
51
     */
52
    const INPUT_ITEM_TEMPLATE_PATH = 'salesforce/_components/fieldtypes/Objects/_inputItem';
53
54
    /**
55
     * @inheritdoc
56
     */
57
    const SETTINGS_TEMPLATE_PATH = 'salesforce/_components/fieldtypes/Objects/settings';
58
59
    /**
60
     * @inheritdoc
61
     */
62
    const ACTION_PREFORM_ACTION_PATH = 'salesforce/cp/fields/perform-action';
63
64
    /**
65
     * @inheritdoc
66
     */
67
    const ACTION_CREATE_ITEM_PATH = 'salesforce/cp/fields/create-item';
68
69
    /**
70
     * @inheritdoc
71
     */
72
    const ACTION_ASSOCIATION_ITEM_PATH = 'salesforce/cp/objects/associate';
73
74
    /**
75
     * @inheritdoc
76
     */
77
    const ACTION_DISSOCIATION_ITEM_PATH = 'salesforce/cp/objects/dissociate';
78
79
    /**
80
     * @inheritdoc
81
     */
82
    const ACTION_PREFORM_ITEM_ACTION_PATH = 'salesforce/cp/fields/perform-item-action';
83
84
    /**
85
     * @var string
86
     */
87
    public $object;
88
89
    /**
90
     * @inheritdoc
91
     */
92
    protected $defaultAvailableActions = [
93
        SyncTo::class
94
    ];
95
96
    /**
97
     * @inheritdoc
98
     */
99
    protected $defaultAvailableItemActions = [
100
        SyncItemFrom::class,
101
        SyncItemTo::class,
102
    ];
103
104
105
    /*******************************************
106
     * OBJECT
107
     *******************************************/
108
109
    /**
110
     * @return string
111
     */
112
    public function getObject(): string
113
    {
114
        return (string)$this->object ?: '';
115
    }
116
117
    /**
118
     * @return string
119
     */
120
    public function getObjectLabel(): string
121
    {
122
        return StringHelper::titleize($this->object);
123
    }
124
125
126
    /*******************************************
127
     * RULES
128
     *******************************************/
129
130
    /**
131
     * @inheritdoc
132
     */
133
    public function rules()
134
    {
135
        return array_merge(
136
            parent::rules(),
137
            [
138
                [
139
                    'object',
140
                    'required',
141
                    'message' => Craft::t(static::TRANSLATION_CATEGORY, 'Object cannot be empty.')
142
                ],
143
                [
144
                    [
145
                        'object',
146
                    ],
147
                    'safe',
148
                    'on' => [
149
                        self::SCENARIO_DEFAULT
150
                    ]
151
                ]
152
            ]
153
        );
154
    }
155
156
157
    /*******************************************
158
     * SETTINGS
159
     *******************************************/
160
161
    /**
162
     * @inheritdoc
163
     */
164
    public function settingsAttributes(): array
165
    {
166
        return array_merge(
167
            [
168
                'object'
169
            ],
170
            parent::settingsAttributes()
171
        );
172
    }
173
174
    /**
175
     * @inheritdoc
176
     */
177
    public static function recordClass(): string
178
    {
179
        return ObjectAssociation::class;
180
    }
181
182
    /**
183
     * @inheritdoc
184
     */
185
    public static function displayName(): string
186
    {
187
        return Craft::t('salesforce', 'Salesforce Objects');
188
    }
189
190
    /**
191
     * @inheritdoc
192
     */
193
    public static function defaultSelectionLabel(): string
194
    {
195
        return Craft::t('salesforce', 'Add a Salesforce Object');
196
    }
197
198
199
    /*******************************************
200
     * SALESFORCE
201
     *******************************************/
202
203
    /**
204
     * @param string $id
205
     * @return ResponseInterface
206
     */
207
    public function readFromSalesforce(
208
        string $id
209
    ): ResponseInterface {
210
211
        return (new ObjectCriteria([
212
            'connection' => $this->getConnection(),
213
            'cache' => $this->getCache(),
214
            'id' => $id
215
        ]))->read();
216
    }
217
218
219
    /*******************************************
220
     * CONNECTION
221
     *******************************************/
222
223
    /**
224
     * @return ConnectionInterface
225
     * @throws \flipbox\craft\integration\exceptions\ConnectionNotFound
226
     */
227
    public function getConnection(): ConnectionInterface
228
    {
229
        return Force::getInstance()->getConnections()->get();
230
    }
231
232
233
    /*******************************************
234
     * CACHE
235
     *******************************************/
236
237
    /**
238
     * @return CacheInterface
239
     */
240
    public function getCache(): CacheInterface
241
    {
242
        return Force::getInstance()->getCache()->get();
243
    }
244
245
246
    /*******************************************
247
     * SYNC TO
248
     *******************************************/
249
250
    /**
251
     * @inheritdoc
252
     * @throws \Throwable
253
     */
254
    public function syncToSalesforce(
255
        ElementInterface $element,
256
        string $objectId = null,
257
        $transformer = null
258
    ): bool {
259
        /** @var Element $element */
260
261
        $id = $objectId ?: $this->resolveObjectIdFromElement($element);
262
263
        // Get callable used to create payload
264
        if (null === ($transformer = TransformerHelper::resolveTransformer($transformer))) {
265
            $transformer = Force::getInstance()->getSettings()->getSyncUpsertPayloadTransformer();
266
        }
267
268
        // Create payload
269
        $payload = call_user_func_array(
270
            $transformer,
271
            [
272
                $element,
273
                $this,
274
                $id
275
            ]
276
        );
277
278
        $response = (new ObjectCriteria([
279
            'connection' => $this->getConnection(),
280
            'cache' => $this->getCache(),
281
            'payload' => $payload,
282
            'id' => $id
283
        ]))->upsert();
284
285
        return $this->handleSyncToSalesforceResponse(
286
            $response,
287
            $element,
288
            $id
289
        );
290
    }
291
292
293
    /*******************************************
294
     * SYNC FROM
295
     *******************************************/
296
297
    /**
298
     * @@inheritdoc
299
     * @throws \Throwable
300
     * @throws \craft\errors\ElementNotFoundException
301
     * @throws \yii\base\Exception
302
     */
303
    public function syncFromSalesforce(
304
        ElementInterface $element,
305
        string $objectId = null,
306
        $transformer = null
307
    ): bool {
308
309
        $id = $objectId ?: $this->resolveObjectIdFromElement($element);
310
311
        if (null === $id) {
312
            return false;
313
        }
314
315
        $response = $this->readFromSalesforce($id);
316
317
        if (($response->getStatusCode() < 200 || $response->getStatusCode() >= 300)) {
318
            call_user_func_array(
319
                new PopulateElementErrorsFromResponse(),
320
                [
321
                    $response,
322
                    $element,
323
                    $this,
324
                    $id
325
                ]
326
            );
327
            return false;
328
        }
329
330
        // Get callable used to populate element
331
        if (null === ($transformer = TransformerHelper::resolveTransformer($transformer))) {
332
            $transformer = Force::getInstance()->getSettings()->getSyncPopulateElementTransformer();
333
        }
334
335
        // Populate element
336
        call_user_func_array(
337
            $transformer,
338
            [
339
                $response,
340
                $element,
341
                $this,
342
                $id
343
            ]
344
        );
345
346
        if ($objectId !== null) {
347
            $this->addAssociation(
348
                $element,
349
                $id
350
            );
351
        }
352
353
        return Craft::$app->getElements()->saveElement($element);
354
    }
355
356
    /**
357
     * @param ElementInterface|Element $element
358
     * @param string $id
359
     * @return bool
360
     * @throws \Throwable
361
     */
362
    protected function addAssociation(
363
        ElementInterface $element,
364
        string $id
365
    ) {
366
        /** @var IntegrationAssociationQuery $query */
367
        if (null === ($query = $element->getFieldValue($this->handle))) {
368
            Force::warning("Field is not available on element.");
369
            return false;
370
        };
371
372
        $associations = ArrayHelper::index($query->all(), 'objectId');
373
374
        if (!array_key_exists($id, $associations)) {
375
            $associations[$id] = $association = new ObjectAssociation([
376
                'element' => $element,
377
                'field' => $this,
378
                'siteId' => SiteHelper::ensureSiteId($element->siteId),
379
                'objectId' => $id
380
            ]);
381
382
            $query->setCachedResult(array_values($associations));
383
384
            return $association->save();
385
        }
386
387
        return true;
388
    }
389
390
    /**
391
     * @param ResponseInterface|Element $response
392
     * @param ElementInterface $element
393
     * @param string|null $objectId
394
     * @return bool
395
     * @throws \Throwable
396
     */
397
    protected function handleSyncToSalesforceResponse(
398
        ResponseInterface $response,
399
        ElementInterface $element,
400
        string $objectId = null
401
    ): bool {
402
403
        if (!($response->getStatusCode() >= 200 && $response->getStatusCode() <= 299)) {
404
            call_user_func_array(
405
                new PopulateElementErrorsFromUpsertResponse(),
406
                [
407
                    $response,
408
                    $element,
409
                    $this,
410
                    $objectId
411
                ]
412
            );
413
            return false;
414
        }
415
416
        if (empty($objectId)) {
417
            if (null === ($objectId = $this->getObjectIdFromResponse($response))) {
418
                Force::error("Unable to determine object id from response");
419
                return false;
420
            };
421
422
            return $this->addAssociation($element, $objectId);
423
        }
424
425
        return true;
426
    }
427
428
    /**
429
     * @param ResponseInterface $response
430
     * @return string|null
431
     */
432
    protected function getObjectIdFromResponse(ResponseInterface $response)
433
    {
434
        $data = Json::decodeIfJson(
435
            $response->getBody()->getContents()
436
        );
437
438
        $id = $data['Id'] ?? ($data['id'] ?? null);
439
440
        return $id ? (string)$id : null;
441
    }
442
443
    /**
444
     * @param ElementInterface|Element $element
445
     * @return null|string
446
     */
447
    protected function resolveObjectIdFromElement(
448
        ElementInterface $element
449
    ) {
450
451
        if (!$objectId = ObjectAssociation::find()
452
            ->select(['objectId'])
453
            ->elementId($element->getId())
454
            ->fieldId($this->id)
455
            ->siteId(SiteHelper::ensureSiteId($element->siteId))
456
            ->scalar()
457
        ) {
458
            Force::warning(sprintf(
459
                "Salesforce Object Id association was not found for element '%s'",
460
                $element->getId()
461
            ));
462
463
            return null;
464
        }
465
466
        Force::info(sprintf(
467
            "Salesforce Object Id '%s' was found for element '%s'",
468
            $objectId,
469
            $element->getId()
470
        ));
471
472
        return $objectId;
473
    }
474
}
475