Completed
Push — develop ( 89b566...4a9624 )
by Nate
04:50
created

Organization::findOne()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

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