Completed
Push — master ( 562d81...f0ef25 )
by Nate
01:30
created

IntegrationField::getInputHtml()   B

Complexity

Conditions 5
Paths 1

Size

Total Lines 46

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 30

Importance

Changes 0
Metric Value
dl 0
loc 46
ccs 0
cts 45
cp 0
rs 8.867
c 0
b 0
f 0
nc 1
cc 5
nop 4
crap 30
1
<?php
2
3
/**
4
 * @copyright  Copyright (c) Flipbox Digital Limited
5
 * @license    https://github.com/flipboxfactory/craft-integration/blob/master/LICENSE
6
 * @link       https://github.com/flipboxfactory/craft-integration/
7
 */
8
9
namespace flipbox\craft\integration\services;
10
11
use Craft;
12
use craft\base\ElementInterface;
13
use craft\base\FieldInterface;
14
use craft\helpers\Component as ComponentHelper;
15
use craft\helpers\StringHelper;
16
use flipbox\craft\integration\db\IntegrationAssociationQuery;
17
use flipbox\craft\integration\events\RegisterIntegrationFieldActionsEvent;
18
use flipbox\craft\integration\fields\actions\IntegrationActionInterface;
19
use flipbox\craft\integration\fields\actions\IntegrationItemActionInterface;
20
use flipbox\craft\integration\fields\Integrations;
21
use flipbox\craft\integration\records\IntegrationAssociation;
22
use flipbox\craft\integration\web\assets\integrations\Integrations as IntegrationsAsset;
23
use flipbox\craft\sortable\associations\db\SortableAssociationQueryInterface;
24
use flipbox\craft\sortable\associations\records\SortableAssociationInterface;
25
use flipbox\craft\sortable\associations\services\SortableFields;
26
use yii\base\Exception;
27
28
/**
29
 * @author Flipbox Factory <[email protected]>
30
 * @since 1.0.0
31
 */
32
abstract class IntegrationField extends SortableFields
33
{
34
    /**
35
     * @inheritdoc
36
     */
37
    const TARGET_ATTRIBUTE = IntegrationAssociation::TARGET_ATTRIBUTE;
38
39
    /**
40
     * @inheritdoc
41
     */
42
    const SOURCE_ATTRIBUTE = IntegrationAssociation::SOURCE_ATTRIBUTE;
43
44
    /**
45
     * @return IntegrationAssociations
46
     */
47
    abstract protected function associationService(): IntegrationAssociations;
48
49
    /**
50
     * @var Integrations[]
51
     */
52
    private $fields = [];
53
54
    /**
55
     * @var IntegrationActionInterface[]
56
     */
57
    protected $defaultAvailableActions = [];
58
59
    /**
60
     * @var IntegrationItemActionInterface[]
61
     */
62
    protected $defaultAvailableItemActions = [];
63
64
    /**
65
     * @param int $id
66
     * @return Integrations|null
67
     */
68
    public function findById(int $id)
69
    {
70
        if (!array_key_exists($id, $this->fields)) {
71
            $objectField = Craft::$app->getFields()->getFieldById($id);
72
            if (!$objectField instanceof Integrations) {
73
                $objectField = null;
74
            }
75
76
            $this->fields[$id] = $objectField;
77
        }
78
79
        return $this->fields[$id];
80
    }
81
82
83
    /**
84
     * @inheritdoc
85
     * @param Integrations $field
86
     * @return IntegrationAssociationQuery
87
     * @throws Exception
88
     */
89
    public function getQuery(
90
        FieldInterface $field,
91
        ElementInterface $element = null
92
    ): SortableAssociationQueryInterface {
93
        $query = $this->baseQuery($field, $element);
94
95
        /** @var Integrations $field */
96
97
        if ($field->max !== null) {
98
            $query->limit($field->max);
99
        }
100
101
        return $query;
102
    }
103
104
    /**
105
     * @param FieldInterface $field
106
     * @param ElementInterface|null $element
107
     * @return IntegrationAssociationQuery
108
     * @throws Exception
109
     */
110
    private function baseQuery(
111
        FieldInterface $field,
112
        ElementInterface $element = null
113
    ): IntegrationAssociationQuery {
114
        if (!$field instanceof Integrations) {
115
            throw new Exception(sprintf(
116
                "The field must be an instance of '%s', '%s' given.",
117
                (string)Integrations::class,
118
                (string)get_class($field)
119
            ));
120
        }
121
122
        /** @var IntegrationAssociationQuery $query */
123
        $query = $this->associationService()->getQuery()
124
            ->field($field->id)
125
            ->site($this->targetSiteId($element));
126
127
        $query->element = $element;
128
129
        return $query;
130
    }
131
132
133
    /*******************************************
134
     * NORMALIZE VALUE
135
     *******************************************/
136
137
    /**
138
     * @inheritdoc
139
     * @throws \Throwable
140
     */
141
    protected function normalizeQueryInputValue(
142
        FieldInterface $field,
143
        $value,
144
        int &$sortOrder,
145
        ElementInterface $element = null
146
    ): SortableAssociationInterface {
147
        if (!$field instanceof Integrations) {
148
            throw new Exception(sprintf(
149
                "The field must be an instance of '%s', '%s' given.",
150
                (string)Integrations::class,
151
                (string)get_class($field)
152
            ));
153
        }
154
155
        if (is_array($value)) {
156
            $value = StringHelper::toString($value);
157
        }
158
159
        return $this->associationService()->create(
160
            [
161
                'field' => $field,
162
                'element' => $element,
163
                'objectId' => $value,
164
                'siteId' => $this->targetSiteId($element),
165
                'sortOrder' => $sortOrder++
166
            ]
167
        );
168
    }
169
170
    /**
171
     * @param Integrations $field
172
     * @param IntegrationAssociationQuery $query
173
     * @param ElementInterface|null $element
174
     * @param bool $static
175
     * @return null|string
176
     * @throws Exception
177
     * @throws \Twig_Error_Loader
178
     */
179
    public function getInputHtml(
180
        Integrations $field,
181
        IntegrationAssociationQuery $query,
182
        ElementInterface $element = null,
183
        bool $static
184
    ) {
185
        Craft::$app->getView()->registerAssetBundle(IntegrationsAsset::class);
186
187
        return Craft::$app->getView()->renderTemplate(
188
            $field::INPUT_TEMPLATE_PATH,
189
            [
190
                'field' => $field,
191
                'element' => $element,
192
                'value' => $query,
193
                'objectLabel' => $this->getObjectLabel($field),
194
                'static' => $static,
195
                'itemTemplate' => $field::INPUT_ITEM_TEMPLATE_PATH,
196
                'settings' => [
197
                    'translationCategory' => $field::TRANSLATION_CATEGORY,
198
                    'limit' => $field->max ? $field->max : null,
199
                    'data' => [
200
                        'field' => $field->id,
201
                        'element' => $element ? $element->getId() : null
202
                    ],
203
                    'actions' => $this->getActionHtml($field, $element),
204
                    'actionAction' => $field::ACTION_PREFORM_ACTION_PATH,
205
                    'createItemAction' => $field::ACTION_CREATE_ITEM_PATH,
206
                    'itemData' => [
207
                        'field' => $field->id,
208
                        'element' => $element ? $element->getId() : null
209
                    ],
210
                    'itemSettings' => [
211
                        'translationCategory' => $field::TRANSLATION_CATEGORY,
212
                        'actionAction' => $field::ACTION_PREFORM_ITEM_ACTION_PATH,
213
                        'associateAction' => $field::ACTION_ASSOCIATION_ITEM_PATH,
214
                        'dissociateAction' => $field::ACTION_DISSOCIATION_ITEM_PATH,
215
                        'data' => [
216
                            'field' => $field->id,
217
                            'element' => $element ? $element->getId() : null
218
                        ],
219
                        'actions' => $this->getItemActionHtml($field, $element),
220
                    ]
221
                ]
222
            ]
223
        );
224
    }
225
226
    /**
227
     * @param Integrations $field
228
     * @return null|string
229
     * @throws Exception
230
     * @throws \Twig_Error_Loader
231
     */
232
    public function getSettingsHtml(
233
        Integrations $field
234
    ) {
235
        return Craft::$app->getView()->renderTemplate(
236
            $field::SETTINGS_TEMPLATE_PATH,
237
            [
238
                'field' => $field,
239
                'availableActions' => $this->getAvailableActions($field),
240
                'availableItemActions' => $this->getAvailableItemActions($field),
241
                'translationCategory' => $field::TRANSLATION_CATEGORY,
242
            ]
243
        );
244
    }
245
246
247
    /*******************************************
248
     * OBJECT
249
     *******************************************/
250
251
    /**
252
     * @param Integrations $field
253
     * @return string
254
     */
255
    public function getObjectLabel(Integrations $field): string
256
    {
257
        return StringHelper::titleize($field->object);
258
    }
259
260
    /*******************************************
261
     * ACTIONS
262
     *******************************************/
263
264
    /**
265
     * @param Integrations $field
266
     * @return IntegrationActionInterface[]
267
     * @throws \craft\errors\MissingComponentException
268
     * @throws \yii\base\InvalidConfigException
269
     */
270
    public function getAvailableActions(Integrations $field): array
271
    {
272
        $event = new RegisterIntegrationFieldActionsEvent([
273
            'actions' => $this->defaultAvailableActions
274
        ]);
275
276
        $field->trigger(
277
            $field::EVENT_REGISTER_AVAILABLE_ACTIONS,
278
            $event
279
        );
280
281
        return $this->resolveActions(
282
            array_filter((array)$event->actions),
283
            IntegrationActionInterface::class
284
        );
285
    }
286
287
    /**
288
     * @param Integrations $field
289
     * @param ElementInterface|null $element
290
     * @return IntegrationActionInterface[]
291
     * @throws \craft\errors\MissingComponentException
292
     * @throws \yii\base\InvalidConfigException
293
     */
294
    public function getActions(Integrations $field, ElementInterface $element = null): array
295
    {
296
        $event = new RegisterIntegrationFieldActionsEvent([
297
            'actions' => $field->selectedActions,
298
            'element' => $element
299
        ]);
300
301
        $field->trigger(
302
            $field::EVENT_REGISTER_ACTIONS,
303
            $event
304
        );
305
306
        return $this->resolveActions(
307
            array_filter((array)$event->actions),
308
            IntegrationActionInterface::class
309
        );
310
    }
311
312
    /**
313
     * @param Integrations $field
314
     * @return IntegrationActionInterface[]
315
     * @throws \craft\errors\MissingComponentException
316
     * @throws \yii\base\InvalidConfigException
317
     */
318
    public function getAvailableItemActions(Integrations $field): array
319
    {
320
        $event = new RegisterIntegrationFieldActionsEvent([
321
            'actions' => $this->defaultAvailableItemActions
322
        ]);
323
324
        $field->trigger(
325
            $field::EVENT_REGISTER_AVAILABLE_ITEM_ACTIONS,
326
            $event
327
        );
328
329
        return $this->resolveActions(
330
            array_filter((array)$event->actions),
331
            IntegrationItemActionInterface::class
332
        );
333
    }
334
335
    /**
336
     * @param Integrations $field
337
     * @param ElementInterface|null $element
338
     * @return IntegrationItemActionInterface[]
339
     * @throws \craft\errors\MissingComponentException
340
     * @throws \yii\base\InvalidConfigException
341
     */
342
    public function getItemActions(Integrations $field, ElementInterface $element = null): array
343
    {
344
        $event = new RegisterIntegrationFieldActionsEvent([
345
            'actions' => $field->selectedItemActions,
346
            'element' => $element
347
        ]);
348
349
        $field->trigger(
350
            $field::EVENT_REGISTER_ITEM_ACTIONS,
351
            $event
352
        );
353
354
        return $this->resolveActions(
355
            array_filter((array)$event->actions),
356
            IntegrationItemActionInterface::class
357
        );
358
    }
359
360
    /**
361
     * @param array $actions
362
     * @param string $instance
363
     * @return array
364
     * @throws \craft\errors\MissingComponentException
365
     * @throws \yii\base\InvalidConfigException
366
     */
367
    protected function resolveActions(array $actions, string $instance)
368
    {
369
        foreach ($actions as $i => $action) {
370
            // $action could be a string or config array
371
            if (!$action instanceof $instance) {
372
                $actions[$i] = $action = ComponentHelper::createComponent($action, $instance);
373
374
                if ($actions[$i] === null) {
375
                    unset($actions[$i]);
376
                }
377
            }
378
        }
379
380
        return array_values($actions);
381
    }
382
383
    /**
384
     * @param Integrations $field
385
     * @param ElementInterface|null $element
386
     * @return array
387
     * @throws \craft\errors\MissingComponentException
388
     * @throws \yii\base\InvalidConfigException
389
     */
390
    protected function getActionHtml(Integrations $field, ElementInterface $element = null): array
391
    {
392
        $actionData = [];
393
394
        foreach ($this->getActions($field, $element) as $action) {
395
            $actionData[] = [
396
                'type' => get_class($action),
397
                'destructive' => $action->isDestructive(),
398
                'name' => $action->getTriggerLabel(),
399
                'trigger' => $action->getTriggerHtml(),
400
                'confirm' => $action->getConfirmationMessage(),
401
            ];
402
        }
403
404
        return $actionData;
405
    }
406
407
    /**
408
     * @param Integrations $field
409
     * @param ElementInterface|null $element
410
     * @return array
411
     * @throws \craft\errors\MissingComponentException
412
     * @throws \yii\base\InvalidConfigException
413
     */
414
    protected function getItemActionHtml(Integrations $field, ElementInterface $element = null): array
415
    {
416
        $actionData = [];
417
418
        foreach ($this->getItemActions($field, $element) as $action) {
419
            $actionData[] = [
420
                'type' => get_class($action),
421
                'destructive' => $action->isDestructive(),
422
                'name' => $action->getTriggerLabel(),
423
                'trigger' => $action->getTriggerHtml(),
424
                'confirm' => $action->getConfirmationMessage(),
425
            ];
426
        }
427
428
        return $actionData;
429
    }
430
}
431