get($class, 'priority'); } arsort($sortedClasses); foreach ($sortedClasses as $className => $priority) { $formatter = new $className(); if (in_array($extension, $formatter->supportedExtensions() ?? [])) { return $formatter; } } } /** * Get formatter for the first matching extension. * * @param array $extensions * @return DataFormatter */ public static function for_extensions($extensions) { foreach ($extensions as $extension) { if ($formatter = self::for_extension($extension)) { return $formatter; } } return false; } /** * Get a DataFormatter object suitable for handling the given mimetype. * * @param string $mimeType * @return DataFormatter */ public static function for_mimetype($mimeType) { $classes = ClassInfo::subclassesFor(DataFormatter::class); array_shift($classes); $sortedClasses = []; foreach ($classes as $class) { $sortedClasses[$class] = Config::inst()->get($class, 'priority'); } arsort($sortedClasses); foreach ($sortedClasses as $className => $priority) { $formatter = new $className(); if (in_array($mimeType, $formatter->supportedMimeTypes() ?? [])) { return $formatter; } } } /** * Get formatter for the first matching mimetype. * Useful for HTTP Accept headers which can contain * multiple comma-separated mimetypes. * * @param array $mimetypes * @return DataFormatter */ public static function for_mimetypes($mimetypes) { foreach ($mimetypes as $mimetype) { if ($formatter = self::for_mimetype($mimetype)) { return $formatter; } } return false; } /** * @param array $fields * @return $this */ public function setCustomFields($fields) { $this->customFields = $fields; return $this; } /** * @return array */ public function getCustomFields() { return $this->customFields; } /** * @param array $fields * @return $this */ public function setCustomAddFields($fields) { $this->customAddFields = $fields; return $this; } /** * @param array $relations * @return $this */ public function setCustomRelations($relations) { $this->customRelations = $relations; return $this; } /** * @return array */ public function getCustomRelations() { return $this->customRelations; } /** * @return array */ public function getCustomAddFields() { return $this->customAddFields; } /** * @param array $fields * @return $this */ public function setRemoveFields($fields) { $this->removeFields = $fields; return $this; } /** * @return array */ public function getRemoveFields() { return $this->removeFields; } /** * @return string */ public function getOutputContentType() { return $this->outputContentType; } /** * @param int $size * @return $this */ public function setTotalSize($size) { $this->totalSize = (int)$size; return $this; } /** * @return int */ public function getTotalSize() { return $this->totalSize; } /** * Returns all fields on the object which should be shown * in the output. Can be customised through {@link self::setCustomFields()}. * * @todo Allow for custom getters on the processed object (currently filtered through inheritedDatabaseFields) * @todo Field level permission checks * * @param DataObject $obj * @return array */ protected function getFieldsForObj($obj) { $dbFields = []; // if custom fields are specified, only select these if (is_array($this->customFields)) { foreach ($this->customFields as $fieldName) { // @todo Possible security risk by making methods accessible - implement field-level security if (($obj->hasField($fieldName) && !is_object($obj->getField($fieldName))) || $obj->hasMethod("get{$fieldName}") ) { $dbFields[$fieldName] = $fieldName; } } } else { // by default, all database fields are selected $dbFields = DataObject::getSchema()->fieldSpecs(get_class($obj)); // $dbFields = $obj->inheritedDatabaseFields(); } if (is_array($this->customAddFields)) { foreach ($this->customAddFields as $fieldName) { // @todo Possible security risk by making methods accessible - implement field-level security if ($obj->hasField($fieldName) || $obj->hasMethod("get{$fieldName}")) { $dbFields[$fieldName] = $fieldName; } } } // add default required fields $dbFields = array_merge($dbFields, ['ID' => 'Int']); if (is_array($this->removeFields)) { $dbFields = array_diff_key( $dbFields ?? [], array_combine($this->removeFields ?? [], $this->removeFields ?? []) ); } return $dbFields; } /** * Return an array of the extensions that this data formatter supports */ abstract public function supportedExtensions(); abstract public function supportedMimeTypes(); /** * Convert a single data object to this format. Return a string. * * @param DataObjectInterface $do * @return mixed */ abstract public function convertDataObject(DataObjectInterface $do); /** * Convert a data object set to this format. Return a string. * * @param SS_List $set * @return string */ abstract public function convertDataObjectSet(SS_List $set); /** * Convert an array to this format. Return a string. * * @param $array * @return string */ abstract public function convertArray($array); /** * @param string $strData HTTP Payload as string */ public function convertStringToArray($strData) { user_error('DataFormatter::convertStringToArray not implemented on subclass', E_USER_ERROR); } /** * Convert an array of aliased field names to their Dataobject field name * * @param string $className * @param string[] $fields * @return string[] */ public function getRealFields($className, $fields) { $apiMapping = $this->getApiMapping($className); if (is_array($apiMapping) && is_array($fields)) { $mappedFields = []; foreach ($fields as $field) { $mappedFields[] = $this->getMappedKey($apiMapping, $field); } return $mappedFields; } return $fields; } /** * Get the DataObject field name from its alias * * @param string $className * @param string $field * @return string */ public function getRealFieldName($className, $field) { $apiMapping = $this->getApiMapping($className); return $this->getMappedKey($apiMapping, $field); } /** * Get a DataObject Field's Alias * defaults to the fieldname * * @param string $className * @param string $field * @return string */ public function getFieldAlias($className, $field) { $apiMapping = $this->getApiMapping($className); $apiMapping = array_flip($apiMapping ?? []); return $this->getMappedKey($apiMapping, $field); } /** * Get the 'api_field_mapping' config value for a class * or return an empty array * * @param string $className * @return string[]|array */ protected function getApiMapping($className) { $apiMapping = Config::inst()->get($className, 'api_field_mapping'); if ($apiMapping && is_array($apiMapping)) { return $apiMapping; } return []; } /** * Helper function to get mapped field names * * @param array $map * @param string $key * @return string */ protected function getMappedKey($map, $key) { if (is_array($map)) { if (array_key_exists($key, $map ?? [])) { return $map[$key]; } else { return $key; } } return $key; } }