JSONDataFormatter::cast()   A
last analyzed

Complexity

Conditions 5
Paths 5

Size

Total Lines 13
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
eloc 10
nc 5
nop 1
dl 0
loc 13
rs 9.6111
c 0
b 0
f 0
1
<?php
2
3
namespace SilverStripe\RestfulServer\DataFormatter;
4
5
use SilverStripe\RestfulServer\RestfulServer;
6
use SilverStripe\View\ArrayData;
7
use SilverStripe\Core\Convert;
8
use SilverStripe\RestfulServer\DataFormatter;
9
use SilverStripe\ORM\DataObjectInterface;
10
use SilverStripe\Control\Director;
11
use SilverStripe\ORM\SS_List;
12
use SilverStripe\ORM\FieldType;
13
14
/**
15
 * Formats a DataObject's member fields into a JSON string
16
 */
17
class JSONDataFormatter extends DataFormatter
18
{
19
    /**
20
     * @config
21
     * @todo pass this from the API to the data formatter somehow
22
     */
23
    private static $api_base = "api/v1/";
24
25
    protected $outputContentType = 'application/json';
26
27
    /**
28
     * @return array
29
     */
30
    public function supportedExtensions()
31
    {
32
        return array(
33
            'json',
34
            'js'
35
        );
36
    }
37
38
    /**
39
     * @return array
40
     */
41
    public function supportedMimeTypes()
42
    {
43
        return array(
44
            'application/json',
45
            'text/x-json'
46
        );
47
    }
48
49
    /**
50
     * @param $array
51
     * @return string
52
     */
53
    public function convertArray($array)
54
    {
55
        return json_encode($array);
56
    }
57
58
    /**
59
     * Generate a JSON representation of the given {@link DataObject}.
60
     *
61
     * @param DataObject $obj   The object
0 ignored issues
show
Bug introduced by
The type SilverStripe\RestfulServ...ataFormatter\DataObject was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
62
     * @param Array $fields     If supplied, only fields in the list will be returned
63
     * @param $relations        Not used
0 ignored issues
show
Bug introduced by
The type SilverStripe\RestfulServer\DataFormatter\Not was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
64
     * @return String JSON
65
     */
66
    public function convertDataObject(DataObjectInterface $obj, $fields = null, $relations = null)
67
    {
68
        return json_encode($this->convertDataObjectToJSONObject($obj, $fields, $relations));
69
    }
70
71
    /**
72
     * Internal function to do the conversion of a single data object. It builds an empty object and dynamically
73
     * adds the properties it needs to it. If it's done as a nested array, json_encode or equivalent won't use
74
     * JSON object notation { ... }.
75
     * @param DataObjectInterface $obj
76
     * @param  $fields
77
     * @param  $relations
78
     * @return EmptyJSONObject
0 ignored issues
show
Bug introduced by
The type SilverStripe\RestfulServ...rmatter\EmptyJSONObject was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
79
     */
80
    public function convertDataObjectToJSONObject(DataObjectInterface $obj, $fields = null, $relations = null)
81
    {
82
        $className = get_class($obj);
83
        $id = $obj->ID;
84
85
        $serobj = ArrayData::array_to_object();
86
87
        foreach ($this->getFieldsForObj($obj) as $fieldName => $fieldType) {
88
            // Field filtering
89
            if ($fields && !in_array($fieldName, $fields)) {
90
                continue;
91
            }
92
93
            $fieldValue = self::cast($obj->obj($fieldName));
94
            $mappedFieldName = $this->getFieldAlias($className, $fieldName);
95
            $serobj->$mappedFieldName = $fieldValue;
96
        }
97
98
        if ($this->relationDepth > 0) {
99
            foreach ($obj->hasOne() as $relName => $relClass) {
100
                if (!singleton($relClass)->stat('api_access')) {
101
                    continue;
102
                }
103
104
                // Field filtering
105
                if ($fields && !in_array($relName, $fields)) {
106
                    continue;
107
                }
108
                if ($this->customRelations && !in_array($relName, $this->customRelations)) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->customRelations of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
109
                    continue;
110
                }
111
                if ($obj->$relName() && (!$obj->$relName()->exists() || !$obj->$relName()->canView())) {
112
                    continue;
113
                }
114
115
                $fieldName = $relName . 'ID';
116
                $rel = $this->config()->api_base;
117
                $rel .= $obj->$fieldName
118
                    ? $this->sanitiseClassName($relClass) . '/' . $obj->$fieldName
119
                    : $this->sanitiseClassName($className) . "/$id/$relName";
120
                $href = Director::absoluteURL($rel);
121
                $serobj->$relName = ArrayData::array_to_object(array(
122
                    "className" => $relClass,
123
                    "href" => "$href.json",
124
                    "id" => self::cast($obj->obj($fieldName))
125
                ));
126
            }
127
128
            foreach ($obj->hasMany() + $obj->manyMany() as $relName => $relClass) {
129
                $relClass = RestfulServer::parseRelationClass($relClass);
130
131
                //remove dot notation from relation names
132
                $parts = explode('.', $relClass);
0 ignored issues
show
Bug introduced by
It seems like $relClass can also be of type array; however, parameter $string of explode() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

132
                $parts = explode('.', /** @scrutinizer ignore-type */ $relClass);
Loading history...
133
                $relClass = array_shift($parts);
134
135
                if (!singleton($relClass)->stat('api_access')) {
136
                    continue;
137
                }
138
139
                // Field filtering
140
                if ($fields && !in_array($relName, $fields)) {
141
                    continue;
142
                }
143
                if ($this->customRelations && !in_array($relName, $this->customRelations)) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->customRelations of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
144
                    continue;
145
                }
146
147
                $innerParts = array();
148
                $items = $obj->$relName();
149
                foreach ($items as $item) {
150
                    if (!$item->canView()) {
151
                        continue;
152
                    }
153
                    $rel = $this->config()->api_base . $this->sanitiseClassName($relClass) . "/$item->ID";
154
                    $href = Director::absoluteURL($rel);
155
                    $innerParts[] = ArrayData::array_to_object(array(
156
                        "className" => $relClass,
157
                        "href" => "$href.json",
158
                        "id" => $item->ID
159
                    ));
160
                }
161
                $serobj->$relName = $innerParts;
162
            }
163
        }
164
165
        return $serobj;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $serobj returns the type stdClass which is incompatible with the documented return type SilverStripe\RestfulServ...rmatter\EmptyJSONObject.
Loading history...
166
    }
167
168
    /**
169
     * Generate a JSON representation of the given {@link SS_List}.
170
     *
171
     * @param SS_List $set
172
     * @return String XML
173
     */
174
    public function convertDataObjectSet(SS_List $set, $fields = null)
175
    {
176
        $items = array();
177
        foreach ($set as $do) {
178
            if (!$do->canView()) {
179
                continue;
180
            }
181
            $items[] = $this->convertDataObjectToJSONObject($do, $fields);
182
        }
183
184
        $serobj = ArrayData::array_to_object(array(
185
            "totalSize" => (is_numeric($this->totalSize)) ? $this->totalSize : null,
0 ignored issues
show
introduced by
The condition is_numeric($this->totalSize) is always true.
Loading history...
186
            "items" => $items
187
        ));
188
189
        return json_encode($serobj);
190
    }
191
192
    /**
193
     * @param string $strData
194
     * @return array|bool|void
195
     */
196
    public function convertStringToArray($strData)
197
    {
198
        return json_decode($strData, true);
199
    }
200
201
    public static function cast(FieldType\DBField $dbfield)
202
    {
203
        switch (true) {
204
            case $dbfield instanceof FieldType\DBInt:
205
                return (int)$dbfield->RAW();
206
            case $dbfield instanceof FieldType\DBFloat:
207
                return (float)$dbfield->RAW();
208
            case $dbfield instanceof FieldType\DBBoolean:
209
                return (bool)$dbfield->RAW();
210
            case is_null($dbfield->RAW()):
211
                return null;
212
        }
213
        return $dbfield->RAW();
214
    }
215
}
216