Passed
Pull Request — master (#206)
by Alex
04:45
created

SerialiseLowLevelWritersTrait::writeBagValue()   B

Complexity

Conditions 10
Paths 8

Size

Total Lines 29
Code Lines 21

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 21
c 1
b 0
f 0
dl 0
loc 29
rs 7.6666
cc 10
nc 8
nop 2

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * Created by PhpStorm.
4
 * User: alex
5
 * Date: 16/02/20
6
 * Time: 12:12 AM
7
 */
8
9
namespace AlgoWeb\PODataLaravel\Serialisers;
10
11
use Illuminate\Database\Eloquent\Model;
12
use POData\Common\InvalidOperationException;
13
use POData\Common\Messages;
14
use POData\ObjectModel\ODataBagContent;
15
use POData\ObjectModel\ODataProperty;
16
use POData\ObjectModel\ODataPropertyContent;
17
use POData\Providers\Metadata\ResourceProperty;
18
use POData\Providers\Metadata\ResourcePropertyKind;
19
use POData\Providers\Metadata\ResourceType;
20
use POData\Providers\Metadata\ResourceTypeKind;
21
use POData\Providers\Metadata\Type\Binary;
22
use POData\Providers\Metadata\Type\Boolean;
23
use POData\Providers\Metadata\Type\DateTime;
24
use POData\Providers\Metadata\Type\IType;
25
use POData\Providers\Metadata\Type\StringType;
26
27
trait SerialiseLowLevelWritersTrait
28
{
29
    /**
30
     * Collection of complex type instances used for cycle detection.
31
     *
32
     * @var array
33
     */
34
    protected $complexTypeInstanceCollection;
35
36
    /**
37
     * @param $entryObject
38
     * @param $nonRelProp
39
     * @return ODataPropertyContent
40
     * @throws InvalidOperationException
41
     */
42
    protected function writePrimitiveProperties(Model $entryObject, $nonRelProp)
43
    {
44
        $propertyContent = new ODataPropertyContent();
45
        $cereal = $this->getModelSerialiser()->bulkSerialise($entryObject);
46
        $cereal = array_intersect_key($cereal, $nonRelProp);
47
48
        foreach ($cereal as $corn => $flake) {
49
            $corn = strval($corn);
50
            $rType = $nonRelProp[$corn]['type'];
51
            /** @var ResourceProperty $nrp */
52
            $nrp = $nonRelProp[$corn]['prop'];
53
            $subProp = new ODataProperty();
54
            $subProp->name = $corn;
55
            $subProp->value = isset($flake) ? $this->primitiveToString($rType, $flake) : null;
56
            $subProp->typeName = $nrp->getResourceType()->getFullName();
57
            $propertyContent->properties[$corn] = $subProp;
58
        }
59
        return $propertyContent;
60
    }
61
62
    /**
63
     * @param ResourceType $resourceType
64
     * @param $result
65
     * @return ODataBagContent|null
66
     * @throws InvalidOperationException
67
     * @throws \ReflectionException
68
     */
69
    protected function writeBagValue(ResourceType &$resourceType, $result)
70
    {
71
        if (!(null == $result || is_array($result))) {
72
            throw new InvalidOperationException('Bag parameter must be null or array');
73
        }
74
        $typeKind = $resourceType->getResourceTypeKind();
75
        $kVal = $typeKind;
76
        if (!(ResourceTypeKind::PRIMITIVE() == $kVal || ResourceTypeKind::COMPLEX() == $kVal)) {
77
            $msg = '$bagItemResourceTypeKind != ResourceTypeKind::PRIMITIVE'
78
                   .' && $bagItemResourceTypeKind != ResourceTypeKind::COMPLEX';
79
            throw new InvalidOperationException($msg);
80
        }
81
        if (null == $result) {
82
            return null;
83
        }
84
        $bag = new ODataBagContent();
85
        $result = array_filter($result);
86
        foreach ($result as $value) {
87
            if (ResourceTypeKind::PRIMITIVE() == $kVal) {
88
                $instance = $resourceType->getInstanceType();
89
                if (!$instance instanceof IType) {
90
                    throw new InvalidOperationException(get_class($instance));
91
                }
92
                $bag->propertyContents[] = $this->primitiveToString($instance, $value);
93
            } elseif (ResourceTypeKind::COMPLEX() == $kVal) {
94
                $bag->propertyContents[] = $this->writeComplexValue($resourceType, $value);
95
            }
96
        }
97
        return $bag;
98
    }
99
100
    /**
101
     * @param  ResourceType         $resourceType
102
     * @param  object               $result
103
     * @param  string|null          $propertyName
104
     * @return ODataPropertyContent
105
     * @throws InvalidOperationException
106
     * @throws \ReflectionException
107
     */
108
    protected function writeComplexValue(ResourceType &$resourceType, &$result, $propertyName = null)
109
    {
110
        if (!is_object($result)) {
111
            throw new InvalidOperationException('Supplied $customObject must be an object');
112
        }
113
114
        $count = count($this->complexTypeInstanceCollection);
115
        for ($i = 0; $i < $count; ++$i) {
116
            if ($this->complexTypeInstanceCollection[$i] === $result) {
117
                throw new InvalidOperationException(
118
                    Messages::objectModelSerializerLoopsNotAllowedInComplexTypes($propertyName)
119
                );
120
            }
121
        }
122
123
        $this->complexTypeInstanceCollection[$count] = &$result;
124
125
        $internalContent = new ODataPropertyContent();
126
        $resourceProperties = $resourceType->getAllProperties();
127
        // first up, handle primitive properties
128
        foreach ($resourceProperties as $prop) {
129
            $resourceKind = $prop->getKind();
130
            $propName = $prop->getName();
131
            $internalProperty = new ODataProperty();
132
            $internalProperty->name = $propName;
133
            if (static::isMatchPrimitive($resourceKind)) {
134
                $iType = $prop->getInstanceType();
135
                if (!$iType instanceof IType) {
136
                    throw new InvalidOperationException(get_class($iType));
137
                }
138
                $internalProperty->typeName = $iType->getFullTypeName();
139
140
                $rType = $prop->getResourceType()->getInstanceType();
141
                if (!$rType instanceof IType) {
142
                    throw new InvalidOperationException(get_class($rType));
143
                }
144
145
                $internalProperty->value = $this->primitiveToString($rType, $result->$propName);
146
147
                $internalContent->properties[$propName] = $internalProperty;
148
            } elseif (ResourcePropertyKind::COMPLEX_TYPE == $resourceKind) {
149
                $rType = $prop->getResourceType();
150
                $internalProperty->typeName = $rType->getFullName();
151
                $internalProperty->value = $this->writeComplexValue($rType, $result->$propName, $propName);
152
153
                $internalContent->properties[$propName] = $internalProperty;
154
            }
155
        }
156
157
        unset($this->complexTypeInstanceCollection[$count]);
158
        return $internalContent;
159
    }
160
161
162
    /**
163
     * Convert the given primitive value to string.
164
     * Note: This method will not handle null primitive value.
165
     *
166
     * @param IType &$type                  Type of the primitive property needing conversion
167
     * @param mixed $primitiveValue         Primitive value to convert
168
     *
169
     * @return string
170
     */
171
    protected function primitiveToString(IType &$type, $primitiveValue)
172
    {
173
        // kludge to enable switching on type of $type without getting tripped up by mocks as we would with get_class
174
        // switch (true) means we unconditionally enter, and then lean on case statements to match given block
175
        switch (true) {
176
            case $type instanceof StringType:
177
                $stringValue = utf8_encode($primitiveValue);
178
                break;
179
            case $type instanceof Boolean:
180
                $stringValue = (true === $primitiveValue) ? 'true' : 'false';
181
                break;
182
            case $type instanceof Binary:
183
                $stringValue = base64_encode($primitiveValue);
184
                break;
185
            case $type instanceof DateTime && $primitiveValue instanceof \DateTime:
186
                $stringValue = $primitiveValue->format(\DateTime::ATOM);
187
                break;
188
            default:
189
                $stringValue = strval($primitiveValue);
190
        }
191
192
        return $stringValue;
193
    }
194
195
    /**
196
     * @return ModelSerialiser
197
     */
198
    abstract public function getModelSerialiser();
199
}
200