Skip to content

Commit 202dabc

Browse files
Generate database seeders with new syntax (#201)
1 parent f12c3f5 commit 202dabc

File tree

10 files changed

+387
-1
lines changed

10 files changed

+387
-1
lines changed

src/Generators/SeederGenerator.php

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
<?php
2+
3+
4+
namespace Blueprint\Generators;
5+
6+
use Blueprint\Contracts\Generator;
7+
use Blueprint\Models\Model;
8+
use Illuminate\Support\Str;
9+
10+
class SeederGenerator implements Generator
11+
{
12+
/** @var \Illuminate\Contracts\Filesystem\Filesystem */
13+
private $files;
14+
15+
private $models = [];
16+
17+
public function __construct($files)
18+
{
19+
$this->files = $files;
20+
}
21+
22+
public function output(array $tree): array
23+
{
24+
if (empty($tree['seeders'])) {
25+
return [];
26+
}
27+
28+
$output = [];
29+
$stub = $this->files->stub('seeder.stub');
30+
31+
$this->registerModels($tree);
32+
33+
foreach ($tree['seeders'] as $model) {
34+
$path = $this->getPath($model);
35+
$this->files->put($path, $this->populateStub($stub, $model));
36+
37+
$output['created'][] = $path;
38+
}
39+
40+
return $output;
41+
}
42+
43+
private function getPath($model)
44+
{
45+
return 'database/seeds/' . $model . 'Seeder.php';
46+
}
47+
48+
protected function populateStub(string $stub, string $model)
49+
{
50+
$stub = str_replace('DummyClass', $this->getClassName($model), $stub);
51+
$stub = str_replace('//', $this->build($model), $stub);
52+
53+
return $stub;
54+
}
55+
56+
private function getClassName(string $model)
57+
{
58+
return $model . 'Seeder';
59+
}
60+
61+
private function build(string $model)
62+
{
63+
return sprintf('factory(\\%s::class, 5)->create();', $this->fqcnForContext($model));
64+
}
65+
66+
private function registerModels(array $tree)
67+
{
68+
$this->models = array_merge($tree['cache'] ?? [], $tree['models'] ?? []);
69+
}
70+
71+
private function fqcnForContext(string $context)
72+
{
73+
if (isset($this->models[$context])) {
74+
return $this->models[$context]->fullyQualifiedClassName();
75+
}
76+
77+
$matches = array_filter(array_keys($this->models), function ($key) use ($context) {
78+
return Str::endsWith($key, '\\' . Str::studly($context));
79+
});
80+
81+
if (count($matches) === 1) {
82+
return $this->models[current($matches)]->fullyQualifiedClassName();
83+
}
84+
85+
$fqn = config('blueprint.namespace');
86+
if (config('blueprint.models_namespace')) {
87+
$fqn .= '\\' . config('blueprint.models_namespace');
88+
}
89+
90+
return $fqn . '\\' . $context;
91+
}
92+
}

src/Lexers/SeederLexer.php

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
<?php
2+
3+
4+
namespace Blueprint\Lexers;
5+
6+
use Blueprint\Contracts\Lexer;
7+
use Blueprint\Models\Statements\DispatchStatement;
8+
use Blueprint\Models\Statements\EloquentStatement;
9+
use Blueprint\Models\Statements\FireStatement;
10+
use Blueprint\Models\Statements\QueryStatement;
11+
use Blueprint\Models\Statements\RedirectStatement;
12+
use Blueprint\Models\Statements\RenderStatement;
13+
use Blueprint\Models\Statements\ResourceStatement;
14+
use Blueprint\Models\Statements\RespondStatement;
15+
use Blueprint\Models\Statements\SendStatement;
16+
use Blueprint\Models\Statements\SessionStatement;
17+
use Blueprint\Models\Statements\ValidateStatement;
18+
use Illuminate\Support\Str;
19+
20+
class SeederLexer implements Lexer
21+
{
22+
public function analyze(array $tokens): array
23+
{
24+
$registry = [
25+
'seeders' => [],
26+
];
27+
28+
if (!empty($tokens['seeders'])) {
29+
$registry['seeders'] = $this->analyzeValue($tokens['seeders']);
30+
}
31+
32+
return $registry;
33+
}
34+
35+
private function analyzeValue($value)
36+
{
37+
return preg_split('/,([ \t]+)?/', $value);
38+
}
39+
}

stubs/seeder.stub

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?php
2+
3+
use Illuminate\Database\Seeder;
4+
5+
class DummyClass extends Seeder
6+
{
7+
/**
8+
* Run the database seeds.
9+
*
10+
* @return void
11+
*/
12+
public function run()
13+
{
14+
//
15+
}
16+
}

tests/Feature/BlueprintTest.php

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,28 @@ public function it_parses_models()
4545
], $this->subject->parse($blueprint));
4646
}
4747

48+
public function it_parses_seeders()
49+
{
50+
$blueprint = $this->fixture('definitions/seeders.bp');
51+
52+
$this->assertEquals([
53+
'models' => [
54+
'Post' => [
55+
'title' => 'string:400',
56+
'content' => 'longtext',
57+
'published_at' => 'nullable timestamp',
58+
],
59+
'Comment' => [
60+
'post_id' => 'id',
61+
'content' => 'longtext',
62+
'approved' => 'boolean',
63+
'user_id' => 'id',
64+
],
65+
],
66+
'seeders' => 'Post, Comment'
67+
], $this->subject->parse($blueprint));
68+
}
69+
4870
/**
4971
* @test
5072
*/
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
<?php
2+
3+
namespace Tests\Feature\Generators;
4+
5+
use Blueprint\Blueprint;
6+
use Blueprint\Generators\SeederGenerator;
7+
use Carbon\Carbon;
8+
use Illuminate\Support\Facades\App;
9+
use Tests\TestCase;
10+
11+
/**
12+
* @see SeederGenerator
13+
*/
14+
class SeederGeneratorTest extends TestCase
15+
{
16+
/**
17+
* @var Blueprint
18+
*/
19+
private $blueprint;
20+
21+
/** @var SeederGenerator */
22+
private $subject;
23+
24+
25+
private $files;
26+
27+
protected function setUp(): void
28+
{
29+
parent::setUp();
30+
31+
$this->files = \Mockery::mock();
32+
33+
$this->subject = new SeederGenerator($this->files);
34+
35+
$this->blueprint = new Blueprint();
36+
$this->blueprint->registerLexer(new \Blueprint\Lexers\ModelLexer());
37+
$this->blueprint->registerLexer(new \Blueprint\Lexers\SeederLexer());
38+
$this->blueprint->registerGenerator($this->subject);
39+
}
40+
41+
/**
42+
* @test
43+
*/
44+
public function output_generates_nothing_for_empty_tree()
45+
{
46+
$this->files->shouldNotHaveReceived('put');
47+
48+
$this->assertEquals([], $this->subject->output(['seeders' => []]));
49+
}
50+
51+
/**
52+
* @test
53+
*/
54+
public function output_generates_seeders()
55+
{
56+
$this->files->expects('stub')
57+
->with('seeder.stub')
58+
->andReturn(file_get_contents('stubs/seeder.stub'));
59+
60+
$this->files->expects('put')
61+
->with('database/seeds/PostSeeder.php', $this->fixture('seeders/PostSeeder.php'));
62+
$this->files->expects('put')
63+
->with('database/seeds/CommentSeeder.php', $this->fixture('seeders/CommentSeeder.php'));
64+
65+
$tokens = $this->blueprint->parse($this->fixture('definitions/seeders.bp'));
66+
$tree = $this->blueprint->analyze($tokens);
67+
68+
$this->assertEquals(['created' => ['database/seeds/PostSeeder.php', 'database/seeds/CommentSeeder.php']], $this->subject->output($tree));
69+
}
70+
71+
/**
72+
* @test
73+
*/
74+
public function output_generates_seeders_from_traced_models()
75+
{
76+
$this->files->expects('stub')
77+
->with('seeder.stub')
78+
->andReturn(file_get_contents('stubs/seeder.stub'));
79+
80+
$this->files->expects('put')
81+
->with('database/seeds/PostSeeder.php', $this->fixture('seeders/PostSeeder.php'));
82+
$this->files->expects('put')
83+
->with('database/seeds/CommentSeeder.php', $this->fixture('seeders/CommentSeeder.php'));
84+
85+
$tokens = $this->blueprint->parse($this->fixture('definitions/seeders.bp'));
86+
$tree = $this->blueprint->analyze($tokens);
87+
$tree['cache'] = $tree['models'];
88+
unset($tree['models']);
89+
90+
$this->assertEquals(['created' => ['database/seeds/PostSeeder.php', 'database/seeds/CommentSeeder.php']], $this->subject->output($tree));
91+
}
92+
}
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
<?php
2+
3+
namespace Tests\Feature\Lexers;
4+
5+
use Blueprint\Lexers\SeederLexer;
6+
use Blueprint\Models\Statements\DispatchStatement;
7+
use Blueprint\Models\Statements\EloquentStatement;
8+
use Blueprint\Models\Statements\FireStatement;
9+
use Blueprint\Models\Statements\QueryStatement;
10+
use Blueprint\Models\Statements\RedirectStatement;
11+
use Blueprint\Models\Statements\RenderStatement;
12+
use Blueprint\Models\Statements\ResourceStatement;
13+
use Blueprint\Models\Statements\RespondStatement;
14+
use Blueprint\Models\Statements\SendStatement;
15+
use Blueprint\Models\Statements\SessionStatement;
16+
use Blueprint\Models\Statements\ValidateStatement;
17+
use PHPUnit\Framework\TestCase;
18+
19+
/**
20+
* @see SeederLexer
21+
*/
22+
class SeederLexerTest extends TestCase
23+
{
24+
/**
25+
* @var SeederLexer
26+
*/
27+
private $subject;
28+
29+
protected function setUp(): void
30+
{
31+
parent::setUp();
32+
33+
$this->subject = new SeederLexer();
34+
}
35+
36+
/**
37+
* @test
38+
*/
39+
public function it_returns_nothing_without_seeders_token()
40+
{
41+
$this->assertEquals([
42+
'seeders' => []
43+
], $this->subject->analyze([]));
44+
}
45+
46+
/**
47+
* @test
48+
*/
49+
public function it_returns_seeders()
50+
{
51+
$tokens = [
52+
'seeders' => 'Post'
53+
];
54+
55+
$actual = $this->subject->analyze($tokens);
56+
57+
$this->assertIsArray($actual['seeders']);
58+
$this->assertCount(1, $actual['seeders']);
59+
60+
$this->assertSame(['Post'], $actual['seeders']);
61+
}
62+
63+
/**
64+
* @test
65+
*/
66+
public function it_returns_multiple_seeders()
67+
{
68+
$tokens = [
69+
'seeders' => 'Post, Comment'
70+
];
71+
72+
$actual = $this->subject->analyze($tokens);
73+
74+
$this->assertIsArray($actual['seeders']);
75+
$this->assertCount(2, $actual['seeders']);
76+
77+
$this->assertSame(['Post', 'Comment'], $actual['seeders']);
78+
}
79+
}

tests/fixtures/definitions/models-only.bp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,4 @@ models:
44

55
ModelTwo:
66
column: datatype
7-
another_column: datatype modifier
7+
another_column: datatype modifier
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
models:
2+
Post:
3+
title: string:400
4+
content: longtext
5+
published_at: nullable timestamp
6+
7+
Blog\Comment:
8+
post_id: id
9+
content: longtext
10+
approved: boolean
11+
user_id: id
12+
13+
seeders: Post, Comment
14+
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?php
2+
3+
use Illuminate\Database\Seeder;
4+
5+
class CommentSeeder extends Seeder
6+
{
7+
/**
8+
* Run the database seeds.
9+
*
10+
* @return void
11+
*/
12+
public function run()
13+
{
14+
factory(\App\Blog\Comment::class, 5)->create();
15+
}
16+
}

0 commit comments

Comments
 (0)