Field::afterSaveUserRelations()   F
last analyzed

Complexity

Conditions 12
Paths 402

Size

Total Lines 87
Code Lines 49

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 156

Importance

Changes 0
Metric Value
dl 0
loc 87
ccs 0
cts 66
cp 0
rs 3.7857
c 0
b 0
f 0
cc 12
eloc 49
nc 402
nop 3
crap 156

How to fix   Long Method    Complexity   

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/organization/license
6
 * @link       https://www.flipboxfactory.com/software/organization/
7
 */
8
9
namespace flipbox\organization\services;
10
11
use Craft;
12
use craft\elements\User as UserElement;
13
use craft\helpers\ArrayHelper;
14
use flipbox\organization\elements\db\Organization as OrganizationQuery;
15
use flipbox\organization\elements\Organization as OrganizationElement;
16
use flipbox\organization\fields\User as OrganizationUserField;
17
use flipbox\organization\Organization as OrganizationPlugin;
18
use flipbox\organization\records\User;
0 ignored issues
show
Bug introduced by
This use statement conflicts with another class in this namespace, flipbox\organization\services\User.

Let’s assume that you have a directory layout like this:

.
|-- OtherDir
|   |-- Bar.php
|   `-- Foo.php
`-- SomeDir
    `-- Foo.php

and let’s assume the following content of Bar.php:

// Bar.php
namespace OtherDir;

use SomeDir\Foo; // This now conflicts the class OtherDir\Foo

If both files OtherDir/Foo.php and SomeDir/Foo.php are loaded in the same runtime, you will see a PHP error such as the following:

PHP Fatal error:  Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.php

However, as OtherDir/Foo.php does not necessarily have to be loaded and the error is only triggered if it is loaded before OtherDir/Bar.php, this problem might go unnoticed for a while. In order to prevent this error from surfacing, you must import the namespace with a different alias:

// Bar.php
namespace OtherDir;

use SomeDir\Foo as SomeDirFoo; // There is no conflict anymore.
Loading history...
19
use flipbox\spark\helpers\RecordHelper;
20
use yii\base\Component;
21
use yii\base\Exception;
22
23
/**
24
 * @author Flipbox Factory <[email protected]>
25
 * @since 1.0.0
26
 */
27
class Field extends Component
28
{
29
30
    /**
31
     * @param OrganizationUserField $field
32
     * @param UserElement $source
33
     * @param OrganizationQuery $target
34
     * @return bool
35
     */
36
    public function beforeSaveUserRelations(
37
        OrganizationUserField $field,
38
        UserElement $source,
39
        OrganizationQuery $target
40
    ) {
41
42
        // Check cache for explicitly set (and possibly not saved) organizations
43
        if (null !== ($targets = $target->getCachedResult())) {
44
45
            /** @var OrganizationElement $target */
46
            foreach ($targets as $target) {
47
                // New organization?
48
                if (!$target->id) {
1 ignored issue
show
Bug introduced by
Accessing id on the interface craft\base\ElementInterface suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
49
                    if (!Craft::$app->getElements()->saveElement($target)) {
50
                        $source->addError(
51
                            $field->handle,
52
                            Craft::t('organization', 'Unable to save organization.')
53
                        );
54
55
                        return false;
56
                    }
57
                }
58
            }
59
        }
60
61
        return true;
62
    }
63
64
    /**
65
     * @param OrganizationUserField $field
66
     * @param UserElement $user
67
     * @param OrganizationQuery $organizationQuery
68
     * @throws \Exception
69
     * @return void
70
     */
71
    public function afterSaveUserRelations(
72
        OrganizationUserField $field,
73
        UserElement $user,
74
        OrganizationQuery $organizationQuery
75
    ) {
76
77
        /** @var OrganizationElement[]|null $targets */
78
        if (null === ($targets = $organizationQuery->getCachedResult())) {
79
            if (null !== $organizationQuery->id) {
80
                $targets = OrganizationPlugin::getInstance()->getOrganization()->getQuery([
81
                    'status' => null,
82
                    'id' => $organizationQuery->id
83
                ])->all();
84
            }
85
        }
86
87
        // Nothing to change
88
        if (null === $targets) {
89
            return;
90
        }
91
92
        $transaction = RecordHelper::beginTransaction();
93
94
        try {
95
            // Delete the existing relations
96
            $oldRelationConditions = [
97
                'and',
98
                [
99
                    'userId' => $user->id
100
                ]
101
            ];
102
103
            if ($field->localizeRelations) {
104
                $oldRelationConditions[] = [
105
                    'or',
106
                    ['siteId' => null],
107
                    ['siteId' => $user->siteId]
108
                ];
109
            }
110
111
            $oldTargetIds = User::find()
112
                ->select(['organizationId'])
113
                ->where($oldRelationConditions)
114
                ->column();
115
116
            // Find organization ids that we need to remove
117
            foreach ($targets as $organizationQuery) {
118
                ArrayHelper::remove($oldTargetIds, $organizationQuery->id);
119
            }
120
121
            /** @var OrganizationElement[] $organizations */
122
            $organizations = OrganizationPlugin::getInstance()->getOrganization()->getQuery()
123
                ->id($oldTargetIds)
124
                ->status(null)
125
                ->all();
126
127
            foreach ($organizations as $organization) {
128
                OrganizationPlugin::getInstance()->getUser()->dissociate($user, $organization);
129
            }
130
131
            // Add the new ones
132
            if (!empty($targets)) {
133
                $siteId = null;
134
135
                if ($field->localizeRelations) {
136
                    $siteId = $user->siteId;
137
                }
138
139
                foreach ($targets as $organizationQuery) {
140
                    if (!OrganizationPlugin::getInstance()->getUser()->associate(
141
                        $user,
142
                        $organizationQuery,
143
                        $siteId,
144
                        0
145
                    )) {
146
                        throw new Exception('Unable to associate user to organization');
147
                    }
148
                }
149
            }
150
151
            $transaction->commit();
152
        } catch (\Exception $e) {
153
            $transaction->rollBack();
154
155
            throw $e;
156
        }
157
    }
158
}
159