Completed
Pull Request — master (#100)
by Alex
18:05
created

MetadataManager::__sleep()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 6
rs 9.4285
cc 1
eloc 4
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\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 Illuminate\Support\Str;
19
use JMS\Serializer\SerializerBuilder;
20
21
class MetadataManager
22
{
23
    private $V3Edmx = null;
24
    private $oldEdmx = null;
0 ignored issues
show
Unused Code introduced by
The property $oldEdmx is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

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