Completed
Push — 3.7 ( 81b2d8...ef0909 )
by
unknown
09:42
created

DataFormatter   A

Complexity

Total Complexity 36

Size/Duplication

Total Lines 295
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 3

Importance

Changes 0
Metric Value
dl 0
loc 295
rs 9.52
c 0
b 0
f 0
wmc 36
lcom 1
cbo 3

21 Methods

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