Completed
Pull Request — master (#100)
by Alex
08:56
created

MetadataManager::addComplexType()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 15
Code Lines 11

Duplication

Lines 6
Ratio 40 %

Importance

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