Completed
Push — master ( 6e68ed...44f89b )
by Alex
09:25
created

MetadataManager::addPropertyToEntityType()   B

Complexity

Conditions 6
Paths 16

Size

Total Lines 36
Code Lines 30

Duplication

Lines 6
Ratio 16.67 %

Importance

Changes 3
Bugs 0 Features 0
Metric Value
c 3
b 0
f 0
dl 6
loc 36
rs 8.439
cc 6
eloc 30
nc 16
nop 9

How to fix   Many Parameters   

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\EntityContainer\FunctionImportAnonymousType;
9
use AlgoWeb\ODataMetadata\MetadataV3\edm\TAssociationEndType;
10
use AlgoWeb\ODataMetadata\MetadataV3\edm\TAssociationType;
11
use AlgoWeb\ODataMetadata\MetadataV3\edm\TComplexTypePropertyType;
12
use AlgoWeb\ODataMetadata\MetadataV3\edm\TComplexTypeType;
13
use AlgoWeb\ODataMetadata\MetadataV3\edm\TConstraintType;
14
use AlgoWeb\ODataMetadata\MetadataV3\edm\TDocumentationType;
15
use AlgoWeb\ODataMetadata\MetadataV3\edm\TEntityPropertyType;
16
use AlgoWeb\ODataMetadata\MetadataV3\edm\TEntityTypeType;
17
use AlgoWeb\ODataMetadata\MetadataV3\edm\TFunctionImportReturnTypeType;
18
use AlgoWeb\ODataMetadata\MetadataV3\edm\TFunctionReturnTypeType;
19
use AlgoWeb\ODataMetadata\MetadataV3\edm\TFunctionType;
20
use AlgoWeb\ODataMetadata\MetadataV3\edm\TNavigationPropertyType;
21
use AlgoWeb\ODataMetadata\MetadataV3\edm\TPropertyRefType;
22
use AlgoWeb\ODataMetadata\MetadataV3\edm\TReferentialConstraintRoleElementType;
23
use AlgoWeb\ODataMetadata\MetadataV3\edm\TTextType;
24
use AlgoWeb\ODataMetadata\MetadataV3\edmx\Edmx;
25
use Illuminate\Support\Str;
26
use JMS\Serializer\SerializerBuilder;
27
28
class MetadataManager
29
{
30
    private $V3Edmx = null;
31
    private $lastError = null;
32
    private $serializer = null;
33
34
    public function __construct($namespaceName = "Data", $containerName = "DefaultContainer")
35
    {
36
        $this->V3Edmx = new Edmx($namespaceName, $containerName);
37
        if (!$this->V3Edmx->isOK($msg)) {
38
            throw new \Exception($msg);
39
        }
40
        $this->initSerialiser();
41
        assert(null != $this->serializer, "Serializer must not be null at end of constructor");
42
    }
43
44
    public function getEdmx()
45
    {
46
        $msg = null;
47
        assert($this->V3Edmx->isOk($msg), $msg);
48
        return $this->V3Edmx;
49
    }
50
51
    public function getEdmxXML()
52
    {
53
        assert(null != $this->serializer, "Serializer must not be null when trying to get edmx xml");
54
        return $this->serializer->serialize($this->getEdmx(), "xml");
55
    }
56
57
    public function addEntityType($name, $accessType = "Public", $summary = null, $longDescription = null)
58
    {
59
        $NewEntity = new TEntityTypeType();
60
        $NewEntity->setName($name);
61
        if (null != $summary || null != $longDescription) {
62
            $documentation = $this->generateDocumentation($summary, $longDescription);
63
            $NewEntity->setDocumentation($documentation);
64
        }
65
66
        $entitySet = new EntitySetAnonymousType();
67
        $entitySet->setName(Str::plural($NewEntity->getName(), 2));
68
        $namespace = $this->V3Edmx->getDataServiceType()->getSchema()[0]->getNamespace();
69 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...
70
            $entityTypeName = $NewEntity->getName();
71
        } else {
72
            $entityTypeName = $namespace . "." . $NewEntity->getName();
73
        }
74
        $entitySet->setEntityType($entityTypeName);
75
        $entitySet->setGetterAccess($accessType);
76
77
        $this->V3Edmx->getDataServiceType()->getSchema()[0]->addToEntityType($NewEntity);
78
        $this->V3Edmx->getDataServiceType()->getSchema()[0]->getEntityContainer()[0]->addToEntitySet($entitySet);
79
        if (!$this->V3Edmx->isok($this->lastError)) {
80
            return false;
81
        }
82
        return $NewEntity;
83
    }
84
85
    public function addComplexType($name, $accessType = "Public", $summary = null, $longDescription = null)
86
    {
87
        $NewEntity = new TComplexTypeType();
88
        $NewEntity->setName($name);
89
        $NewEntity->setTypeAccess($accessType);
90 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...
91
            $documentation = new TDocumentationType();
92
            $documentation->setSummary($summary);
93
            $documentation->setLongDescription($longDescription);
94
            $NewEntity->setDocumentation($documentation);
95
        }
96
        $this->V3Edmx->getDataServiceType()->getSchema()[0]->addToComplexType($NewEntity);
97
98
        return $NewEntity;
99
    }
100
101
    public function getSerialiser()
102
    {
103
        return $this->serializer;
104
    }
105
106
    public function addPropertyToComplexType(
107
        \AlgoWeb\ODataMetadata\MetadataV3\edm\TComplexTypeType $complexType,
108
        $name,
109
        $type,
110
        $defaultValue = null,
111
        $nullable = false,
112
        $summary = null,
113
        $longDescription = null
114
    ) {
115
        $NewProperty = new TComplexTypePropertyType();
116
        $NewProperty->setName($name);
117
        $NewProperty->setType($type);
118
        $NewProperty->setNullable($nullable);
119
        if (null != $summary || null != $longDescription) {
120
            $documentation = $this->generateDocumentation($summary, $longDescription);
121
            $NewProperty->addToDocumentation($documentation);
122
        }
123
        if (null != $defaultValue) {
124
            $NewProperty->setDefaultValue($defaultValue);
125
        }
126
        $complexType->addToProperty($NewProperty);
127
        return $NewProperty;
128
    }
129
130
    public function addPropertyToEntityType(
131
        $entityType,
132
        $name,
133
        $type,
134
        $defaultValue = null,
135
        $nullable = false,
136
        $isKey = false,
137
        $storeGeneratedPattern = null,
138
        $summary = null,
139
        $longDescription = null
140
    ) {
141
        $NewProperty = new TEntityPropertyType();
142
        $NewProperty->setName($name);
143
        $NewProperty->setType($type);
144
        $NewProperty->setStoreGeneratedPattern($storeGeneratedPattern);
145
        $NewProperty->setNullable($nullable);
146 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...
147
            $documentation = new TDocumentationType();
148
            $documentation->setSummary($summary);
149
            $documentation->setLongDescription($longDescription);
150
            $NewProperty->addToDocumentation($documentation);
151
        }
152
        if (null != $defaultValue) {
153
            $NewProperty->setDefaultValue($defaultValue);
154
        }
155
        $entityType->addToProperty($NewProperty);
156
        if ($isKey) {
157
            $Key = new TPropertyRefType();
158
            $Key->setName($name);
159
            $entityType->addToKey($Key);
160
        }
161
        if (!$this->V3Edmx->isok($this->lastError)) {
162
            return false;
163
        }
164
        return $NewProperty;
165
    }
166
167
    public function addNavigationPropertyToEntityType(
168
        TEntityTypeType $principalType,
169
        $principalMultiplicity,
170
        $principalProperty,
171
        TEntityTypeType $dependentType,
172
        $dependentMultiplicity,
173
        $dependentProperty = "",
174
        array $principalConstraintProperty = null,
175
        array $dependentConstraintProperty = null,
176
        $principalGetterAccess = "Public",
177
        $principalSetterAccess = "Public",
178
        $dependentGetterAccess = "Public",
179
        $dependentSetterAccess = "Public",
180
        $principalSummery = null,
181
        $principalLongDescription = null,
182
        $dependentSummery = null,
183
        $dependentLongDescription = null
184
    ) {
185
        $principalEntitySetName = Str::plural($principalType->getName(), 2);
186
        $dependentEntitySetName = Str::plural($dependentType->getName(), 2);
187
        $relationName = $principalType->getName() . "_" . $principalProperty . "_"
188
                        . $dependentType->getName() . "_" . $dependentProperty;
189
        $relationName = trim($relationName, "_");
190
191
        $namespace = $this->V3Edmx->getDataServiceType()->getSchema()[0]->getNamespace();
192
        if (0 == strlen(trim($namespace))) {
193
            $relationFQName = $relationName;
194
        } else {
195
            $relationFQName = $namespace . "." . $relationName;
196
        }
197
198
        $principalNavigationProperty = new TNavigationPropertyType();
199
        $principalNavigationProperty->setName($principalProperty);
200
        $principalNavigationProperty->setToRole(trim($dependentEntitySetName . "_" . $dependentProperty, "_"));
201
        $principalNavigationProperty->setFromRole($principalEntitySetName . "_" . $principalProperty);
202
        $principalNavigationProperty->setRelationship($relationFQName);
203
        $principalNavigationProperty->setGetterAccess($principalGetterAccess);
204
        $principalNavigationProperty->setSetterAccess($principalSetterAccess);
205
        if (null != $principalSummery || null != $principalLongDescription) {
206
            $principalDocumentation = new TDocumentationType();
207
            $principalDocumentation->setSummary($principalSummery);
208
            $principalDocumentation->setLongDescription($principalLongDescription);
209
            $principalNavigationProperty->setDocumentation($principalDocumentation);
210
        }
211
        $principalType->addToNavigationProperty($principalNavigationProperty);
212
        $dependentNavigationProperty = null;
213
        if (!empty($dependentProperty)) {
214
            $dependentNavigationProperty = new TNavigationPropertyType();
215
            $dependentNavigationProperty->setName($dependentProperty);
216
            $dependentNavigationProperty->setToRole($principalEntitySetName . "_" . $principalProperty);
217
            $dependentNavigationProperty->setFromRole($dependentEntitySetName . "_" . $dependentProperty);
218
            $dependentNavigationProperty->setRelationship($relationFQName);
219
            $dependentNavigationProperty->setGetterAccess($dependentGetterAccess);
220
            $dependentNavigationProperty->setSetterAccess($dependentSetterAccess);
221
            if (null != $dependentSummery || null != $dependentLongDescription) {
222
                $dependentDocumentation = new TDocumentationType();
223
                $dependentDocumentation->setSummary($dependentSummery);
224
                $dependentDocumentation->setLongDescription($dependentLongDescription);
225
                $dependentNavigationProperty->setDocumentation($dependentDocumentation);
226
            }
227
            $dependentType->addToNavigationProperty($dependentNavigationProperty);
228
        }
229
230
        $assocation = $this->createAssocationFromNavigationProperty(
231
            $principalType,
232
            $dependentType,
233
            $principalNavigationProperty,
234
            $dependentNavigationProperty,
235
            $principalMultiplicity,
236
            $dependentMultiplicity,
237
            $principalConstraintProperty,
238
            $dependentConstraintProperty
239
        );
240
241
        $this->V3Edmx->getDataServiceType()->getSchema()[0]->addToAssociation($assocation);
242
243
        $associationSet = $this->createAssocationSetForAssocation(
244
            $assocation,
245
            $principalEntitySetName,
246
            $dependentEntitySetName
247
        );
248
249
        $this->V3Edmx->getDataServiceType()->getSchema()[0]
250
            ->getEntityContainer()[0]->addToAssociationSet($associationSet);
251
252
        if (!$this->V3Edmx->isok($this->lastError)) {
253
            return false;
254
        }
255
        return [$principalNavigationProperty, $dependentNavigationProperty];
256
    }
257
258
    protected function createAssocationFromNavigationProperty(
259
        TEntityTypeType $principalType,
260
        TEntityTypeType $dependentType,
261
        TNavigationPropertyType $principalNavigationProperty,
262
        TNavigationPropertyType $dependentNavigationProperty = null,
263
        $principalMultiplicity,
264
        $dependentMultiplicity,
265
        array $principalConstraintProperty = null,
266
        array $dependentConstraintProperty = null
267
    ) {
268
        if (null != $dependentNavigationProperty) {
269
            if ($dependentNavigationProperty->getRelationship() != $principalNavigationProperty->getRelationship()) {
270
                $msg = "if you have both a dependent property and a principal property,"
271
                       ." they should both have the same relationship";
272
                throw new \Exception($msg);
273
            }
274
            if ($dependentNavigationProperty->getFromRole() != $principalNavigationProperty->getToRole() ||
275
                $dependentNavigationProperty->getToRole() != $principalNavigationProperty->getFromRole()
276
            ) {
277
                throw new \Exception("The from roles and two roles from matching properties should match");
278
            }
279
        }
280
        $namespace = $this->V3Edmx->getDataServiceType()->getSchema()[0]->getNamespace();
281
282
        if (0 == strlen(trim($namespace))) {
283
            $principalTypeFQName = $principalType->getName();
284
            $dependentTypeFQName = $dependentType->getName();
285
        } else {
286
            $principalTypeFQName = $namespace . "." . $principalType->getName();
287
            $dependentTypeFQName = $namespace . "." . $dependentType->getName();
288
        }
289
        $association = new TAssociationType();
290
        $relationship = $principalNavigationProperty->getRelationship();
291
        if (strpos($relationship, '.') !== false) {
292
            $relationship = substr($relationship, strpos($relationship, '.') + 1);
293
        }
294
295
        $association->setName($relationship);
296
        $principalEnd = new TAssociationEndType();
297
        $principalEnd->setType($principalTypeFQName);
298
        $principalEnd->setRole($principalNavigationProperty->getFromRole());
299
        $principalEnd->setMultiplicity($principalMultiplicity);
300
        $association->addToEnd($principalEnd);
301
        $dependentEnd = new TAssociationEndType();
302
        $dependentEnd->setType($dependentTypeFQName);
303
        $dependentEnd->setMultiplicity($dependentMultiplicity);
304
        $association->addToEnd($dependentEnd);
305
306
        if (null != $dependentNavigationProperty) {
307
            $dependentEnd->setRole($dependentNavigationProperty->getFromRole());
308
        } else {
309
            $dependentEnd->setRole($principalNavigationProperty->getToRole());
310
        }
311
312
        $principalReferralConstraint = null;
313
        $dependentReferralConstraint = null;
314
315 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...
316
            $principalReferralConstraint = new TReferentialConstraintRoleElementType();
317
            $principalReferralConstraint->setRole($principalNavigationProperty->getFromRole());
318
            foreach ($principalConstraintProperty as $propertyRef) {
319
                $TpropertyRef = new TPropertyRefType();
320
                $TpropertyRef->setName($propertyRef);
321
                $principalReferralConstraint->addToPropertyRef($TpropertyRef);
322
            }
323
        }
324 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...
325
            $dependentReferralConstraint = new TReferentialConstraintRoleElementType();
326
            $dependentReferralConstraint->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...
327
            foreach ($dependentConstraintProperty as $propertyRef) {
328
                $TpropertyRef = new TPropertyRefType();
329
                $TpropertyRef->setName($propertyRef);
330
                $dependentReferralConstraint->addToPropertyRef($TpropertyRef);
331
            }
332
        }
333
334
        if (null != $dependentReferralConstraint || null != $principalReferralConstraint) {
335
            $constraint = new TConstraintType();
336
            $constraint->setPrincipal($principalReferralConstraint);
0 ignored issues
show
Bug introduced by
It seems like $principalReferralConstraint defined by null on line 312 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...
337
            $constraint->setDependent($dependentReferralConstraint);
0 ignored issues
show
Bug introduced by
It seems like $dependentReferralConstraint defined by null on line 313 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...
338
            $association->setReferentialConstraint($constraint);
339
        }
340
        return $association;
341
    }
342
343
    protected function createAssocationSetForAssocation(
344
        TAssociationType $association,
345
        $principalEntitySetName,
346
        $dependentEntitySetName
347
    ) {
348
        $as = new AssociationSetAnonymousType();
349
        $name = $association->getName();
350
        $as->setName($name);
351
        $namespace = $this->V3Edmx->getDataServiceType()->getSchema()[0]->getNamespace();
352 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...
353
            $associationSetName = $association->getName();
354
        } else {
355
            $associationSetName = $namespace . "." . $association->getName();
356
        }
357
        $as->setAssociation($associationSetName);
358
        $end1 = new EndAnonymousType();
359
        $end1->setRole($association->getEnd()[0]->getRole());
360
        $end1->setEntitySet($principalEntitySetName);
361
        $end2 = new EndAnonymousType();
362
        $end2->setRole($association->getEnd()[1]->getRole());
363
        $end2->setEntitySet($dependentEntitySetName);
364
        $as->addToEnd($end1);
365
        $as->addToEnd($end2);
366
        return $as;
367
    }
368
369
    public function getLastError()
370
    {
371
        return $this->lastError;
372
    }
373
374
    /**
375
     * @param string $name
376
     * @param IsOK $expectedReturnType
377
     * @param TTextType $shortDesc
378
     * @param TTextType $longDesc
379
     * @return FunctionImportAnonymousType
380
     */
381
    public function createSingleton(
382
        $name,
383
        IsOK $expectedReturnType,
384
        TTextType $shortDesc = null,
385
        TTextType $longDesc = null
386
    ) {
387
        if (!($expectedReturnType instanceof TEntityTypeType) && !($expectedReturnType instanceof TComplexTypeType)) {
388
            $msg = "Expected return type must be either TEntityType or TComplexType";
389
            throw new \InvalidArgumentException($msg);
390
        }
391
392
        if (!is_string($name) || empty($name)) {
393
            $msg = "Name must be a non-empty string";
394
            throw new \InvalidArgumentException($msg);
395
        }
396
397
        $documentation = null;
398
        if (null != $shortDesc || null != $longDesc) {
399
            $documentation = $this->generateDocumentation($shortDesc, $longDesc);
400
        }
401
        $funcType = new FunctionImportAnonymousType();
402
        $funcType->setName($name);
403
404
        $typeName = $expectedReturnType->getName();
405
        $returnType = new TFunctionImportReturnTypeType();
406
        $returnType->setType($typeName);
407
        $returnType->setEntitySetAttribute($typeName);
408
        assert($returnType->isOK($msg), $msg);
409
        $funcType->addToReturnType($returnType);
410
        if (null != $documentation) {
411
            $funcType->setDocumentation($documentation);
412
        }
413
414
        $this->getEdmx()->getDataServiceType()->getSchema()[0]->getEntityContainer()[0]->addToFunctionImport($funcType);
415
416
        return $funcType;
417
    }
418
419
    private function initSerialiser()
420
    {
421
        $ymlDir = __DIR__ . DIRECTORY_SEPARATOR . "MetadataV3" . DIRECTORY_SEPARATOR . "JMSmetadata";
422
        $this->serializer =
423
            SerializerBuilder::create()
424
                ->addMetadataDir($ymlDir)
425
                ->build();
426
    }
427
428
    public function __sleep()
429
    {
430
        $this->serializer = null;
431
        $result = array_keys(get_object_vars($this));
432
        return $result;
433
    }
434
435
    public function __wakeup()
436
    {
437
        $this->initSerialiser();
438
    }
439
440
    /**
441
     * @param $summary
442
     * @param $longDescription
443
     * @return TDocumentationType
444
     */
445
    private function generateDocumentation($summary, $longDescription)
446
    {
447
        $documentation = new TDocumentationType();
448
        $documentation->setSummary($summary);
449
        $documentation->setLongDescription($longDescription);
450
        return $documentation;
451
    }
452
}
453