Completed
Push — master ( b27204...a2c64d )
by Nate
05:15 queued 02:48
created

Organization   F

Complexity

Total Complexity 61

Size/Duplication

Total Lines 561
Duplicated Lines 0 %

Coupling/Cohesion

Components 3
Dependencies 18

Test Coverage

Coverage 0%

Importance

Changes 0
Metric Value
wmc 61
lcom 3
cbo 18
dl 0
loc 561
ccs 0
cts 323
cp 0
rs 3.52
c 0
b 0
f 0

33 Methods

Rating   Name   Duplication   Size   Complexity  
A displayName() 0 4 1
A getIsEditable() 0 4 1
A hasTitles() 0 4 1
A isLocalized() 0 4 1
A hasContent() 0 4 1
A hasUris() 0 4 1
A hasStatuses() 0 4 1
A find() 0 4 1
A findByCondition() 0 12 3
A attributes() 0 7 1
A rules() 0 22 1
A attributeLabels() 0 7 1
A getFieldLayout() 0 8 2
A getRef() 0 8 2
A getCpEditUrl() 0 4 1
A defineSources() 0 10 2
A defineDefaultSources() 0 11 1
A defineTypeSources() 0 21 2
A defineUserSources() 0 24 2
A defineActions() 0 17 1
A defineSearchableAttributes() 0 6 1
A defineSortOptions() 0 7 1
A eagerLoadingMap() 0 9 2
A setEagerLoadedElements() 0 12 2
A defineTableAttributes() 0 11 1
A tableAttributeHtml() 0 19 4
A getUriFormat() 0 12 3
A route() 0 28 4
A getSiteSettings() 0 22 3
A beforeSave() 0 8 2
A afterSave() 0 18 4
A saveRecord() 0 30 5
A elementToRecord() 0 14 2

How to fix   Complexity   

Complex Class

Complex classes like Organization often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Organization, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
/**
4
 * @copyright  Copyright (c) Flipbox Digital Limited
5
 * @license    https://flipboxfactory.com/software/organization/license
6
 * @link       https://www.flipboxfactory.com/software/organization/
7
 */
8
9
namespace flipbox\organizations\elements;
10
11
use Craft;
12
use craft\base\Element;
13
use craft\elements\actions\Edit as EditAction;
14
use craft\elements\db\ElementQueryInterface;
15
use craft\elements\User;
16
use craft\helpers\DateTimeHelper;
17
use craft\helpers\Json;
18
use craft\helpers\StringHelper;
19
use craft\helpers\UrlHelper as UrlHelper;
20
use flipbox\craft\ember\elements\ExplicitElementTrait;
21
use flipbox\craft\ember\helpers\ModelHelper;
22
use flipbox\organizations\models\DateJoinedAttributeTrait;
23
use flipbox\organizations\Organizations as OrganizationPlugin;
24
use flipbox\organizations\queries\OrganizationQuery;
25
use flipbox\organizations\records\Organization as OrganizationRecord;
26
use flipbox\organizations\records\OrganizationType;
27
use flipbox\organizations\records\OrganizationType as TypeModel;
28
use yii\base\ErrorException as Exception;
29
30
/**
31
 * @author Flipbox Factory <[email protected]>
32
 * @since 1.0.0
33
 *
34
 * @method static Organization findOne($criteria = null)
35
 * @method static Organization[] findAll($criteria = null) : array
36
 */
37
class Organization extends Element
38
{
39
    use ExplicitElementTrait,
40
        DateJoinedAttributeTrait,
41
        TypesAttributeTrait,
42
        UsersAttributeTrait;
43
44
    /**
45
     * @inheritdoc
46
     */
47
    public static function displayName(): string
48
    {
49
        return Craft::t('organizations', 'Organization');
50
    }
51
52
    /**
53
     * @inheritdoc
54
     */
55
    public function getIsEditable(): bool
56
    {
57
        return true;
58
    }
59
60
    /**
61
     * @inheritdoc
62
     */
63
    public static function hasTitles(): bool
64
    {
65
        return true;
66
    }
67
68
    /**
69
     * @inheritdoc
70
     */
71
    public static function isLocalized(): bool
72
    {
73
        return true;
74
    }
75
76
    /**
77
     * @inheritdoc
78
     */
79
    public static function hasContent(): bool
80
    {
81
        return true;
82
    }
83
84
    /**
85
     * @inheritdoc
86
     */
87
    public static function hasUris(): bool
88
    {
89
        return true;
90
    }
91
92
    /**
93
     * Returns whether this element type can have statuses.
94
     *
95
     * @return boolean
96
     */
97
    public static function hasStatuses(): bool
98
    {
99
        return true;
100
    }
101
102
103
    /************************************************************
104
     * QUERY
105
     ************************************************************/
106
107
    /**
108
     * @inheritdoc
109
     *
110
     * @return OrganizationQuery
111
     */
112
    public static function find(): ElementQueryInterface
113
    {
114
        return new OrganizationQuery(static::class);
115
    }
116
117
    /**
118
     * @param mixed $criteria
119
     * @param bool $one
120
     * @return Element|Element[]|null
121
     */
122
    protected static function findByCondition($criteria, bool $one)
123
    {
124
        if (is_numeric($criteria)) {
125
            $criteria = ['id' => $criteria];
126
        }
127
128
        if (is_string($criteria)) {
129
            $criteria = ['slug' => $criteria];
130
        }
131
132
        return parent::findByCondition($criteria, $one);
133
    }
134
135
136
    /************************************************************
137
     * PROPERTIES / ATTRIBUTES
138
     ************************************************************/
139
140
    /**
141
     * @return array
142
     */
143
    public function attributes()
144
    {
145
        return array_merge(
146
            parent::attributes(),
147
            $this->dateJoinedAttributes()
148
        );
149
    }
150
151
    /**
152
     * @inheritdoc
153
     * @throws \yii\base\InvalidConfigException
154
     */
155
    public function rules()
156
    {
157
        return array_merge(
158
            parent::rules(),
159
            $this->dateJoinedRules(),
160
            [
161
                [
162
                    [
163
                        'types',
164
                        'activeType',
165
                        'primaryType',
166
                        'users',
167
                    ],
168
                    'safe',
169
                    'on' => [
170
                        ModelHelper::SCENARIO_DEFAULT
171
                    ]
172
173
                ],
174
            ]
175
        );
176
    }
177
178
    /**
179
     * @return array
180
     */
181
    public function attributeLabels()
182
    {
183
        return array_merge(
184
            parent::attributeLabels(),
185
            $this->dateJoinedAttributeLabels()
186
        );
187
    }
188
189
190
    /************************************************************
191
     * FIELD LAYOUT
192
     ************************************************************/
193
194
    /**
195
     * @inheritdoc
196
     */
197
    public function getFieldLayout()
198
    {
199
        if (null === ($type = $this->getActiveType())) {
200
            return OrganizationPlugin::getInstance()->getSettings()->getFieldLayout();
201
        }
202
203
        return $type->getFieldLayout();
204
    }
205
206
207
    /************************************************************
208
     * ELEMENT ADMIN
209
     ************************************************************/
210
211
    /**
212
     * @inheritdoc
213
     */
214
    public function getRef()
215
    {
216
        if (!$primary = $this->getPrimaryType()) {
217
            return $this->slug;
218
        }
219
220
        return $primary->handle . '/' . $this->slug;
221
    }
222
223
    /**
224
     * @inheritdoc
225
     */
226
    public function getCpEditUrl()
227
    {
228
        return UrlHelper::cpUrl('organizations/' . $this->id);
229
    }
230
231
    /**
232
     * @inheritdoc
233
     */
234
    protected static function defineSources(string $context = null): array
235
    {
236
        switch ($context) {
237
            case 'user':
238
                return self::defineUserSources();
239
240
            default:
241
                return self::defineTypeSources();
242
        }
243
    }
244
245
    /**
246
     * @return array
247
     */
248
    private static function defineDefaultSources(): array
249
    {
250
        return [
251
            [
252
                'key' => '*',
253
                'label' => Craft::t('organizations', 'All organizations'),
254
                'criteria' => ['status' => null],
255
                'hasThumbs' => true
256
            ]
257
        ];
258
    }
259
260
    /**
261
     * @return array
262
     */
263
    private static function defineTypeSources(): array
264
    {
265
        $sources = self::defineDefaultSources();
266
267
        // Array of all organization types
268
        $organizationTypes = OrganizationType::findAll([]);
269
270
        $sources[] = ['heading' => Craft::t('organizations', 'Types')];
271
272
        /** @var TypeModel $organizationType */
273
        foreach ($organizationTypes as $organizationType) {
274
            $sources[] = [
275
                'key' => 'type:' . $organizationType->id,
276
                'label' => $organizationType->name,
277
                'criteria' => ['status' => null, 'typeId' => $organizationType->id],
278
                'hasThumbs' => true
279
            ];
280
        }
281
282
        return $sources;
283
    }
284
285
    /**
286
     * @return array
287
     */
288
    private static function defineUserSources(): array
289
    {
290
        $sources = self::defineDefaultSources();
291
292
        // Array of all organization types
293
        $organizationUsers = User::find();
294
295
        $sources[] = ['heading' => Craft::t('organizations', 'Users')];
296
297
        /** @var User $organizationUser */
298
        foreach ($organizationUsers as $organizationUser) {
299
            $sources[] = [
300
                'key' => 'user:' . $organizationUser->id,
301
                'label' => $organizationUser->getFullName(),
302
                'criteria' => [
303
                    'status' => null,
304
                    'users' => [$organizationUser->id]
305
                ],
306
                'hasThumbs' => true
307
            ];
308
        }
309
310
        return $sources;
311
    }
312
313
    /**
314
     * @inheritdoc
315
     */
316
    protected static function defineActions(string $source = null): array
317
    {
318
        $actions = [];
319
320
        // Edit
321
        $actions[] = Craft::$app->getElements()->createAction([
322
            'type' => EditAction::class,
323
            'label' => Craft::t('organizations', 'Edit organization'),
324
        ]);
325
326
//        if (Craft::$app->getUser()->checkPermission('deleteOrganizations')) {
327
//            // Delete Organization
328
//            $actions[] = DeleteAction::class;
329
//        }
330
331
        return $actions;
332
    }
333
334
    /**
335
     * @inheritdoc
336
     */
337
    protected static function defineSearchableAttributes(): array
338
    {
339
        return [
340
            'id'
341
        ];
342
    }
343
344
    /**
345
     * @inheritdoc
346
     */
347
    protected static function defineSortOptions(): array
348
    {
349
        return [
350
            'title' => Craft::t('organizations', 'Name'),
351
            'dateJoined' => Craft::t('organizations', 'Join Date'),
352
        ];
353
    }
354
355
    /**
356
     * @inheritdoc
357
     */
358
    public static function eagerLoadingMap(array $sourceElements, string $handle)
359
    {
360
        switch ($handle) {
361
            case 'users':
362
                return self::eagerLoadingUsersMap($sourceElements);
363
        }
364
365
        return parent::eagerLoadingMap($sourceElements, $handle);
366
    }
367
368
    /**
369
     * @inheritdoc
370
     */
371
    public function setEagerLoadedElements(string $handle, array $elements)
372
    {
373
        switch ($handle) {
374
            case 'users':
375
                $users = $elements ?? [];
376
                $this->setUsers($users);
377
                break;
378
379
            default:
380
                parent::setEagerLoadedElements($handle, $elements);
381
        }
382
    }
383
384
    /**
385
     * @inheritdoc
386
     */
387
    public static function defineTableAttributes(): array
388
    {
389
        return [
390
            'id' => ['label' => Craft::t('app', 'Name')],
391
            'uri' => ['label' => Craft::t('app', 'URI')],
392
            'types' => ['label' => Craft::t('organizations', 'Type(s)')],
393
            'dateJoined' => ['label' => Craft::t('organizations', 'Join Date')],
394
            'dateCreated' => ['label' => Craft::t('app', 'Date Created')],
395
            'dateUpdated' => ['label' => Craft::t('app', 'Date Updated')],
396
        ];
397
    }
398
399
400
401
    // Indexes, etc.
402
    // -------------------------------------------------------------------------
403
404
    /**
405
     * @inheritdoc
406
     */
407
    public function tableAttributeHtml(string $attribute): string
408
    {
409
410
        switch ($attribute) {
411
            case 'types':
412
                $typeHtmlParts = [];
413
                foreach ($this->getTypes()->all() as $type) {
414
                    $typeHtmlParts[] = '<a href="' .
415
                        UrlHelper::cpUrl('organizations/' . $this->id . '/' . $type->handle) .
416
                        '">' .
417
                        $type->name .
418
                        '</a>';
419
                }
420
421
                return !empty($typeHtmlParts) ? StringHelper::toString($typeHtmlParts, ', ') : '';
422
        }
423
424
        return parent::tableAttributeHtml($attribute);
425
    }
426
427
    /**
428
     * @inheritdoc
429
     */
430
    public function getUriFormat()
431
    {
432
        if (null === ($siteSettings = $this->getSiteSettings())) {
433
            return null;
434
        }
435
436
        if (!$siteSettings->hasUrls()) {
437
            return null;
438
        }
439
440
        return $siteSettings->getUriFormat();
441
    }
442
443
    /**
444
     * @inheritdoc
445
     */
446
    public function route()
447
    {
448
        if (in_array(
449
            $this->getStatus(),
450
            [static::STATUS_DISABLED, static::STATUS_ARCHIVED],
451
            true
452
        )) {
453
            return null;
454
        }
455
456
        if (null === ($siteSettings = $this->getSiteSettings())) {
457
            return null;
458
        }
459
460
        if (!$siteSettings->hasUrls()) {
461
            return null;
462
        }
463
464
        return [
465
            'templates/render',
466
            [
467
                'template' => $siteSettings->getTemplate(),
468
                'variables' => [
469
                    'organization' => $this,
470
                ]
471
            ]
472
        ];
473
    }
474
475
    /**
476
     * @return \flipbox\organizations\records\OrganizationTypeSiteSettings|null
477
     */
478
    protected function getSiteSettings()
479
    {
480
        try {
481
            $settings = OrganizationPlugin::getInstance()->getSettings();
482
            $siteSettings = $settings->getSiteSettings()[$this->siteId] ?? null;
483
484
            if (null !== ($type = $this->getPrimaryType())) {
485
                $siteSettings = $type->getSiteSettings()[$this->siteId] ?? $siteSettings;
486
            }
487
488
            return $siteSettings;
489
        } catch (\Exception $e) {
490
            OrganizationPlugin::error(
491
                sprintf(
492
                    "An exception was caught while to resolve site settings: %s",
493
                    $e->getMessage()
494
                )
495
            );
496
        }
497
498
        return null;
499
    }
500
501
    /************************************************************
502
     * EVENTS
503
     ************************************************************/
504
505
    /**
506
     * @inheritdoc
507
     */
508
    public function beforeSave(bool $isNew): bool
509
    {
510
        if (empty($this->getDateJoined())) {
511
            $this->setDateJoined(DateTimeHelper::currentUTCDateTime());
512
        }
513
514
        return parent::beforeSave($isNew);
515
    }
516
517
    /**
518
     * @inheritdoc
519
     * @throws /Exception
520
     */
521
    public function afterSave(bool $isNew)
522
    {
523
        if (false === $this->saveRecord($isNew)) {
524
            throw new Exception('Unable to save organization record');
525
        }
526
527
        // Types
528
        if (false === $this->saveTypes()) {
529
            throw new Exception("Unable to save types.");
530
        }
531
532
        // Users
533
        if (false === $this->saveUsers()) {
534
            throw new Exception("Unable to save users.");
535
        }
536
537
        parent::afterSave($isNew);
538
    }
539
540
    /*******************************************
541
     * RECORD
542
     *******************************************/
543
544
    /**
545
     * @param bool $isNew
546
     * @return bool
547
     */
548
    protected function saveRecord(bool $isNew): bool
549
    {
550
        $record = $this->elementToRecord();
551
552
        if (!$record->save()) {
553
            $this->addErrors($record->getErrors());
554
555
            OrganizationPlugin::error(
556
                Json::encode($this->getErrors()),
557
                __METHOD__
558
            );
559
560
            return false;
561
        }
562
563
        if (false !== ($dateUpdated = DateTimeHelper::toDateTime($record->dateUpdated))) {
564
            $this->dateUpdated = $dateUpdated;
565
        }
566
567
568
        if ($isNew) {
569
            $this->id = $record->id;
570
571
            if (false !== ($dateCreated = DateTimeHelper::toDateTime($record->dateCreated))) {
572
                $this->dateCreated = $dateCreated;
573
            }
574
        }
575
576
        return true;
577
    }
578
579
    /**
580
     * @inheritdoc
581
     * @return OrganizationRecord
582
     */
583
    protected function elementToRecord(): OrganizationRecord
584
    {
585
        if (!$record = OrganizationRecord::findOne([
586
            'id' => $this->getId()
587
        ])) {
588
            $record = new OrganizationRecord();
589
        }
590
591
        // PopulateOrganizationTypeTrait the record attributes
592
        $record->id = $this->getId();
593
        $record->dateJoined = $this->getDateJoined();
594
595
        return $record;
596
    }
597
}
598