Completed
Push — master ( fb0960...b5a813 )
by Christopher
04:22
created

createAssocationFromNavigationProperty()   C

Complexity

Conditions 14
Paths 34

Size

Total Lines 70
Code Lines 53

Duplication

Lines 14
Ratio 20 %

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 14
loc 70
rs 5.6188
cc 14
eloc 53
nc 34
nop 8

How to fix   Long Method    Complexity    Many Parameters   

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:

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php
2
3
namespace AlgoWeb\ODataMetadata;
4
5
use AlgoWeb\ODataMetadata\MetadataV3\edm\EntityContainer\AssociationSetAnonymousType;
6
use AlgoWeb\ODataMetadata\MetadataV3\edm\EntityContainer\AssociationSetAnonymousType\EndAnonymousType;
7
use AlgoWeb\ODataMetadata\MetadataV3\edm\EntityContainer\EntitySetAnonymousType;
8
use AlgoWeb\ODataMetadata\MetadataV3\edm\TAssociationEndType;
9
use AlgoWeb\ODataMetadata\MetadataV3\edm\TAssociationType;
10
use AlgoWeb\ODataMetadata\MetadataV3\edm\TConstraintType;
11
use AlgoWeb\ODataMetadata\MetadataV3\edm\TDocumentationType;
12
use AlgoWeb\ODataMetadata\MetadataV3\edm\TEntityPropertyType;
13
use AlgoWeb\ODataMetadata\MetadataV3\edm\TEntityTypeType;
14
use AlgoWeb\ODataMetadata\MetadataV3\edm\TNavigationPropertyType;
15
use AlgoWeb\ODataMetadata\MetadataV3\edm\TPropertyRefType;
16
use AlgoWeb\ODataMetadata\MetadataV3\edm\TReferentialConstraintRoleElementType;
17
use AlgoWeb\ODataMetadata\MetadataV3\edmx\Edmx;
18
use JMS\Serializer\SerializerBuilder;
19
20
class MetadataManager
21
{
22
    private $V3Edmx = null;
23
    private $oldEdmx = null;
24
    private $lastError = null;
25
    private $serializer = null;
26
27
    public function __construct($namespaceName = "Data", $containerName = "DefaultContainer")
28
    {
29
        $this->V3Edmx = new Edmx($namespaceName, $containerName);
30
        if (!$this->V3Edmx->isOK($msg)) {
31
            throw new \Exception($msg);
32
        }
33
        $ymlDir = __DIR__ . DIRECTORY_SEPARATOR . "MetadataV3" . DIRECTORY_SEPARATOR . "JMSmetadata";
34
        $this->serializer =
35
            SerializerBuilder::create()
36
                ->addMetadataDir($ymlDir)
37
                ->build();
38
    }
39
40
    public function getEdmx()
41
    {
42
        return $this->V3Edmx;
43
    }
44
45
    public function getEdmxXML()
46
    {
47
        return $this->serializer->serialize($this->V3Edmx, "xml");
48
    }
49
50
    public function addEntityType($name, $accessType = "Public", $summary = null, $longDescription = null)
51
    {
52
        $this->startEdmxTransaction();
53
        $NewEntity = new TEntityTypeType();
54
        $NewEntity->setName($name);
55 View Code Duplication
        if (null != $summary || null != $longDescription) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
56
            $documentation = new TDocumentationType();
57
            $documentation->setSummary($summary);
58
            $documentation->setLongDescription($longDescription);
59
            $NewEntity->setDocumentation($documentation);
60
        }
61
62
        $entitySet = new EntitySetAnonymousType();
63
        $entitySet->setName($this->pluralize(2, $NewEntity->getName()));
64
        $namespace = $this->V3Edmx->getDataServices()[0]->getNamespace();
65 View Code Duplication
        if (0 == strlen(trim($namespace))) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
66
            $entityTypeName = $NewEntity->getName();
67
        } else {
68
            $entityTypeName = $namespace . "." . $NewEntity->getName();
69
        }
70
        $entitySet->setEntityType($entityTypeName);
71
        $entitySet->setGetterAccess($accessType);
72
73
        $this->V3Edmx->getDataServices()[0]->addToEntityType($NewEntity);
74
        $this->V3Edmx->getDataServices()[0]->getEntityContainer()[0]->addToEntitySet($entitySet);
75
        if (!$this->V3Edmx->isok($this->lastError)) {
76
            $this->revertEdmxTransaction();
77
            return false;
78
        }
79
        $this->commitEdmxTransaction();
80
        return $NewEntity;
81
    }
82
83
    private function startEdmxTransaction()
84
    {
85
        $this->oldEdmx = serialize($this->V3Edmx);
86
    }
87
88
    /**
89
     * Pluralizes a word if quantity is not one.
90
     *
91
     * @param int $quantity Number of items
92
     * @param string $singular Singular form of word
93
     * @param string $plural Plural form of word; function will attempt to deduce plural
94
     * form from singular if not provided
95
     * @return string Pluralized word if quantity is not one, otherwise singular
96
     */
97 View Code Duplication
    public static function pluralize($quantity, $singular, $plural = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
98
    {
99
        if ($quantity == 1 || !strlen($singular)) {
100
            return $singular;
101
        }
102
        if ($plural !== null) {
103
            return $plural;
104
        }
105
106
        $last_letter = strtolower($singular[strlen($singular) - 1]);
107
        switch ($last_letter) {
108
            case 'y':
109
                return substr($singular, 0, -1) . 'ies';
110
            case 's':
111
                return $singular . 'es';
112
            default:
113
                return $singular . 's';
114
        }
115
    }
116
117
    private function revertEdmxTransaction()
118
    {
119
        $this->V3Edmx = unserialize($this->oldEdmx);
120
    }
121
122
    private function commitEdmxTransaction()
123
    {
124
        $this->oldEdmx = null;
125
    }
126
127
    public function addPropertyToEntityType(
128
        $entityType,
129
        $name,
130
        $type,
131
        $defaultValue = null,
132
        $nullable = false,
133
        $isKey = false,
134
        $storeGeneratedPattern = null,
135
        $summary = null,
136
        $longDescription = null
137
    )
138
    {
139
        $this->startEdmxTransaction();
140
        $NewProperty = new TEntityPropertyType();
141
        $NewProperty->setName($name);
142
        $NewProperty->setType($type);
143
        $NewProperty->setStoreGeneratedPattern($storeGeneratedPattern);
144
        $NewProperty->setNullable($nullable);
145 View Code Duplication
        if (null != $summary || null != $longDescription) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
146
            $documentation = new TDocumentationType();
147
            $documentation->setSummary($summary);
148
            $documentation->setLongDescription($longDescription);
149
            $NewProperty->addToDocumentation($documentation);
150
        }
151
        if (null != $defaultValue) {
152
            $NewProperty->setDefaultValue($defaultValue);
153
        }
154
        $entityType->addToProperty($NewProperty);
155
        if ($isKey) {
156
            $Key = new TPropertyRefType();
157
            $Key->setName($name);
158
            $entityType->addToKey($Key);
159
        }
160
        if (!$this->V3Edmx->isok($this->lastError)) {
161
            $this->revertEdmxTransaction();
162
            return false;
163
        }
164
        $this->commitEdmxTransaction();
165
        return $NewProperty;
166
    }
167
168
    public function addNavigationPropertyToEntityType(
169
        TEntityTypeType $principalType,
170
        $principalProperty,
171
        $principalMultiplicity,
0 ignored issues
show
Unused Code introduced by
The parameter $principalMultiplicity is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
172
        $principalSummery,
0 ignored issues
show
Unused Code introduced by
The parameter $principalSummery is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
173
        $principalLongDescription,
0 ignored issues
show
Unused Code introduced by
The parameter $principalLongDescription is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
174
        TEntityTypeType $dependentType,
175
        $dependentProperty = "",
176
        $dependentMultiplicity,
0 ignored issues
show
Unused Code introduced by
The parameter $dependentMultiplicity is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
177
        $dependentSummery,
0 ignored issues
show
Unused Code introduced by
The parameter $dependentSummery is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
178
        $dependentLongDescription,
0 ignored issues
show
Unused Code introduced by
The parameter $dependentLongDescription is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
179
        array $principalConstraintProperty = null,
0 ignored issues
show
Unused Code introduced by
The parameter $principalConstraintProperty is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
180
        array $dependentConstraintProperty = null,
0 ignored issues
show
Unused Code introduced by
The parameter $dependentConstraintProperty is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
181
        $principalGetterAccess = "Public",
182
        $principalSetterAccess = "Public",
183
        $dependentGetterAccess = "Public",
184
        $dependentSetterAccess = "Public"
185
    )
186
    {
187
        $this->startEdmxTransaction();
188
        $dependentNavigationProperty = new TNavigationPropertyType();
189
        $principalEntitySetName = $this->pluralize(2, $principalType->getName());
190
        $dependentEntitySetName = $this->pluralize(2, $dependentType->getName());
191
        $relationName = $principalType->getName() . "_" . $principalProperty . "_" . $dependentType->getName() . "_" . $dependentProperty;
192
        $relationName = trim($relationName, "_");
193
194
        $namespace = $this->V3Edmx->getDataServices()[0]->getNamespace();
195
        if (0 == strlen(trim($namespace))) {
196
            $relationFQName = $relationName;
197
        } else {
198
            $relationFQName = $namespace . "." . $relationName;
199
        }
200
201
        $principalNavigationProperty = new TNavigationPropertyType();
202
        $principalNavigationProperty->setName($principalProperty);
203
        $principalNavigationProperty->setToRole($dependentEntitySetName);
204
        $principalNavigationProperty->setFromRole($principalEntitySetName);
205
        $principalNavigationProperty->setRelationship($relationFQName);
206
        $principalNavigationProperty->setGetterAccess($principalGetterAccess);
207
        $principalNavigationProperty->setSetterAccess($principalSetterAccess);
208
        $principalType->addToNavigationProperty($principalNavigationProperty);
209
210
        if (!empty($dependentProperty)) {
211
            $dependentNavigationProperty = new TNavigationPropertyType();
212
            $dependentNavigationProperty->setName($dependentProperty);
213
            $dependentNavigationProperty->setToRole($principalEntitySetName);
214
            $dependentNavigationProperty->setFromRole($dependentEntitySetName);
215
            $dependentNavigationProperty->setRelationship($relationFQName);
216
            $dependentNavigationProperty->setGetterAccess($dependentGetterAccess);
217
            $dependentNavigationProperty->setSetterAccess($dependentSetterAccess);
218
            $dependentType->addToNavigationProperty($dependentNavigationProperty);
219
        }
220
221
222
        if (!$this->V3Edmx->isok($this->lastError)) {
223
            $this->revertEdmxTransaction();
224
            return false;
225
        }
226
        $this->commitEdmxTransaction();
227
        return [$principalNavigationProperty, $dependentNavigationProperty];
228
    }
229
230
    public function getLastError()
231
    {
232
        return $this->lastError;
233
    }
234
235
    protected function createAssocationFromNavigationProperty(
236
        TEntityTypeType $principalType,
237
        TEntityTypeType $dependentType,
238
        TNavigationPropertyType $principalNavigationProperty,
239
        TNavigationPropertyType $dependentNavigationProperty = null,
240
        $principalMultiplicity,
241
        $dependentMultiplicity,
242
        array $principalConstraintProperty = null,
243
        array $dependentConstraintProperty = null
244
245
    )
246
    {
247
        if (null != $dependentNavigationProperty) {
248
            if ($dependentNavigationProperty->getRelationship() != $principalNavigationProperty->getRelationship()) {
249
                throw new \Exception("if you have both a dependant property and a principal property they should both have the same relationship");
250
            }
251
            if ($dependentNavigationProperty->getFromRole() != $principalNavigationProperty->getToRole() ||
252
                $dependentNavigationProperty->getToRole() != $principalNavigationProperty->getFromRole()
253
            ) {
254
                throw new \Exception("The from roles and two roles from matching properties should match");
255
            }
256
        }
257
        $namespace = $this->V3Edmx->getDataServices()[0]->getNamespace();
258
259
        if (0 == strlen(trim($namespace))) {
260
            $principalTypeFQName = $principalType->getName();
261
            $dependentTypeFQName = $dependentType->getName();
262
        } else {
263
            $principalTypeFQName = $namespace . "." . $principalType->getName();
264
            $dependentTypeFQName = $namespace . "." . $dependentType->getName();
265
        }
266
        $association = new TAssociationType();
267
        $association->setName($principalNavigationProperty->getRelationship());
268
        $principalEnd = new TAssociationEndType();
269
        $principalEnd->setType($principalTypeFQName);
270
        $principalEnd->setRole($principalNavigationProperty->getFromRole());
271
        $principalEnd->setMultiplicity($principalMultiplicity);
272
        $dependentEnd = new TAssociationEndType();
273
        $dependentEnd->setType($dependentTypeFQName);
274
        $dependentEnd->setRole($dependentNavigationProperty->getFromRole());
0 ignored issues
show
Bug introduced by
It seems like $dependentNavigationProperty is not always an object, but can also be of type null. Maybe add an additional type check?

If a variable is not always an object, we recommend to add an additional type check to ensure your method call is safe:

function someFunction(A $objectMaybe = null)
{
    if ($objectMaybe instanceof A) {
        $objectMaybe->doSomething();
    }
}
Loading history...
275
        $dependentEnd->setMultiplicity($dependentMultiplicity);
276
        $association->addToEnd($principalEnd);
277
        $association->addToEnd($dependentEnd);
278
        $principalReferralConstraint = null;
279
        $dependentReferralConstraint = null;
280 View Code Duplication
        if (null != $principalConstraintProperty && 0 < count($principalConstraintProperty)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
281
            $principalReferralConstraint = new TReferentialConstraintRoleElementType();
282
            $principalReferralConstraint->setRole($principalNavigationProperty->getFromRole());
283
            foreach ($principalConstraintProperty as $propertyRef) {
284
                $principalReferralConstraint->addToPropertyRef($propertyRef);
285
            }
286
        }
287 View Code Duplication
        if (null != $dependentConstraintProperty && 0 < count($dependentConstraintProperty)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
288
            $dependentReferralConstraint = new TReferentialConstraintRoleElementType();
289
            $dependentReferralConstraint->setRole($dependentNavigationProperty->getFromRole());
290
            foreach ($dependentConstraintProperty as $propertyRef) {
291
                $dependentReferralConstraint->addToPropertyRef($propertyRef);
292
            }
293
        }
294
295
        if (null != $dependentReferralConstraint || null != $principalReferralConstraint) {
296
            $constraint = new TConstraintType();
297
            $constraint->setPrincipal($principalReferralConstraint);
0 ignored issues
show
Bug introduced by
It seems like $principalReferralConstraint defined by null on line 278 can be null; however, AlgoWeb\ODataMetadata\Me...intType::setPrincipal() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
298
            $constraint->setDependent($dependentReferralConstraint);
0 ignored issues
show
Bug introduced by
It seems like $dependentReferralConstraint defined by null on line 279 can be null; however, AlgoWeb\ODataMetadata\Me...intType::setDependent() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
299
            $association->setReferentialConstraint($constraint);
300
        }
301
        return $association;
302
303
        return true;
0 ignored issues
show
Unused Code introduced by
return true; does not seem to be reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
304
    }
305
306
    protected function createAssocationSetForAssocation(
307
        TAssociationType $association,
308
        $principalEntitySetName,
309
        $dependentEntitySetName
310
    )
311
    {
312
        $as = new AssociationSetAnonymousType();
313
        $name = $association->getName();
314
        $as->setName($name);
315
        $namespace = $this->V3Edmx->getDataServices()[0]->getNamespace();
316 View Code Duplication
        if (0 == strlen(trim($namespace))) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
317
            $associationSetName = $association->getName();
318
        } else {
319
            $associationSetName = $namespace . "." . $association->getName();
320
        }
321
        $as->setAssociation($associationSetName);
322
        $end1 = new EndAnonymousType();
323
        $end1->setRole($association->getEnd()[0]->getRole());
324
        $end1->setEntitySet($principalEntitySetName);
325
        $end2 = new EndAnonymousType();
326
        $end2->setRole($association->getEnd()[1]->getRole());
327
        $end2->setEntitySet($dependentEntitySetName);
328
        $as->addToEnd($end1);
329
        $as->addToEnd($end2);
330
        return $as;
331
    }
332
333
}
334