Completed
Push — master ( e5a757...47ec18 )
by Robbie
14s
created

DataFormatter::getFieldsForObj()   D

Complexity

Conditions 10
Paths 8

Size

Total Lines 35
Code Lines 15

Duplication

Lines 12
Ratio 34.29 %

Importance

Changes 0
Metric Value
dl 12
loc 35
rs 4.8196
c 0
b 0
f 0
cc 10
eloc 15
nc 8
nop 1

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
namespace SilverStripe\RestfulServer;
4
5
use SilverStripe\Core\ClassInfo;
6
use SilverStripe\Core\Config\Configurable;
7
use SilverStripe\ORM\DataObject;
8
use SilverStripe\ORM\DataObjectInterface;
9
use SilverStripe\ORM\SS_List;
10
11
/**
12
 * A DataFormatter object handles transformation of data from SilverStripe model objects to a particular output
13
 * format, and vice versa.  This is most commonly used in developing RESTful APIs.
14
 */
15
abstract class DataFormatter
16
{
17
18
    use Configurable;
19
20
    /**
21
     * Set priority from 0-100.
22
     * If multiple formatters for the same extension exist,
23
     * we select the one with highest priority.
24
     *
25
     * @var int
26
     */
27
    private static $priority = 50;
0 ignored issues
show
introduced by
The private property $priority is not used, and could be removed.
Loading history...
28
29
    /**
30
     * Follow relations for the {@link DataObject} instances
31
     * ($has_one, $has_many, $many_many).
32
     * Set to "0" to disable relation output.
33
     *
34
     * @todo Support more than one nesting level
35
     *
36
     * @var int
37
     */
38
    public $relationDepth = 1;
39
40
    /**
41
     * Allows overriding of the fields which are rendered for the
42
     * processed dataobjects. By default, this includes all
43
     * fields in {@link DataObject::inheritedDatabaseFields()}.
44
     *
45
     * @var array
46
     */
47
    protected $customFields = null;
48
49
    /**
50
     * Allows addition of fields
51
     * (e.g. custom getters on a DataObject)
52
     *
53
     * @var array
54
     */
55
    protected $customAddFields = null;
56
57
    /**
58
     * Allows to limit or add relations.
59
     * Only use in combination with {@link $relationDepth}.
60
     * By default, all relations will be shown.
61
     *
62
     * @var array
63
     */
64
    protected $customRelations = null;
65
66
    /**
67
     * Fields which should be expicitly excluded from the export.
68
     * Comes in handy for field-level permissions.
69
     * Will overrule both {@link $customAddFields} and {@link $customFields}
70
     *
71
     * @var array
72
     */
73
    protected $removeFields = null;
74
75
    /**
76
     * Specifies the mimetype in which all strings
77
     * returned from the convert*() methods should be used,
78
     * e.g. "text/xml".
79
     *
80
     * @var string
81
     */
82
    protected $outputContentType = null;
83
84
    /**
85
     * Used to set totalSize properties on the output
86
     * of {@link convertDataObjectSet()}, shows the
87
     * total number of records without the "limit" and "offset"
88
     * GET parameters. Useful to implement pagination.
89
     *
90
     * @var int
91
     */
92
    protected $totalSize;
93
94
    /**
95
     * Backslashes in fully qualified class names (e.g. NameSpaced\ClassName)
96
     * kills both requests (i.e. URIs) and XML (invalid character in a tag name)
97
     * So we'll replace them with a hyphen (-), as it's also unambiguious
98
     * in both cases (invalid in a php class name, and safe in an xml tag name)
99
     *
100
     * @param string $classname
101
     * @return string 'escaped' class name
102
     */
103
    protected function sanitiseClassName($className)
104
    {
105
        return str_replace('\\', '-', $className);
106
    }
107
108
    /**
109
     * Get a DataFormatter object suitable for handling the given file extension.
110
     *
111
     * @param string $extension
112
     * @return DataFormatter
113
     */
114 View Code Duplication
    public static function for_extension($extension)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
115
    {
116
        $classes = ClassInfo::subclassesFor(DataFormatter::class);
117
        array_shift($classes);
118
        $sortedClasses = array();
119
        foreach ($classes as $class) {
120
            $sortedClasses[$class] = singleton($class)->stat('priority');
121
        }
122
        arsort($sortedClasses);
123
        foreach ($sortedClasses as $className => $priority) {
124
            $formatter = new $className();
125
            if (in_array($extension, $formatter->supportedExtensions())) {
126
                return $formatter;
127
            }
128
        }
129
    }
130
131
    /**
132
     * Get formatter for the first matching extension.
133
     *
134
     * @param array $extensions
135
     * @return DataFormatter
136
     */
137
    public static function for_extensions($extensions)
138
    {
139
        foreach ($extensions as $extension) {
140
            if ($formatter = self::for_extension($extension)) {
141
                return $formatter;
142
            }
143
        }
144
145
        return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type SilverStripe\RestfulServer\DataFormatter.
Loading history...
146
    }
147
148
    /**
149
     * Get a DataFormatter object suitable for handling the given mimetype.
150
     *
151
     * @param string $mimeType
152
     * @return DataFormatter
153
     */
154 View Code Duplication
    public static function for_mimetype($mimeType)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
155
    {
156
        $classes = ClassInfo::subclassesFor(DataFormatter::class);
157
        array_shift($classes);
158
        $sortedClasses = array();
159
        foreach ($classes as $class) {
160
            $sortedClasses[$class] = singleton($class)->stat('priority');
161
        }
162
        arsort($sortedClasses);
163
        foreach ($sortedClasses as $className => $priority) {
164
            $formatter = new $className();
165
            if (in_array($mimeType, $formatter->supportedMimeTypes())) {
166
                return $formatter;
167
            }
168
        }
169
    }
170
171
    /**
172
     * Get formatter for the first matching mimetype.
173
     * Useful for HTTP Accept headers which can contain
174
     * multiple comma-separated mimetypes.
175
     *
176
     * @param array $mimetypes
177
     * @return DataFormatter
178
     */
179
    public static function for_mimetypes($mimetypes)
180
    {
181
        foreach ($mimetypes as $mimetype) {
182
            if ($formatter = self::for_mimetype($mimetype)) {
183
                return $formatter;
184
            }
185
        }
186
187
        return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type SilverStripe\RestfulServer\DataFormatter.
Loading history...
188
    }
189
190
    /**
191
     * @param array $fields
192
     */
193
    public function setCustomFields($fields)
194
    {
195
        $this->customFields = $fields;
196
    }
197
198
    /**
199
     * @return array
200
     */
201
    public function getCustomFields()
202
    {
203
        return $this->customFields;
204
    }
205
206
    /**
207
     * @param array $fields
208
     */
209
    public function setCustomAddFields($fields)
210
    {
211
        $this->customAddFields = $fields;
212
    }
213
214
    /**
215
     * @param array $relations
216
     */
217
    public function setCustomRelations($relations)
218
    {
219
        $this->customRelations = $relations;
220
    }
221
222
    /**
223
     * @return array
224
     */
225
    public function getCustomRelations()
226
    {
227
        return $this->customRelations;
228
    }
229
230
    /**
231
     * @return array
232
     */
233
    public function getCustomAddFields()
234
    {
235
        return $this->customAddFields;
236
    }
237
238
    /**
239
     * @param array $fields
240
     */
241
    public function setRemoveFields($fields)
242
    {
243
        $this->removeFields = $fields;
244
    }
245
246
    /**
247
     * @return array
248
     */
249
    public function getRemoveFields()
250
    {
251
        return $this->removeFields;
252
    }
253
254
    public function getOutputContentType()
255
    {
256
        return $this->outputContentType;
257
    }
258
259
    /**
260
     * @param int $size
261
     */
262
    public function setTotalSize($size)
263
    {
264
        $this->totalSize = (int)$size;
265
    }
266
267
    /**
268
     * @return int
269
     */
270
    public function getTotalSize()
271
    {
272
        return $this->totalSize;
273
    }
274
275
    /**
276
     * Returns all fields on the object which should be shown
277
     * in the output. Can be customised through {@link self::setCustomFields()}.
278
     *
279
     * @todo Allow for custom getters on the processed object (currently filtered through inheritedDatabaseFields)
280
     * @todo Field level permission checks
281
     *
282
     * @param DataObject $obj
283
     * @return array
284
     */
285
    protected function getFieldsForObj($obj)
286
    {
287
        $dbFields = array();
288
289
        // if custom fields are specified, only select these
290
        if (is_array($this->customFields)) {
291 View Code Duplication
            foreach ($this->customFields as $fieldName) {
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...
292
                // @todo Possible security risk by making methods accessible - implement field-level security
293
                if ($obj->hasField($fieldName) || $obj->hasMethod("get{$fieldName}")) {
294
                    $dbFields[$fieldName] = $fieldName;
295
                }
296
            }
297
        } else {
298
            // by default, all database fields are selected
299
            $dbFields = DataObject::getSchema()->fieldSpecs(get_class($obj));
300
            // $dbFields = $obj->inheritedDatabaseFields();
0 ignored issues
show
Unused Code Comprehensibility introduced by
55% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
301
        }
302
303
        if (is_array($this->customAddFields)) {
304 View Code Duplication
            foreach ($this->customAddFields as $fieldName) {
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...
305
                // @todo Possible security risk by making methods accessible - implement field-level security
306
                if ($obj->hasField($fieldName) || $obj->hasMethod("get{$fieldName}")) {
307
                    $dbFields[$fieldName] = $fieldName;
308
                }
309
            }
310
        }
311
312
        // add default required fields
313
        $dbFields = array_merge($dbFields, array('ID'=>'Int'));
314
315
        if (is_array($this->removeFields)) {
316
            $dbFields = array_diff_key($dbFields, array_combine($this->removeFields, $this->removeFields));
317
        }
318
319
        return $dbFields;
320
    }
321
322
    /**
323
     * Return an array of the extensions that this data formatter supports
324
     */
325
    abstract public function supportedExtensions();
326
327
    abstract public function supportedMimeTypes();
328
329
330
    /**
331
     * Convert a single data object to this format.  Return a string.
332
     */
333
    abstract public function convertDataObject(DataObjectInterface $do);
334
335
    /**
336
     * Convert a data object set to this format.  Return a string.
337
     */
338
    abstract public function convertDataObjectSet(SS_List $set);
339
340
    /**
341
     * @param string $strData HTTP Payload as string
342
     */
343
    public function convertStringToArray($strData)
0 ignored issues
show
Unused Code introduced by
The parameter $strData is not used and could be removed. ( Ignorable by Annotation )

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

343
    public function convertStringToArray(/** @scrutinizer ignore-unused */ $strData)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
344
    {
345
        user_error('DataFormatter::convertStringToArray not implemented on subclass', E_USER_ERROR);
346
    }
347
}
348