Completed
Push — develop ( 2d4f17...d5975b )
by Nate
05:25
created

Objects::recordClass()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 0
cts 4
cp 0
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 0
crap 2
1
<?php
2
3
/**
4
 * @copyright  Copyright (c) Flipbox Digital Limited
5
 * @license    https://flipboxfactory.com/software/hubspot/license
6
 * @link       https://www.flipboxfactory.com/software/hubspot/
7
 */
8
9
namespace flipbox\craft\hubspot\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 flipbox\craft\ember\helpers\SiteHelper;
17
use flipbox\craft\hubspot\fields\actions\SyncItemFrom;
18
use flipbox\craft\hubspot\fields\actions\SyncItemTo;
19
use flipbox\craft\hubspot\fields\actions\SyncTo;
20
use flipbox\craft\hubspot\helpers\TransformerHelper;
21
use flipbox\craft\hubspot\HubSpot;
22
use flipbox\craft\hubspot\records\ObjectAssociation;
23
use flipbox\craft\hubspot\transformers\PopulateElementErrorsFromResponse;
24
use flipbox\craft\hubspot\transformers\PopulateElementErrorsFromUpsertResponse;
25
use flipbox\craft\integration\fields\Integrations;
26
use flipbox\craft\integration\queries\IntegrationAssociationQuery;
27
use flipbox\hubspot\connections\ConnectionInterface;
28
use Psr\Http\Message\ResponseInterface;
29
use Psr\SimpleCache\CacheInterface;
30
31
/**
32
 * @author Flipbox Factory <[email protected]>
33
 * @since 1.0.0
34
 */
35
abstract class Objects extends Integrations implements ObjectsFieldInterface
36
{
37
    /**
38
     * @inheritdoc
39
     */
40
    const TRANSLATION_CATEGORY = 'hubspot';
41
42
    /**
43
     * @inheritdoc
44
     */
45
    const INPUT_TEMPLATE_PATH = 'hubspot/_components/fieldtypes/Objects/input';
46
47
    /**
48
     * @inheritdoc
49
     */
50
    const INPUT_ITEM_TEMPLATE_PATH = 'hubspot/_components/fieldtypes/Objects/_inputItem';
51
52
    /**
53
     * @inheritdoc
54
     */
55
    const SETTINGS_TEMPLATE_PATH = 'hubspot/_components/fieldtypes/Objects/settings';
56
57
    /**
58
     * @inheritdoc
59
     */
60
    const ACTION_PREFORM_ACTION_PATH = 'hubspot/cp/fields/perform-action';
61
62
    /**
63
     * @inheritdoc
64
     */
65
    const ACTION_CREATE_ITEM_PATH = 'hubspot/cp/fields/create-item';
66
67
    /**
68
     * @inheritdoc
69
     */
70
    const ACTION_ASSOCIATION_ITEM_PATH = 'hubspot/cp/objects/associate';
71
72
    /**
73
     * @inheritdoc
74
     */
75
    const ACTION_DISSOCIATION_ITEM_PATH = 'hubspot/cp/objects/dissociate';
76
77
    /**
78
     * @inheritdoc
79
     */
80
    const ACTION_PREFORM_ITEM_ACTION_PATH = 'hubspot/cp/fields/perform-item-action';
81
82
    /**
83
     * Indicates whether the full sync operation should be preformed if a matching HubSpot Object was found but not
84
     * currently associated to the element.  For example, when attempting to Sync a Craft User to a HubSpot Contact, if
85
     * the HubSpot Contact already exists; true would override data in HubSpot while false would just perform
86
     * an association (note, a subsequent sync operation could be preformed)
87
     * @var bool
88
     *
89
     * @deprecated
90
     */
91
    public $syncToHubSpotOnMatch = false;
92
93
    /**
94
     * @inheritdoc
95
     */
96
    protected $defaultAvailableActions = [
97
        SyncTo::class
98
    ];
99
100
    /**
101
     * @inheritdoc
102
     */
103
    protected $defaultAvailableItemActions = [
104
        SyncItemFrom::class,
105
        SyncItemTo::class,
106
    ];
107
108
    /**
109
     * @param array $payload
110
     * @param string|null $id
111
     * @return ResponseInterface
112
     */
113
    abstract protected function upsertToHubSpot(
114
        array $payload,
115
        string $id = null
116
    ): ResponseInterface;
117
118
    /**
119
     * @param ResponseInterface $response
120
     * @return string|null
121
     */
122
    abstract protected function getObjectIdFromResponse(ResponseInterface $response);
123
124
    /**
125
     * @inheritdoc
126
     */
127
    public static function recordClass(): string
128
    {
129
        return ObjectAssociation::class;
130
    }
131
132
    /*******************************************
133
     * CONNECTION
134
     *******************************************/
135
136
    /**
137
     * @return ConnectionInterface
138
     * @throws \flipbox\craft\integration\exceptions\ConnectionNotFound
139
     */
140
    public function getConnection(): ConnectionInterface
141
    {
142
        return HubSpot::getInstance()->getConnections()->get();
143
    }
144
145
    /*******************************************
146
     * CACHE
147
     *******************************************/
148
149
    /**
150
     * @return CacheInterface
151
     */
152
    public function getCache(): CacheInterface
153
    {
154
        return HubSpot::getInstance()->getCache()->get();
155
    }
156
157
158
    /*******************************************
159
     * SYNC TO
160
     *******************************************/
161
162
    /**
163
     * @inheritdoc
164
     * @throws \Throwable
165
     */
166
    public function syncToHubSpot(
167
        ElementInterface $element,
168
        string $objectId = null,
169
        $transformer = null
170
    ): bool {
171
        /** @var Element $element */
172
173
        $id = $objectId ?: $this->resolveObjectIdFromElement($element);
174
175
        // Get callable used to create payload
176
        if (null === ($transformer = TransformerHelper::resolveTransformer($transformer))) {
177
            $transformer = HubSpot::getInstance()->getSettings()->getSyncUpsertPayloadTransformer();
178
        }
179
180
        // Create payload
181
        $payload = call_user_func_array(
182
            $transformer,
183
            [
184
                $element,
185
                $this,
186
                $id
187
            ]
188
        );
189
190
        $response = $this->upsertToHubSpot($payload, $id);
191
192
        return $this->handleSyncToHubSpotResponse(
193
            $response,
194
            $element,
195
            $id
196
        );
197
    }
198
199
    /*******************************************
200
     * SYNC FROM
201
     *******************************************/
202
203
    /**
204
     * @@inheritdoc
205
     * @throws \Throwable
206
     * @throws \craft\errors\ElementNotFoundException
207
     * @throws \yii\base\Exception
208
     */
209
    public function syncFromHubSpot(
210
        ElementInterface $element,
211
        string $objectId = null,
212
        $transformer = null
213
    ): bool {
214
215
        $id = $objectId ?: $this->resolveObjectIdFromElement($element);
216
217
        if (null === $id) {
218
            return false;
219
        }
220
221
        $response = $this->readFromHubSpot($id);
222
223
        if (($response->getStatusCode() < 200 || $response->getStatusCode() >= 300)) {
224
            call_user_func_array(
225
                new PopulateElementErrorsFromResponse(),
226
                [
227
                    $response,
228
                    $element,
229
                    $this,
230
                    $id
231
                ]
232
            );
233
            return false;
234
        }
235
236
        // Get callable used to populate element
237
        if (null === ($transformer = TransformerHelper::resolveTransformer($transformer))) {
238
            $transformer = HubSpot::getInstance()->getSettings()->getSyncPopulateElementTransformer();
239
        }
240
241
        // Populate element
242
        call_user_func_array(
243
            $transformer,
244
            [
245
                $response,
246
                $element,
247
                $this,
248
                $id
249
            ]
250
        );
251
252
        if ($objectId !== null) {
253
            $this->addAssociation(
254
                $element,
255
                $id
256
            );
257
        }
258
259
        return Craft::$app->getElements()->saveElement($element);
260
    }
261
262
263
    /**
264
     * @param ElementInterface|Element $element
265
     * @param string $id
266
     * @return bool
267
     * @throws \Throwable
268
     */
269
    protected function addAssociation(
270
        ElementInterface $element,
271
        string $id
272
    ) {
273
        /** @var IntegrationAssociationQuery $query */
274
        if (null === ($query = $element->getFieldValue($this->handle))) {
275
            HubSpot::warning("Field is not available on element.");
276
            return false;
277
        };
278
279
        $associations = ArrayHelper::index($query->all(), 'objectId');
280
281
        if (!array_key_exists($id, $associations)) {
282
            $associations[$id] = $association = new ObjectAssociation([
283
                'element' => $element,
284
                'field' => $this,
285
                'siteId' => SiteHelper::ensureSiteId($element->siteId),
286
                'objectId' => $id
287
            ]);
288
289
            $query->setCachedResult(array_values($associations));
290
291
            return $association->save();
292
        }
293
294
        return true;
295
    }
296
297
    /**
298
     * @param ResponseInterface $response
299
     * @param ElementInterface $element
300
     * @param string|null $objectId
301
     * @return bool
302
     * @throws \Throwable
303
     */
304
    protected function handleSyncToHubSpotResponse(
305
        ResponseInterface $response,
306
        ElementInterface $element,
307
        string $objectId = null
308
    ): bool {
309
310
        /** @var Element $element */
311
312
        if (!($response->getStatusCode() >= 200 && $response->getStatusCode() <= 299)) {
313
            call_user_func_array(
314
                new PopulateElementErrorsFromUpsertResponse(),
315
                [
316
                    $response,
317
                    $element,
318
                    $this,
319
                    $objectId
320
                ]
321
            );
322
            return false;
323
        }
324
325
        if (empty($objectId)) {
326
            if (null === ($objectId = $this->getObjectIdFromResponse($response))) {
327
                HubSpot::error("Unable to determine object id from response");
328
                return false;
329
            };
330
331
            return $this->addAssociation($element, $objectId);
332
        }
333
334
        return true;
335
    }
336
337
    /**
338
     * @param ElementInterface|Element $element
339
     * @return null|string
340
     */
341
    protected function resolveObjectIdFromElement(
342
        ElementInterface $element
343
    ) {
344
345
        if (!$objectId = ObjectAssociation::find()
346
            ->select(['objectId'])
347
            ->elementId($element->getId())
348
            ->fieldId($this->id)
349
            ->siteId(SiteHelper::ensureSiteId($element->siteId))
350
            ->scalar()
351
        ) {
352
            HubSpot::warning(sprintf(
353
                "HubSpot Object Id association was not found for element '%s'",
354
                $element->getId()
355
            ));
356
357
            return null;
358
        }
359
360
        HubSpot::info(sprintf(
361
            "HubSpot Object Id '%s' was found for element '%s'",
362
            $objectId,
363
            $element->getId()
364
        ));
365
366
        return $objectId;
367
    }
368
}
369