Skip to content

Commit 6e9221b

Browse files
authored
Option to disable generation of separate resource collection class (#723)
1 parent f35f399 commit 6e9221b

File tree

9 files changed

+226
-8
lines changed

9 files changed

+226
-8
lines changed

config/blueprint.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,18 @@
130130
*/
131131
'property_promotion' => false,
132132

133+
/*
134+
|--------------------------------------------------------------------------
135+
| Generate Resource Collection Classes
136+
|--------------------------------------------------------------------------
137+
|
138+
| By default, Blueprint generates resource collection classes to manage resource
139+
| collections (e.g. `PostResourceCollection`). You may disable this option to
140+
| generate only resource classes and use their `collection` method instead.
141+
|
142+
*/
143+
'generate_resource_collection_classes' => true,
144+
133145
/*
134146
|--------------------------------------------------------------------------
135147
| Generators

src/Generators/ControllerGenerator.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,7 @@ protected function buildMethods(Controller $controller): string
195195
$statement instanceof InertiaStatement => 'Inertia\Response',
196196
$statement instanceof RenderStatement => 'Illuminate\View\View',
197197
$statement instanceof RedirectStatement => 'Illuminate\Http\RedirectResponse',
198-
$statement instanceof ResourceStatement => config('blueprint.namespace') . '\\Http\\Resources\\' . ($controller->namespace() ? $controller->namespace() . '\\' : '') . $statement->name(),
198+
$statement instanceof ResourceStatement => $statement->collection() && !$statement->generateCollectionClass() ? 'Illuminate\Http\Resources\Json\ResourceCollection' : config('blueprint.namespace') . '\\Http\\Resources\\' . ($controller->namespace() ? $controller->namespace() . '\\' : '') . $statement->name(),
199199
default => 'Illuminate\Http\Response'
200200
};
201201

src/Generators/Statements/ResourceGenerator.php

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -60,14 +60,14 @@ protected function populateStub(string $stub, Controller $controller, ResourceSt
6060
. ($controller->namespace() ? '\\' . $controller->namespace() : '');
6161

6262
$imports = ['use Illuminate\\Http\\Request;'];
63-
$imports[] = $resource->collection() ? 'use Illuminate\\Http\\Resources\\Json\\ResourceCollection;' : 'use Illuminate\\Http\\Resources\\Json\\JsonResource;';
63+
$imports[] = $resource->collection() && $resource->generateCollectionClass() ? 'use Illuminate\\Http\\Resources\\Json\\ResourceCollection;' : 'use Illuminate\\Http\\Resources\\Json\\JsonResource;';
6464

6565
$stub = str_replace('{{ namespace }}', $namespace, $stub);
6666
$stub = str_replace('{{ imports }}', implode(PHP_EOL, $imports), $stub);
67-
$stub = str_replace('{{ parentClass }}', $resource->collection() ? 'ResourceCollection' : 'JsonResource', $stub);
67+
$stub = str_replace('{{ parentClass }}', $resource->collection() && $resource->generateCollectionClass() ? 'ResourceCollection' : 'JsonResource', $stub);
6868
$stub = str_replace('{{ class }}', $resource->name(), $stub);
69-
$stub = str_replace('{{ parentClass }}', $resource->collection() ? 'ResourceCollection' : 'JsonResource', $stub);
70-
$stub = str_replace('{{ resource }}', $resource->collection() ? 'resource collection' : 'resource', $stub);
69+
$stub = str_replace('{{ parentClass }}', $resource->collection() && $resource->generateCollectionClass() ? 'ResourceCollection' : 'JsonResource', $stub);
70+
$stub = str_replace('{{ resource }}', $resource->collection() && $resource->generateCollectionClass() ? 'resource collection' : 'resource', $stub);
7171
$stub = str_replace('{{ body }}', $this->buildData($resource), $stub);
7272

7373
return $stub;
@@ -83,7 +83,7 @@ protected function buildData(ResourceStatement $resource): string
8383
$model = $this->tree->modelForContext($context, true);
8484

8585
$data = [];
86-
if ($resource->collection()) {
86+
if ($resource->collection() && $resource->generateCollectionClass()) {
8787
$data[] = 'return [';
8888
$data[] = self::INDENT . '\'data\' => $this->collection,';
8989
$data[] = ' ];';

src/Models/Statements/ResourceStatement.php

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ public function __construct(string $reference, bool $collection = false, bool $p
2121

2222
public function name(): string
2323
{
24-
if ($this->collection()) {
24+
if ($this->collection() && $this->generateCollectionClass()) {
2525
return Str::studly(Str::singular($this->reference)) . 'Collection';
2626
}
2727

@@ -43,8 +43,17 @@ public function paginate(): bool
4343
return $this->paginate;
4444
}
4545

46+
public function generateCollectionClass(): bool
47+
{
48+
return config('blueprint.generate_resource_collection_classes', true);
49+
}
50+
4651
public function output(array $properties = []): string
4752
{
53+
if ($this->collection() && !$this->generateCollectionClass()) {
54+
return sprintf('return %s::collection(%s);', $this->name(), $this->buildArgument($properties));
55+
}
56+
4857
return sprintf('return new %s(%s);', $this->name(), $this->buildArgument($properties));
4958
}
5059

tests/Feature/Generators/ControllerGeneratorTest.php

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,33 @@ public function output_generates_controller_with_authorize_resource(): void
232232
$this->assertEquals(['created' => [$path]], $this->subject->output($tree));
233233
}
234234

235+
#[Test]
236+
public function output_generates_controller_without_generating_resource_collection_classes(): void
237+
{
238+
config(['blueprint.generate_resource_collection_classes' => false]);
239+
240+
$definition = 'drafts/api-resource-pagination.yaml';
241+
$path = 'app/Http/Controllers/PostController.php';
242+
$controller = 'controllers/without-generating-resource-collection-classes.php';
243+
244+
$this->filesystem->expects('stub')
245+
->with('controller.class.stub')
246+
->andReturn($this->stub('controller.class.stub'));
247+
$this->filesystem->expects('stub')
248+
->with('controller.method.stub')
249+
->andReturn($this->stub('controller.method.stub'));
250+
251+
$this->filesystem->expects('exists')
252+
->with(dirname($path))
253+
->andReturnTrue();
254+
$this->filesystem->expects('put')
255+
->with($path, $this->fixture($controller));
256+
257+
$tokens = $this->blueprint->parse($this->fixture($definition));
258+
$tree = $this->blueprint->analyze($tokens);
259+
$this->assertEquals(['created' => [$path]], $this->subject->output($tree));
260+
}
261+
235262
public static function controllerTreeDataProvider(): array
236263
{
237264
return [

tests/Feature/Generators/Statements/ResourceGeneratorTest.php

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,38 @@ public function output_writes_resources_for_render_statements(): void
9595
$this->assertEquals(['created' => ['app/Http/Resources/UserCollection.php', 'app/Http/Resources/UserResource.php']], $this->subject->output($tree));
9696
}
9797

98+
#[Test]
99+
public function output_writes_resource_for_resource_statements_whitout_generating_resource_collection_classes(): void
100+
{
101+
config(['blueprint.generate_resource_collection_classes' => false]);
102+
103+
$template = $this->stub('resource.stub');
104+
$this->filesystem->expects('stub')
105+
->with('resource.stub')
106+
->andReturn($template);
107+
108+
$this->filesystem->shouldReceive('exists')
109+
->with('app/Http/Resources')
110+
->andReturns(false, true);
111+
$this->filesystem->expects('makeDirectory')
112+
->with('app/Http/Resources', 0755, true);
113+
114+
$this->filesystem->shouldReceive('exists')
115+
->with('app/Http/Resources/UserResource.php')
116+
->andReturns(false, true);
117+
$this->filesystem->expects('put')
118+
->with('app/Http/Resources/UserResource.php', $this->fixture('resources/user.php'));
119+
120+
$this->filesystem->shouldReceive('put')
121+
->with('app/Http/Resources/UserCollection.php')
122+
->never();
123+
124+
$tokens = $this->blueprint->parse($this->fixture('drafts/resource-statements.yaml'));
125+
$tree = $this->blueprint->analyze($tokens);
126+
127+
$this->assertEquals(['created' => ['app/Http/Resources/UserResource.php']], $this->subject->output($tree));
128+
}
129+
98130
#[Test]
99131
public function output_writes_namespaced_classes(): void
100132
{
@@ -164,6 +196,40 @@ public function output_writes_nested_resource(): void
164196
], $this->subject->output($tree));
165197
}
166198

199+
#[Test]
200+
public function output_writes_nested_resource_without_generating_resource_collection_classes(): void
201+
{
202+
config(['blueprint.generate_resource_collection_classes' => false]);
203+
204+
$this->filesystem->expects('stub')
205+
->with('resource.stub')
206+
->andReturn(file_get_contents('stubs/resource.stub'));
207+
208+
$this->filesystem->expects('exists')
209+
->with('app/Http/Resources/Api')
210+
->andReturns(false);
211+
$this->filesystem->expects('makeDirectory')
212+
->with('app/Http/Resources/Api', 0755, true);
213+
214+
$this->filesystem->expects('exists')
215+
->times(4)
216+
->with('app/Http/Resources/Api/CertificateResource.php')
217+
->andReturns(false, true, true, true);
218+
$this->filesystem->expects('put')
219+
->with('app/Http/Resources/Api/CertificateResource.php', $this->fixture('resources/certificate-with-nested-resource.php'));
220+
221+
$this->filesystem->shouldReceive('put')
222+
->with('app/Http/Resources/Api/CertificateCollection.php')
223+
->never();
224+
225+
$tokens = $this->blueprint->parse($this->fixture('drafts/resource-nested.yaml'));
226+
$tree = $this->blueprint->analyze($tokens);
227+
228+
$this->assertEquals([
229+
'created' => ['app/Http/Resources/Api/CertificateResource.php'],
230+
], $this->subject->output($tree));
231+
}
232+
167233
#[Test]
168234
public function output_api_resource_pagination(): void
169235
{
@@ -198,4 +264,40 @@ public function output_api_resource_pagination(): void
198264
'created' => ['app/Http/Resources/PostCollection.php', 'app/Http/Resources/PostResource.php'],
199265
], $this->subject->output($tree));
200266
}
267+
268+
#[Test]
269+
public function output_api_resource_pagination_without_generating_resource_collection_classes(): void
270+
{
271+
config(['blueprint.generate_resource_collection_classes' => false]);
272+
273+
$this->files->expects('stub')
274+
->with('resource.stub')
275+
->andReturn(file_get_contents('stubs/resource.stub'));
276+
277+
$this->files->expects('exists')
278+
->with('app/Http/Resources')
279+
->andReturns(false, true);
280+
281+
$this->files->expects('makeDirectory')
282+
->with('app/Http/Resources', 0755, true);
283+
284+
$this->files->expects('exists')
285+
->times(4)
286+
->with('app/Http/Resources/PostResource.php')
287+
->andReturns(false, true, true, true);
288+
289+
$this->files->expects('put')
290+
->with('app/Http/Resources/PostResource.php', $this->fixture('resources/api-post-resource.php'));
291+
292+
$this->files->expects('put')
293+
->with('app/Http/Resources/PostCollection.php')
294+
->never();
295+
296+
$tokens = $this->blueprint->parse($this->fixture('drafts/api-resource-pagination.yaml'));
297+
$tree = $this->blueprint->analyze($tokens);
298+
299+
$this->assertEquals([
300+
'created' => ['app/Http/Resources/PostResource.php'],
301+
], $this->subject->output($tree));
302+
}
201303
}

tests/Feature/Lexers/StatementLexerTest.php

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
use Blueprint\Models\Statements\ValidateStatement;
1818
use PHPUnit\Framework\Attributes\DataProvider;
1919
use PHPUnit\Framework\Attributes\Test;
20-
use PHPUnit\Framework\TestCase;
20+
use Tests\TestCase;
2121

2222
/**
2323
* @see StatementLexer
@@ -679,6 +679,26 @@ public function it_returns_a_resource_collection_statement_with_pagination(): vo
679679
$this->assertTrue($actual[0]->paginate());
680680
}
681681

682+
#[Test]
683+
public function it_returns_a_resource_collection_statement_without_generating_a_resource_collection_class(): void
684+
{
685+
config(['blueprint.generate_resource_collection_classes' => false]);
686+
687+
$tokens = [
688+
'resource' => 'collection:users',
689+
];
690+
691+
$actual = $this->subject->analyze($tokens);
692+
693+
$this->assertCount(1, $actual);
694+
$this->assertInstanceOf(ResourceStatement::class, $actual[0]);
695+
696+
$this->assertEquals('UserResource', $actual[0]->name());
697+
$this->assertEquals('users', $actual[0]->reference());
698+
$this->assertTrue($actual[0]->collection());
699+
$this->assertFalse($actual[0]->paginate());
700+
}
701+
682702
public static function sessionTokensProvider(): array
683703
{
684704
return [

tests/TestCase.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ protected function getEnvironmentSetUp($app)
3030
$app['config']->set('blueprint.generate_phpdocs', false);
3131
$app['config']->set('blueprint.use_constraints', false);
3232
$app['config']->set('blueprint.fake_nullables', true);
33+
$app['config']->set('blueprint.generate_resource_collection_classes', true);
3334
$app['config']->set('database.default', 'testing');
3435
}
3536

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
<?php
2+
3+
namespace App\Http\Controllers;
4+
5+
use App\Http\Requests\PostStoreRequest;
6+
use App\Http\Requests\PostUpdateRequest;
7+
use App\Http\Resources\PostResource;
8+
use App\Models\Post;
9+
use Illuminate\Http\Request;
10+
use Illuminate\Http\Resources\Json\ResourceCollection;
11+
use Illuminate\Http\Response;
12+
13+
class PostController extends Controller
14+
{
15+
public function index(Request $request): ResourceCollection
16+
{
17+
$posts = Post::paginate();
18+
19+
return PostResource::collection($posts);
20+
}
21+
22+
public function store(PostStoreRequest $request): PostResource
23+
{
24+
$post = Post::create($request->validated());
25+
26+
return new PostResource($post);
27+
}
28+
29+
public function show(Request $request, Post $post): PostResource
30+
{
31+
return new PostResource($post);
32+
}
33+
34+
public function update(PostUpdateRequest $request, Post $post): PostResource
35+
{
36+
$post->update($request->validated());
37+
38+
return new PostResource($post);
39+
}
40+
41+
public function destroy(Request $request, Post $post): Response
42+
{
43+
$post->delete();
44+
45+
return response()->noContent();
46+
}
47+
}

0 commit comments

Comments
 (0)