Skip to content

Commit 67e0b5b

Browse files
committed
Add morphing and morphed callbacks so users can have a finer control of how responses are morphed.
Signed-off-by: Jason Lewis <[email protected]>
1 parent 67c2ac7 commit 67e0b5b

File tree

2 files changed

+130
-11
lines changed

2 files changed

+130
-11
lines changed

src/Http/Response.php

Lines changed: 88 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
namespace Dingo\Api\Http;
44

5+
use Closure;
56
use ArrayObject;
67
use UnexpectedValueException;
78
use Dingo\Api\Transformer\Binding;
@@ -36,6 +37,20 @@ class Response extends IlluminateResponse
3637
*/
3738
protected static $transformer;
3839

40+
/**
41+
* Array of user-defined morphing callbacks.
42+
*
43+
* @var array
44+
*/
45+
protected static $morphingCallbacks = [];
46+
47+
/**
48+
* Array of user-defined morphed callbacks.
49+
*
50+
* @var array
51+
*/
52+
protected static $morphedCallbacks = [];
53+
3954
/**
4055
* Create a new response instance.
4156
*
@@ -78,10 +93,12 @@ public static function makeFromExisting(IlluminateResponse $old)
7893
*/
7994
public function morph($format = 'json')
8095
{
81-
$content = $this->getOriginalContent();
96+
$this->content = $this->getOriginalContent();
8297

83-
if (isset(static::$transformer) && static::$transformer->transformableResponse($content)) {
84-
$content = static::$transformer->transform($content);
98+
$this->fireMorphingCallbacks();
99+
100+
if (isset(static::$transformer) && static::$transformer->transformableResponse($this->content)) {
101+
$this->content = static::$transformer->transform($this->content);
85102
}
86103

87104
$formatter = static::getFormatter($format);
@@ -90,21 +107,57 @@ public function morph($format = 'json')
90107

91108
$this->headers->set('content-type', $formatter->getContentType());
92109

93-
if ($content instanceof EloquentModel) {
94-
$content = $formatter->formatEloquentModel($content);
95-
} elseif ($content instanceof EloquentCollection) {
96-
$content = $formatter->formatEloquentCollection($content);
97-
} elseif (is_array($content) || $content instanceof ArrayObject || $content instanceof Arrayable) {
98-
$content = $formatter->formatArray($content);
110+
$this->fireMorphedCallbacks();
111+
112+
if ($this->content instanceof EloquentModel) {
113+
$this->content = $formatter->formatEloquentModel($this->content);
114+
} elseif ($this->content instanceof EloquentCollection) {
115+
$this->content = $formatter->formatEloquentCollection($this->content);
116+
} elseif (is_array($this->content) || $this->content instanceof ArrayObject || $this->content instanceof Arrayable) {
117+
$this->content = $formatter->formatArray($this->content);
99118
} else {
100119
$this->headers->set('content-type', $defaultContentType);
101120
}
102121

103-
$this->content = $content;
104-
105122
return $this;
106123
}
107124

125+
/**
126+
* Fire the morphed callbacks.
127+
*
128+
* @return void
129+
*/
130+
protected function fireMorphedCallbacks()
131+
{
132+
$this->fireCallbacks(static::$morphedCallbacks);
133+
}
134+
135+
/**
136+
* Fire the morphing callbacks.
137+
*
138+
* @return void
139+
*/
140+
protected function fireMorphingCallbacks()
141+
{
142+
$this->fireCallbacks(static::$morphingCallbacks);
143+
}
144+
145+
/**
146+
* Fire the callbacks with the content and response instance as parameters.
147+
*
148+
* @param array $callbacks
149+
*
150+
* @return void
151+
*/
152+
protected function fireCallbacks(array $callbacks)
153+
{
154+
foreach ($callbacks as $callback) {
155+
if ($response = call_user_func($callback, $this->content, $this)) {
156+
$this->content = $response;
157+
}
158+
}
159+
}
160+
108161
/**
109162
* {@inheritdoc}
110163
*/
@@ -123,6 +176,30 @@ public function setContent($content)
123176
}
124177
}
125178

179+
/**
180+
* Add a callback for when the response is about to be morphed.
181+
*
182+
* @param \Closure $callback
183+
*
184+
* @return void
185+
*/
186+
public static function morphing(Closure $callback)
187+
{
188+
static::$morphingCallbacks[] = $callback;
189+
}
190+
191+
/**
192+
* Add a callback for when the response has been morphed.
193+
*
194+
* @param \Closure $callback
195+
*
196+
* @return void
197+
*/
198+
public static function morphed(Closure $callback)
199+
{
200+
static::$morphedCallbacks[] = $callback;
201+
}
202+
126203
/**
127204
* Get the formatter based on the requested format type.
128205
*

tests/Http/ResponseTest.php

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,29 @@
44

55
use StdClass;
66
use Mockery as m;
7+
use ReflectionClass;
78
use Dingo\Api\Http\Response;
89
use PHPUnit_Framework_TestCase;
910
use Dingo\Api\Transformer\Binding;
11+
use Dingo\Api\Http\Response\Format\Json;
1012

1113
class ResponseTest extends PHPUnit_Framework_TestCase
1214
{
1315
public function tearDown()
1416
{
1517
m::close();
18+
19+
$reflection = new ReflectionClass('Dingo\Api\Http\Response');
20+
21+
$property = $reflection->getProperty('morphedCallbacks');
22+
23+
$property->setAccessible(true);
24+
$property->setValue([]);
25+
26+
$property = $reflection->getProperty('morphingCallbacks');
27+
28+
$property->setAccessible(true);
29+
$property->setValue([]);
1630
}
1731

1832
/**
@@ -55,4 +69,32 @@ public function testBuildingWithCustomStatusCodeAndHeaders()
5569
$this->assertEquals('Bar', $response->headers->get('Foo'));
5670
$this->assertEquals(302, $response->getStatusCode());
5771
}
72+
73+
public function testChangingContentWithCallbacks()
74+
{
75+
Response::morphed(function ($content, Response $response) {
76+
$content['foo'] = 'bam!';
77+
78+
return $content;
79+
});
80+
81+
Response::addFormatter('json', new Json);
82+
83+
$response = new Response(['foo' => 'bar']);
84+
85+
$this->assertEquals('{"foo":"bam!"}', $response->morph('json')->getContent());
86+
}
87+
88+
public function testChangingResponseHeadersWithCallbacks()
89+
{
90+
Response::morphing(function ($content, Response $response) {
91+
$response->headers->set('x-foo', 'bar');
92+
});
93+
94+
Response::addFormatter('json', new Json);
95+
96+
$response = new Response(['foo' => 'bar']);
97+
98+
$this->assertEquals('bar', $response->morph('json')->headers->get('x-foo'));
99+
}
58100
}

0 commit comments

Comments
 (0)