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

MetadataManager::initSerialiser()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 8
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 8
rs 9.4285
cc 1
eloc 6
nc 1
nop 0
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
        $storeGeneratedPattern = null,
108
        $summary = null,
109
        $longDescription = null
110
    ) {
111
        $NewProperty = new TComplexTypePropertyType();
112
        $NewProperty->setName($name);
113
        $NewProperty->setType($type);
114
        $NewProperty->setStoreGeneratedPattern($storeGeneratedPattern);
0 ignored issues
show
Bug introduced by
The method setStoreGeneratedPattern() does not seem to exist on object<AlgoWeb\ODataMeta...omplexTypePropertyType>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
115
        $NewProperty->setNullable($nullable);
116
        if (null != $summary || null != $longDescription) {
117
            $documentation = $this->generateDocumentation($summary, $longDescription);
118
            $NewProperty->addToDocumentation($documentation);
119
        }
120
        if (null != $defaultValue) {
121
            $NewProperty->setDefaultValue($defaultValue);
122
        }
123
        $complexType->addToProperty($NewProperty);
124
        return $NewProperty;
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
        $NewProperty = new TEntityPropertyType();
139
        $NewProperty->setName($name);
140
        $NewProperty->setType($type);
141
        $NewProperty->setStoreGeneratedPattern($storeGeneratedPattern);
142
        $NewProperty->setNullable($nullable);
143 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...
144
            $documentation = new TDocumentationType();
145
            $documentation->setSummary($summary);
146
            $documentation->setLongDescription($longDescription);
147
            $NewProperty->addToDocumentation($documentation);
148
        }
149
        if (null != $defaultValue) {
150
            $NewProperty->setDefaultValue($defaultValue);
151
        }
152
        $entityType->addToProperty($NewProperty);
153
        if ($isKey) {
154
            $Key = new TPropertyRefType();
155
            $Key->setName($name);
156
            $entityType->addToKey($Key);
157
        }
158
        if (!$this->V3Edmx->isok($this->lastError)) {
159
            return false;
160
        }
161
        return $NewProperty;
162
    }
163
164
    public function addNavigationPropertyToEntityType(
165
        TEntityTypeType $principalType,
166
        $principalMultiplicity,
167
        $principalProperty,
168
        TEntityTypeType $dependentType,
169
        $dependentMultiplicity,
170
        $dependentProperty = "",
171
        array $principalConstraintProperty = null,
172
        array $dependentConstraintProperty = null,
173
        $principalGetterAccess = "Public",
174
        $principalSetterAccess = "Public",
175
        $dependentGetterAccess = "Public",
176
        $dependentSetterAccess = "Public",
177
        $principalSummery = null,
178
        $principalLongDescription = null,
179
        $dependentSummery = null,
180
        $dependentLongDescription = null
181
    ) {
182
        $principalEntitySetName = Str::plural($principalType->getName(), 2);
183
        $dependentEntitySetName = Str::plural($dependentType->getName(), 2);
184
        $relationName = $principalType->getName() . "_" . $principalProperty . "_"
185
                        . $dependentType->getName() . "_" . $dependentProperty;
186
        $relationName = trim($relationName, "_");
187
188
        $namespace = $this->V3Edmx->getDataServiceType()->getSchema()[0]->getNamespace();
189
        if (0 == strlen(trim($namespace))) {
190
            $relationFQName = $relationName;
191
        } else {
192
            $relationFQName = $namespace . "." . $relationName;
193
        }
194
195
        $principalNavigationProperty = new TNavigationPropertyType();
196
        $principalNavigationProperty->setName($principalProperty);
197
        $principalNavigationProperty->setToRole(trim($dependentEntitySetName . "_" . $dependentProperty, "_"));
198
        $principalNavigationProperty->setFromRole($principalEntitySetName . "_" . $principalProperty);
199
        $principalNavigationProperty->setRelationship($relationFQName);
200
        $principalNavigationProperty->setGetterAccess($principalGetterAccess);
201
        $principalNavigationProperty->setSetterAccess($principalSetterAccess);
202
        if (null != $principalSummery || null != $principalLongDescription) {
203
            $principalDocumentation = new TDocumentationType();
204
            $principalDocumentation->setSummary($principalSummery);
205
            $principalDocumentation->setLongDescription($principalLongDescription);
206
            $principalNavigationProperty->setDocumentation($principalDocumentation);
207
        }
208
        $principalType->addToNavigationProperty($principalNavigationProperty);
209
        $dependentNavigationProperty = null;
210
        if (!empty($dependentProperty)) {
211
            $dependentNavigationProperty = new TNavigationPropertyType();
212
            $dependentNavigationProperty->setName($dependentProperty);
213
            $dependentNavigationProperty->setToRole($principalEntitySetName . "_" . $principalProperty);
214
            $dependentNavigationProperty->setFromRole($dependentEntitySetName . "_" . $dependentProperty);
215
            $dependentNavigationProperty->setRelationship($relationFQName);
216
            $dependentNavigationProperty->setGetterAccess($dependentGetterAccess);
217
            $dependentNavigationProperty->setSetterAccess($dependentSetterAccess);
218
            if (null != $dependentSummery || null != $dependentLongDescription) {
219
                $dependentDocumentation = new TDocumentationType();
220
                $dependentDocumentation->setSummary($dependentSummery);
221
                $dependentDocumentation->setLongDescription($dependentLongDescription);
222
                $dependentNavigationProperty->setDocumentation($dependentDocumentation);
223
            }
224
            $dependentType->addToNavigationProperty($dependentNavigationProperty);
225
        }
226
227
        $assocation = $this->createAssocationFromNavigationProperty(
228
            $principalType,
229
            $dependentType,
230
            $principalNavigationProperty,
231
            $dependentNavigationProperty,
232
            $principalMultiplicity,
233
            $dependentMultiplicity,
234
            $principalConstraintProperty,
235
            $dependentConstraintProperty
236
        );
237
238
        $this->V3Edmx->getDataServiceType()->getSchema()[0]->addToAssociation($assocation);
239
240
        $associationSet = $this->createAssocationSetForAssocation(
241
            $assocation,
242
            $principalEntitySetName,
243
            $dependentEntitySetName
244
        );
245
246
        $this->V3Edmx->getDataServiceType()->getSchema()[0]
247
            ->getEntityContainer()[0]->addToAssociationSet($associationSet);
248
249
        if (!$this->V3Edmx->isok($this->lastError)) {
250
            return false;
251
        }
252
        return [$principalNavigationProperty, $dependentNavigationProperty];
253
    }
254
255
    protected function createAssocationFromNavigationProperty(
256
        TEntityTypeType $principalType,
257
        TEntityTypeType $dependentType,
258
        TNavigationPropertyType $principalNavigationProperty,
259
        TNavigationPropertyType $dependentNavigationProperty = null,
260
        $principalMultiplicity,
261
        $dependentMultiplicity,
262
        array $principalConstraintProperty = null,
263
        array $dependentConstraintProperty = null
264
    ) {
265
        if (null != $dependentNavigationProperty) {
266
            if ($dependentNavigationProperty->getRelationship() != $principalNavigationProperty->getRelationship()) {
267
                $msg = "if you have both a dependent property and a principal property,"
268
                       ." they should both have the same relationship";
269
                throw new \Exception($msg);
270
            }
271
            if ($dependentNavigationProperty->getFromRole() != $principalNavigationProperty->getToRole() ||
272
                $dependentNavigationProperty->getToRole() != $principalNavigationProperty->getFromRole()
273
            ) {
274
                throw new \Exception("The from roles and two roles from matching properties should match");
275
            }
276
        }
277
        $namespace = $this->V3Edmx->getDataServiceType()->getSchema()[0]->getNamespace();
278
279
        if (0 == strlen(trim($namespace))) {
280
            $principalTypeFQName = $principalType->getName();
281
            $dependentTypeFQName = $dependentType->getName();
282
        } else {
283
            $principalTypeFQName = $namespace . "." . $principalType->getName();
284
            $dependentTypeFQName = $namespace . "." . $dependentType->getName();
285
        }
286
        $association = new TAssociationType();
287
        $relationship = $principalNavigationProperty->getRelationship();
288
        if (strpos($relationship, '.') !== false) {
289
            $relationship = substr($relationship, strpos($relationship, '.') + 1);
290
        }
291
292
        $association->setName($relationship);
293
        $principalEnd = new TAssociationEndType();
294
        $principalEnd->setType($principalTypeFQName);
295
        $principalEnd->setRole($principalNavigationProperty->getFromRole());
296
        $principalEnd->setMultiplicity($principalMultiplicity);
297
        $association->addToEnd($principalEnd);
298
        $dependentEnd = new TAssociationEndType();
299
        $dependentEnd->setType($dependentTypeFQName);
300
        $dependentEnd->setMultiplicity($dependentMultiplicity);
301
        $association->addToEnd($dependentEnd);
302
303
        if (null != $dependentNavigationProperty) {
304
            $dependentEnd->setRole($dependentNavigationProperty->getFromRole());
305
        } else {
306
            $dependentEnd->setRole($principalNavigationProperty->getToRole());
307
        }
308
309
        $principalReferralConstraint = null;
310
        $dependentReferralConstraint = null;
311
312 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...
313
            $principalReferralConstraint = new TReferentialConstraintRoleElementType();
314
            $principalReferralConstraint->setRole($principalNavigationProperty->getFromRole());
315
            foreach ($principalConstraintProperty as $propertyRef) {
316
                $TpropertyRef = new TPropertyRefType();
317
                $TpropertyRef->setName($propertyRef);
318
                $principalReferralConstraint->addToPropertyRef($TpropertyRef);
319
            }
320
        }
321 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...
322
            $dependentReferralConstraint = new TReferentialConstraintRoleElementType();
323
            $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...
324
            foreach ($dependentConstraintProperty as $propertyRef) {
325
                $TpropertyRef = new TPropertyRefType();
326
                $TpropertyRef->setName($propertyRef);
327
                $dependentReferralConstraint->addToPropertyRef($TpropertyRef);
328
            }
329
        }
330
331
        if (null != $dependentReferralConstraint || null != $principalReferralConstraint) {
332
            $constraint = new TConstraintType();
333
            $constraint->setPrincipal($principalReferralConstraint);
0 ignored issues
show
Bug introduced by
It seems like $principalReferralConstraint defined by null on line 309 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...
334
            $constraint->setDependent($dependentReferralConstraint);
0 ignored issues
show
Bug introduced by
It seems like $dependentReferralConstraint defined by null on line 310 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...
335
            $association->setReferentialConstraint($constraint);
336
        }
337
        return $association;
338
    }
339
340
    protected function createAssocationSetForAssocation(
341
        TAssociationType $association,
342
        $principalEntitySetName,
343
        $dependentEntitySetName
344
    ) {
345
        $as = new AssociationSetAnonymousType();
346
        $name = $association->getName();
347
        $as->setName($name);
348
        $namespace = $this->V3Edmx->getDataServiceType()->getSchema()[0]->getNamespace();
349 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...
350
            $associationSetName = $association->getName();
351
        } else {
352
            $associationSetName = $namespace . "." . $association->getName();
353
        }
354
        $as->setAssociation($associationSetName);
355
        $end1 = new EndAnonymousType();
356
        $end1->setRole($association->getEnd()[0]->getRole());
357
        $end1->setEntitySet($principalEntitySetName);
358
        $end2 = new EndAnonymousType();
359
        $end2->setRole($association->getEnd()[1]->getRole());
360
        $end2->setEntitySet($dependentEntitySetName);
361
        $as->addToEnd($end1);
362
        $as->addToEnd($end2);
363
        return $as;
364
    }
365
366
    public function getLastError()
367
    {
368
        return $this->lastError;
369
    }
370
371
    private function initSerialiser()
372
    {
373
        $ymlDir = __DIR__ . DIRECTORY_SEPARATOR . "MetadataV3" . DIRECTORY_SEPARATOR . "JMSmetadata";
374
        $this->serializer =
375
            SerializerBuilder::create()
376
                ->addMetadataDir($ymlDir)
377
                ->build();
378
    }
379
380
    public function __sleep()
381
    {
382
        $this->serializer = null;
383
        $result = array_keys(get_object_vars($this));
384
        return $result;
385
    }
386
387
    public function __wakeup()
388
    {
389
        $this->initSerialiser();
390
    }
391
392
    /**
393
     * @param $summary
394
     * @param $longDescription
395
     * @return TDocumentationType
396
     */
397
    private function generateDocumentation($summary, $longDescription)
398
    {
399
        $documentation = new TDocumentationType();
400
        $documentation->setSummary($summary);
401
        $documentation->setLongDescription($longDescription);
402
        return $documentation;
403
    }
404
}
405