Skip to content

Commit

Permalink
Add the feature of configuring levels of key transformation (#8)
Browse files Browse the repository at this point in the history
  • Loading branch information
specialtactics authored Mar 26, 2019
1 parent 4fd8ef8 commit 6843bbb
Show file tree
Hide file tree
Showing 2 changed files with 80 additions and 52 deletions.
45 changes: 21 additions & 24 deletions helpers/helpers.php
Original file line number Diff line number Diff line change
@@ -1,23 +1,22 @@
<?php

if (! function_exists('APIUser')) {
function APIUser()
{
if (!function_exists('APIUser')) {
function APIUser() {
$user = app('Dingo\Api\Auth\Auth')->user();

return $user;
}
}

if (! function_exists('camel_case_array_keys')) {
if (!function_exists('camel_case_array_keys')) {
/**
* Recursively camel-case an array's keys
*
* @param $array
* @param int|null $levels How many levels of an array keys to transform - by default recurse infiniately (null)
* @return array $array
*/
function camel_case_array_keys($array)
{
function camel_case_array_keys($array, $levels = null) {
foreach (array_keys($array) as $key) {
// Get a reference to the value of the key (avoid copy)
// Then remove that array element
Expand All @@ -28,8 +27,8 @@ function camel_case_array_keys($array)
$transformedKey = camel_case($key);

// Recurse
if (is_array($value)) {
$value = camel_case_array_keys($value);
if (is_array($value) && (is_null($levels) || --$levels > 0)) {
$value = camel_case_array_keys($value, $levels);
}

// Store the transformed key with the referenced value
Expand All @@ -43,15 +42,15 @@ function camel_case_array_keys($array)
}
}

if (! function_exists('snake_case_array_keys')) {
if (!function_exists('snake_case_array_keys')) {
/**
* Recursively snake-case an array's keys
*
* @param $array
* @param int|null $levels How many levels of an array keys to transform - by default recurse infiniately (null)
* @return array $array
*/
function snake_case_array_keys(array $array)
{
function snake_case_array_keys(array $array, $levels = null) {
foreach (array_keys($array) as $key) {
// Get a reference to the value of the key (avoid copy)
// Then remove that array element
Expand All @@ -62,8 +61,8 @@ function snake_case_array_keys(array $array)
$transformedKey = snake_case($key);

// Recurse
if (is_array($value)) {
$value = snake_case_array_keys($value);
if (is_array($value) && (is_null($levels) || --$levels > 0)) {
$value = snake_case_array_keys($value, $levels);
}

// Store the transformed key with the referenced value
Expand All @@ -77,47 +76,45 @@ function snake_case_array_keys(array $array)
}
}

if (! function_exists('class_basename')) {
if (!function_exists('class_basename')) {

/**
* Get the basename of a class's FQNS name. This is proven to be the fastest way to do this (for now).
*
* @param string $className
* @return string
*/
function class_basename(string $className)
{
function class_basename(string $className) {
$reflection = new ReflectionClass($className);

return $reflection->getShortName();
}
}

if (! function_exists('get_calling_method')) {
if (!function_exists('get_calling_method')) {
/**
* Get the calling method name
*
* @return string
*/
function get_calling_method()
{
function get_calling_method() {
return debug_backtrace()[1]['function'];
}
}

if (! function_exists('model_relation_name')) {

if (!function_exists('model_relation_name')) {
/**
* Converts the name of a model class to the name of the relation of this resource on another model
*
* @param string $relationType The type of relation - ie.. one to.. X ('one', 'many')
* @return string The name of the relation, as it would appear inside an eloquent model
* @throws \Exception
*/
function model_relation_name($resourceName, $relationType = 'many')
{
function model_relation_name($resourceName, $relationType = 'many') {
if ($relationType == 'many') {
return lcfirst(str_plural(class_basename($resourceName)));
} elseif ($relationType == 'one') {
}
else if ($relationType == 'one') {
return lcfirst(class_basename($resourceName));
} else {
throw new \Exception('Undefined relation type');
Expand Down
87 changes: 59 additions & 28 deletions src/Transformers/RestfulTransformer.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
use League\Fractal\TransformerAbstract;
use Specialtactics\L5Api\APIBoilerplate;
use Specialtactics\L5Api\Models\RestfulModel;
use Specialtactics\L5Api\Helpers\APIHelper;

class RestfulTransformer extends TransformerAbstract
{
Expand All @@ -16,7 +17,7 @@ class RestfulTransformer extends TransformerAbstract
/**
* Transform an object into a jsonable array
*
* @param object $model
* @param mixed $model
* @return array
* @throws \Exception
*/
Expand All @@ -39,14 +40,14 @@ public function transform($object)
* @param \stdClass $object
* @return array
*/
public function transformStdClass($object)
{
$transformed = (array) $object;
public function transformStdClass($object) {

$transformed = (array)$object;

/**
* Transform all keys to CamelCase, recursively
* Transform all keys to correct case, recursively
*/
$transformed = $this->formatCase($transformed);
$transformed = $this->transformKeysCase($transformed);

return $transformed;
}
Expand All @@ -57,8 +58,7 @@ public function transformStdClass($object)
* @param RestfulModel $model
* @return array
*/
public function transformRestfulModel(RestfulModel $model)
{
public function transformRestfulModel(RestfulModel $model) {
$this->model = $model;

// Begin the transformation!
Expand All @@ -73,11 +73,11 @@ public function transformRestfulModel(RestfulModel $model)
return ! in_array($key, $filterOutAttributes);
}, ARRAY_FILTER_USE_KEY);

/*
/**
* Format all dates as Iso8601 strings, this includes the created_at and updated_at columns
*/
foreach ($model->getDates() as $dateColumn) {
if (! empty($model->$dateColumn) && ! in_array($dateColumn, $filterOutAttributes)) {
if (!empty($model->$dateColumn) && !in_array($dateColumn, $filterOutAttributes)) {
$transformed[$dateColumn] = $model->$dateColumn->toIso8601String();
}
}
Expand All @@ -91,10 +91,10 @@ public function transformRestfulModel(RestfulModel $model)
);
unset($transformed[$model->getKeyName()]);

/**
* Transform all keys to CamelCase, recursively
/*
* Transform the model keys' case
*/
$transformed = $this->formatCase($transformed);
$transformed = $this->transformKeysCase($transformed);

/**
* Get the relations for this object and transform them
Expand All @@ -105,24 +105,57 @@ public function transformRestfulModel(RestfulModel $model)
}

/**
* Formats case of the input array or scalar to desired case
* Transform the keys of the object to the correct case as required
*
* @param array|mixed $input
* @param array $transformed
* @return array $transformed
*/
protected function formatCase($input)
protected function transformKeysCase(array $transformed)
{
// Get the config for how many levels of keys to transform
$levels = config('api.formatsOptions.transform_keys_levels', null);

/**
* Transform all keys to required case, to the specified number of levels (by default, infinite)
*/
$transformed = $this->formatKeyCase($transformed, $levels);

/*
* However, if the level is 1, we also want to transform the first level of keys in a json field which has been cast to array
*/
if (! $levels == 1) {
foreach ($this->model->getCasts() as $fieldName => $castType) {
if ($castType == 'array') {
$fieldNameFormatted = $this->formatKeyCase($fieldName);
if (array_key_exists($fieldNameFormatted, $transformed)) {
$transformed[$fieldNameFormatted] = $this->formatKeyCase($transformed[$fieldNameFormatted]);
}
}
}
}

return $transformed;
}

/**
* Formats case of the input array or scalar to desired case
*
* @param array|string $input
* @param int|null $levels How many levels of an array keys to transform - by default recurse infiniately (null)
* @return array $transformed
*/
protected function formatKeyCase($input, $levels = null) {
$caseFormat = APIBoilerplate::getResponseCaseType();

if ($caseFormat == APIBoilerplate::CAMEL_CASE) {
if (is_array($input)) {
$transformed = camel_case_array_keys($input);
$transformed = camel_case_array_keys($input, 1);
} else {
$transformed = camel_case($input);
}
} elseif ($caseFormat == APIBoilerplate::SNAKE_CASE) {
} else if ($caseFormat == APIBoilerplate::SNAKE_CASE) {
if (is_array($input)) {
$transformed = snake_case_array_keys($input);
$transformed = snake_case_array_keys($input, 1);
} else {
$transformed = snake_case($input);
}
Expand All @@ -139,8 +172,7 @@ protected function formatCase($input)
*
* @return array Array of attributes to filter out
*/
protected function getFilteredOutAttributes()
{
protected function getFilteredOutAttributes() {
$filterOutAttributes = array_merge(
$this->model->getHidden(),
[
Expand All @@ -158,26 +190,25 @@ protected function getFilteredOutAttributes()
* @param array $transformed
* @return array $transformed
*/
protected function transformRelations(array $transformed)
{
protected function transformRelations(array $transformed) {
// Iterate through all relations
foreach ($this->model->getRelations() as $relationKey => $relation) {
$transformedRelationKey = $this->formatKeyCase($relationKey);

// Skip Pivot
if ($relation instanceof \Illuminate\Database\Eloquent\Relations\Pivot) {
continue;
}

// Transform Collection
elseif ($relation instanceof \Illuminate\Database\Eloquent\Collection) {
else if ($relation instanceof \Illuminate\Database\Eloquent\Collection) {
if (count($relation->getIterator()) > 0) {

$relationModel = $relation->first();
$relationTransformer = $relationModel::getTransformer();

// Transform related model collection
if ($this->model->$relationKey) {
$transformedRelationKey = $this->formatCase($relationKey);

// Create empty array for relation
$transformed[$transformedRelationKey] = [];

Expand All @@ -198,12 +229,12 @@ protected function transformRelations(array $transformed)
}

// Transformed related model
elseif ($relation instanceof RestfulModel) {
else if ($relation instanceof RestfulModel) {
// Get transformer of relation model
$relationTransformer = $relation::getTransformer();

if ($this->model->$relationKey) {
$transformed[$this->formatCase($relationKey)] = $relationTransformer->transform($this->model->$relationKey);
$transformed[$transformedRelationKey] = $relationTransformer->transform($this->model->$relationKey);
}
}
}
Expand Down

0 comments on commit 6843bbb

Please sign in to comment.