Skip to content

Commit f31951f

Browse files
committed
Implemented configurable response formats. See dingo#3.
Signed-off-by: Jason Lewis <[email protected]>
1 parent 5687f23 commit f31951f

File tree

12 files changed

+432
-99
lines changed

12 files changed

+432
-99
lines changed

composer.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@
2626
"autoload": {
2727
"psr-4": {
2828
"Dingo\\Api\\": "src"
29-
}
29+
},
30+
"files": ["tests/stubs.php"]
3031
},
3132
"minimum-stability": "stable"
3233
}

src/ApiServiceProvider.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,12 @@ public function boot()
3131
return $app['dingo.api.authentication'];
3232
};
3333

34+
// Set the static formatters on the response class so that requested formats
35+
// can be formatted and returned correctly.
36+
$formats = array_map('value', $this->app['config']['api::formats']);
37+
38+
Response::setFormatters($formats);
39+
3440
$this->app['router']->filter('api', function($route, $request)
3541
{
3642
try

src/Http/Response.php

Lines changed: 56 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
<?php namespace Dingo\Api\Http;
22

3+
use RuntimeException;
34
use Illuminate\Support\MessageBag;
45
use Illuminate\Support\Contracts\JsonableInterface;
56
use Illuminate\Support\Contracts\ArrayableInterface;
@@ -8,6 +9,13 @@
89

910
class Response extends \Illuminate\Http\Response {
1011

12+
/**
13+
* Array of registered formatters.
14+
*
15+
* @var array
16+
*/
17+
protected static $formatters = [];
18+
1119
/**
1220
* Make an API response from an existing Illuminate response.
1321
*
@@ -24,95 +32,83 @@ public static function makeFromExisting(\Illuminate\Http\Response $response)
2432
*
2533
* @return \Dingo\Api\Http\Response
2634
*/
27-
public function morph()
35+
public function morph($format = 'json')
2836
{
29-
$this->headers->set('content-type', 'application/json');
37+
$formatter = static::getFormatter($format);
3038

31-
return $this->morphJsonResponse();
32-
}
39+
$response = $this->original;
3340

34-
/**
35-
* Morph the response to it's JSON representation by checking the type
36-
* of response that's going to be returned.
37-
*
38-
* @return \Dingo\Api\Http\Response
39-
*/
40-
protected function morphJsonResponse()
41-
{
42-
if ($this->original instanceof JsonableInterface)
41+
// First we'll attempt to format the response if it's either an Eloquent
42+
// model or an Eloquent collection.
43+
if ($response instanceof EloquentModel)
4344
{
44-
$this->content = $this->morphJsonableInterface($this->original);
45+
$response = $formatter->formatEloquentModel($response);
4546
}
46-
elseif (is_string($this->original))
47+
elseif ($response instanceof EloquentCollection)
4748
{
48-
$this->content = $this->encode(['message' => $this->original]);
49+
$response = $formatter->formatEloquentCollection($response);
4950
}
5051
else
5152
{
52-
$this->content = $this->morphArrayableInterface($this->original);
53-
54-
if (is_array($this->content))
53+
// Next we'll attempt to format the response if it's a string,
54+
// an array or an object implementing ArrayableInterface,
55+
// an object implementing JsonableInterface or an
56+
// unknown type.
57+
if (is_string($response))
5558
{
56-
array_walk_recursive($this->content, function(&$value)
57-
{
58-
$value = $this->morphArrayableInterface($value);
59-
});
59+
$response = $formatter->formatString($response);
60+
}
61+
elseif (is_array($response) or $response instanceof ArrayableInterface)
62+
{
63+
$response = $formatter->formatArrayableInterface($response);
64+
}
65+
elseif ($response instanceof JsonableInterface)
66+
{
67+
$response = $formatter->formatJsonableInterface($response);
68+
}
69+
else
70+
{
71+
$response = $formatter->formatUnknown($response);
6072
}
61-
62-
$this->content = $this->encode($this->content);
6373
}
6474

65-
return $this;
66-
}
75+
// Set the "Content-Type" header of the response to that which
76+
// is defined by the formatter being used.
77+
$this->headers->set('content-type', $formatter->getContentType());
6778

68-
/**
69-
* If content implements the ArrayableInterface it will be morphed to its
70-
* array value.
71-
*
72-
* @param array|\Illuminate\Support\Contracts\ArrayableInterface $content
73-
* @return array
74-
*/
75-
protected function morphArrayableInterface($content)
76-
{
77-
return $content instanceof ArrayableInterface ? $content->toArray() : $content;
79+
// Directly set the property because using setContent results in
80+
// the original content also being updated.
81+
$this->content = $response;
82+
83+
return $this;
7884
}
7985

8086
/**
81-
* If content implements the JsonableInterface it will be morphed to its
82-
* JSON value.
87+
* Get the formatter based on the requested format type.
8388
*
84-
* @param \Illuminate\Support\Contracts\JsonableInterface $content
85-
* @return string
89+
* @param string $format
90+
* @return \Dingo\Api\Http\Format\FormatInterface
91+
* @throws \RuntimeException
8692
*/
87-
protected function morphJsonableInterface($content)
93+
public static function getFormatter($format)
8894
{
89-
if ($content instanceof EloquentModel)
95+
if ( ! isset(static::$formatters[$format]))
9096
{
91-
$key = $content->getTable();
92-
93-
return $this->encode([$key => $content->toArray()]);
97+
throw new RuntimeException('Response formatter "'.$format.'" has not been registered.');
9498
}
95-
elseif ($content instanceof EloquentCollection)
96-
{
97-
$key = str_plural($content->first()->getTable());
9899

99-
return $this->encode([$key => $content->toArray()]);
100-
}
101-
else
102-
{
103-
return $content->toJson();
104-
}
100+
return static::$formatters[$format];
105101
}
106102

107103
/**
108-
* Encode the content to its JSON representation.
104+
* Set the response formatters.
109105
*
110-
* @param string $content
111-
* @return string
106+
* @param array $formatters
107+
* @return void
112108
*/
113-
protected function encode($content)
109+
public static function setFormatters(array $formatters)
114110
{
115-
return json_encode($content);
111+
static::$formatters = $formatters;
116112
}
117113

118114
}
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
<?php namespace Dingo\Api\Http\ResponseFormat;
2+
3+
use Illuminate\Support\Contracts\ArrayableInterface;
4+
5+
class JsonResponseFormat implements ResponseFormatInterface {
6+
7+
/**
8+
* Format an Eloquent model.
9+
*
10+
* @param \Illuminate\Database\Eloquent\Model $model
11+
* @return string
12+
*/
13+
public function formatEloquentModel($model)
14+
{
15+
$key = str_singular($model->getTable());
16+
17+
return $this->encode([$key => $model->toArray()]);
18+
}
19+
20+
/**
21+
* Format an Eloquent collection.
22+
*
23+
* @param \Illuminate\Database\Eloquent\Collection $collection
24+
* @return string
25+
*/
26+
public function formatEloquentCollection($collection)
27+
{
28+
$key = str_plural($collection->first()->getTable());
29+
30+
return $this->encode([$key => $collection->toArray()]);
31+
}
32+
33+
/**
34+
* Format a string.
35+
*
36+
* @param string $string
37+
* @return string
38+
*/
39+
public function formatString($string)
40+
{
41+
return $this->encode(['message' => $string]);
42+
}
43+
44+
/**
45+
* Format an array or instance implementing ArrayableInterface.
46+
*
47+
* @param \Illuminate\Support\Contracts\ArrayableInterface $response
48+
* @return string
49+
*/
50+
public function formatArrayableInterface($response)
51+
{
52+
$response = $this->morphToArray($response);
53+
54+
array_walk_recursive($response, function(&$value)
55+
{
56+
$value = $this->morphToArray($value);
57+
});
58+
59+
return $this->encode($response);
60+
}
61+
62+
/**
63+
* Format an instance implementing JsonableInterface.
64+
*
65+
* @param \Illuminate\Support\Contracts\JsonableInterface $response
66+
* @return string
67+
*/
68+
public function formatJsonableInterface($response)
69+
{
70+
return $response->toJson();
71+
}
72+
73+
/**
74+
* Format an unknown type.
75+
*
76+
* @param mixed $response
77+
* @return string
78+
*/
79+
public function formatUnknown($response)
80+
{
81+
return $this->encode($response);
82+
}
83+
84+
/**
85+
* Get the response content type.
86+
*
87+
* @return string
88+
*/
89+
public function getContentType()
90+
{
91+
return 'application/json';
92+
}
93+
94+
/**
95+
* Morph a value to an array.
96+
*
97+
* @param array|\Illuminate\Support\Contracts\ArrayableInterface
98+
* @return array
99+
*/
100+
protected function morphToArray($value)
101+
{
102+
return $value instanceof ArrayableInterface ? $value->toArray() : $value;
103+
}
104+
105+
/**
106+
* Encode the content to its JSON representation.
107+
*
108+
* @param string $content
109+
* @return string
110+
*/
111+
protected function encode($content)
112+
{
113+
return json_encode($content);
114+
}
115+
116+
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
<?php namespace Dingo\Api\Http\ResponseFormat;
2+
3+
interface ResponseFormatInterface {
4+
5+
/**
6+
* Format an Eloquent model.
7+
*
8+
* @param \Illuminate\Database\Eloquent\Model $model
9+
* @return string
10+
*/
11+
public function formatEloquentModel($model);
12+
13+
/**
14+
* Format an Eloquent collection.
15+
*
16+
* @param \Illuminate\Database\Eloquent\Collection $collection
17+
* @return string
18+
*/
19+
public function formatEloquentCollection($collection);
20+
21+
/**
22+
* Format a string.
23+
*
24+
* @param string $string
25+
* @return string
26+
*/
27+
public function formatString($string);
28+
29+
/**
30+
* Format an array or instance implementing ArrayableInterface.
31+
*
32+
* @param \Illuminate\Support\Contracts\ArrayableInterface $response
33+
* @return string
34+
*/
35+
public function formatArrayableInterface($response);
36+
37+
/**
38+
* Format an instance implementing JsonableInterface.
39+
*
40+
* @param \Illuminate\Support\Contracts\JsonableInterface $response
41+
* @return string
42+
*/
43+
public function formatJsonableInterface($response);
44+
45+
/**
46+
* Format an unknown type.
47+
*
48+
* @param mixed $response
49+
* @return string
50+
*/
51+
public function formatUnknown($response);
52+
53+
/**
54+
* Get the response content type.
55+
*
56+
* @return string
57+
*/
58+
public function getContentType();
59+
60+
}

0 commit comments

Comments
 (0)