Skip to content

Commit 9c29794

Browse files
authored
[7.x] Conditionally returning appended attributes in API resources (#33422)
* Add hasAppended method to HasAttributes trait * Add whenAppended() method for resources * Fix docblock * Test hasAppended() method * Test whenAppended() method * Fix docblock * Improve test * Test the unhappy path
1 parent f1a21b3 commit 9c29794

File tree

6 files changed

+121
-0
lines changed

6 files changed

+121
-0
lines changed

src/Illuminate/Database/Eloquent/Concerns/HasAttributes.php

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1484,6 +1484,17 @@ public function setAppends(array $appends)
14841484
return $this;
14851485
}
14861486

1487+
/**
1488+
* Return whether the accessor attribute has been appended.
1489+
*
1490+
* @param string $attribute
1491+
* @return bool
1492+
*/
1493+
public function hasAppended($attribute)
1494+
{
1495+
return in_array($attribute, $this->appends);
1496+
}
1497+
14871498
/**
14881499
* Get the mutated attributes for a given instance.
14891500
*

src/Illuminate/Http/Resources/ConditionallyLoadsAttributes.php

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,23 @@ protected function whenPivotLoadedAs($accessor, $table, $value, $default = null)
209209
);
210210
}
211211

212+
/**
213+
* Retrieve an accessor when it has been appended.
214+
*
215+
* @param string $attribute
216+
* @param mixed $value
217+
* @param mixed $default
218+
* @return \Illuminate\Http\Resources\MissingValue|mixed
219+
*/
220+
protected function whenAppended($attribute, $value = null, $default = null)
221+
{
222+
if ($this->resource->hasAppended($attribute)) {
223+
return func_num_args() >= 2 ? value($value) : $this->resource->$attribute;
224+
}
225+
226+
return func_num_args() === 3 ? value($default) : new MissingValue;
227+
}
228+
212229
/**
213230
* Transform the given value if it is present.
214231
*

tests/Database/DatabaseEloquentModelTest.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1593,6 +1593,11 @@ public function testAppendingOfAttributes()
15931593
$this->assertSame('camelCased', $model->camelCased);
15941594
$this->assertSame('StudlyCased', $model->StudlyCased);
15951595

1596+
$this->assertTrue($model->hasAppended('is_admin'));
1597+
$this->assertTrue($model->hasAppended('camelCased'));
1598+
$this->assertTrue($model->hasAppended('StudlyCased'));
1599+
$this->assertFalse($model->hasAppended('not_appended'));
1600+
15961601
$model->setHidden(['is_admin', 'camelCased', 'StudlyCased']);
15971602
$this->assertEquals([], $model->toArray());
15981603

tests/Integration/Http/Fixtures/Post.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,14 @@ class Post extends Model
1212
* @var array
1313
*/
1414
protected $guarded = [];
15+
16+
/**
17+
* Return whether the post is published.
18+
*
19+
* @return bool
20+
*/
21+
public function getIsPublishedAttribute()
22+
{
23+
return true;
24+
}
1525
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<?php
2+
3+
namespace Illuminate\Tests\Integration\Http\Fixtures;
4+
5+
use Illuminate\Http\Resources\Json\JsonResource;
6+
7+
class PostResourceWithOptionalAppendedAttributes extends JsonResource
8+
{
9+
public function toArray($request)
10+
{
11+
return [
12+
'id' => $this->id,
13+
'first' => $this->whenAppended('is_published'),
14+
'second' => $this->whenAppended('is_published', 'override value'),
15+
'third' => $this->whenAppended('is_published', function () {
16+
return 'override value';
17+
}),
18+
'fourth' => $this->whenAppended('is_published', $this->is_published, 'default'),
19+
'fifth' => $this->whenAppended('is_published', $this->is_published, function () {
20+
return 'default';
21+
}),
22+
];
23+
}
24+
}

tests/Integration/Http/ResourceTest.php

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
use Illuminate\Tests\Integration\Http\Fixtures\PostCollectionResource;
1818
use Illuminate\Tests\Integration\Http\Fixtures\PostResource;
1919
use Illuminate\Tests\Integration\Http\Fixtures\PostResourceWithExtraData;
20+
use Illuminate\Tests\Integration\Http\Fixtures\PostResourceWithOptionalAppendedAttributes;
2021
use Illuminate\Tests\Integration\Http\Fixtures\PostResourceWithOptionalData;
2122
use Illuminate\Tests\Integration\Http\Fixtures\PostResourceWithOptionalMerging;
2223
use Illuminate\Tests\Integration\Http\Fixtures\PostResourceWithOptionalPivotRelationship;
@@ -153,6 +154,59 @@ public function testResourcesMayHaveOptionalValues()
153154
]);
154155
}
155156

157+
public function testResourcesMayHaveOptionalAppendedAttributes()
158+
{
159+
Route::get('/', function () {
160+
$post = new Post([
161+
'id' => 5,
162+
]);
163+
164+
$post->append('is_published');
165+
166+
return new PostResourceWithOptionalAppendedAttributes($post);
167+
});
168+
169+
$response = $this->withoutExceptionHandling()->get(
170+
'/', ['Accept' => 'application/json']
171+
);
172+
173+
$response->assertStatus(200);
174+
175+
$response->assertJson([
176+
'data' => [
177+
'id' => 5,
178+
'first' => true,
179+
'second' => 'override value',
180+
'third' => 'override value',
181+
'fourth' => true,
182+
'fifth' => true,
183+
],
184+
]);
185+
}
186+
187+
public function testResourcesWithOptionalAppendedAttributesReturnDefaultValuesAndNotMissingValues()
188+
{
189+
Route::get('/', function () {
190+
return new PostResourceWithOptionalAppendedAttributes(new Post([
191+
'id' => 5,
192+
]));
193+
});
194+
195+
$response = $this->withoutExceptionHandling()->get(
196+
'/', ['Accept' => 'application/json']
197+
);
198+
199+
$response->assertStatus(200);
200+
201+
$response->assertExactJson([
202+
'data' => [
203+
'id' => 5,
204+
'fourth' => 'default',
205+
'fifth' => 'default',
206+
],
207+
]);
208+
}
209+
156210
public function testResourcesMayHaveOptionalMerges()
157211
{
158212
Route::get('/', function () {

0 commit comments

Comments
 (0)