Skip to content

Commit 20f537d

Browse files
committed
WIP: generate statement code
1 parent ae66c32 commit 20f537d

21 files changed

+530
-25
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ models:
4141
controllers:
4242
Post:
4343
index:
44-
query: all posts
44+
query: all:posts
4545
render: post.index with:posts
4646

4747
store:

src/BlueprintCommand.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,8 @@ public function handle()
5959
$blueprint->registerGenerator(new \Blueprint\Generators\ModelGenerator($this->files));
6060
$blueprint->registerGenerator(new \Blueprint\Generators\FactoryGenerator($this->files));
6161

62+
// TODO: load additional generators
63+
6264
$tokens = $blueprint->parse($contents);
6365
$registry = $blueprint->analyze($tokens);
6466
$generated = $blueprint->generate($registry);

src/Generators/ControllerGenerator.php

Lines changed: 77 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,26 @@
44

55
use Blueprint\Contracts\Generator;
66
use Blueprint\Models\Controller;
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\SendStatement;
14+
use Blueprint\Models\Statements\SessionStatement;
15+
use Blueprint\Models\Statements\ValidateStatement;
716
use Illuminate\Support\Str;
817

918
class ControllerGenerator implements Generator
1019
{
20+
const INDENT = ' ';
21+
1122
/** @var \Illuminate\Contracts\Filesystem\Filesystem */
1223
private $files;
1324

25+
private $imports = [];
26+
1427
public function __construct($files)
1528
{
1629
$this->files = $files;
@@ -24,6 +37,7 @@ public function output(array $tree): array
2437

2538
/** @var \Blueprint\Models\Controller $controller */
2639
foreach ($tree['controllers'] as $controller) {
40+
$this->addImport($controller, 'Illuminate\\Http\\Request');
2741
$path = $this->getPath($controller);
2842
$this->files->put(
2943
$path,
@@ -41,7 +55,7 @@ protected function populateStub(string $stub, Controller $controller)
4155
$stub = str_replace('DummyNamespace', 'App\\Http\\Controllers', $stub);
4256
$stub = str_replace('DummyClass', $this->className($controller), $stub);
4357
$stub = str_replace('// methods...', $this->buildMethods($controller), $stub);
44-
$stub = $this->addImports($controller, $stub);
58+
$stub = str_replace('// imports...', $this->buildImports($controller), $stub);
4559

4660
return $stub;
4761
}
@@ -52,13 +66,54 @@ private function buildMethods(Controller $controller)
5266

5367
$methods = '';
5468

55-
foreach ($controller->methods() as $name => $body) {
56-
$methods .= PHP_EOL . str_replace('DummyMethod', $name, $template);
57-
// TODO:
58-
// foreach statements
59-
// output their resulting code
60-
// validate => replace Request injection (addImport)
61-
// queue => output Job::dispatch($data) (addImport)
69+
foreach ($controller->methods() as $name => $statements) {
70+
$method = str_replace('DummyMethod', $name, $template);
71+
72+
// TODO: if resourceful action, do implicit model binding
73+
74+
$body = '';
75+
foreach ($statements as $statement) {
76+
if ($statement instanceof SendStatement) {
77+
$body .= self::INDENT . $statement->output() . PHP_EOL;
78+
$this->addImport($controller, 'Illuminate\\Support\\Facades\\Mail');
79+
$this->addImport($controller, 'App\\Mail\\' . $statement->mail());
80+
} elseif ($statement instanceof ValidateStatement) {
81+
$class = $controller->name() . Str::studly($name) . 'Request';
82+
83+
$method = str_replace('\Illuminate\Http\Request $request', '\\App\\Http\\Requests\\' . $class . ' $request', $method);
84+
$method = str_replace('(Request $request)', '(' . $class . ' $request)', $method);
85+
86+
$this->addImport($controller, 'App\\Http\\Requests\\' . $class);
87+
} elseif ($statement instanceof DispatchStatement) {
88+
$body .= self::INDENT . $statement->output() . PHP_EOL;
89+
$this->addImport($controller, 'App\\Jobs\\' . $statement->job());
90+
} elseif ($statement instanceof FireStatement) {
91+
$body .= self::INDENT . $statement->output() . PHP_EOL;
92+
if (!$statement->isNamedEvent()) {
93+
$this->addImport($controller, 'App\\Events\\' . $statement->event());
94+
}
95+
} elseif ($statement instanceof RenderStatement) {
96+
$body .= self::INDENT . $statement->output() . PHP_EOL;
97+
} elseif ($statement instanceof RedirectStatement) {
98+
$body .= self::INDENT . $statement->output() . PHP_EOL;
99+
} elseif ($statement instanceof SessionStatement) {
100+
$body .= self::INDENT . $statement->output() . PHP_EOL;
101+
} elseif ($statement instanceof EloquentStatement) {
102+
// TODO: pass controller method for context..
103+
$body .= self::INDENT . $statement->output() . PHP_EOL;
104+
$this->addImport($controller, 'App\\' . Str::studly($statement->reference()));
105+
} elseif ($statement instanceof QueryStatement) {
106+
$body .= self::INDENT . $statement->output() . PHP_EOL;
107+
}
108+
109+
$body .= PHP_EOL;
110+
}
111+
112+
if (!empty($body)) {
113+
$method = str_replace('//', trim($body), $method);
114+
}
115+
116+
$methods .= PHP_EOL . $method;
62117
}
63118

64119
return trim($methods);
@@ -80,13 +135,23 @@ private function methodStub()
80135
return $stub;
81136
}
82137

83-
private function addImports(Controller $controller, $stub)
138+
protected function className(Controller $controller): string
84139
{
85-
return $stub;
140+
return $controller->name() . (Str::endsWith($controller->name(), 'Controller') ? '' : 'Controller');
86141
}
87142

88-
protected function className(Controller $controller): string
143+
private function addImport(Controller $controller, $class)
89144
{
90-
return $controller->name() . (Str::endsWith($controller->name(), 'Controller') ? '' : 'Controller');
145+
$this->imports[$controller->name()][] = $class;
146+
}
147+
148+
private function buildImports(Controller $controller)
149+
{
150+
$imports = array_unique($this->imports[$controller->name()]);
151+
sort($imports);
152+
153+
return implode(PHP_EOL, array_map(function ($class) {
154+
return 'use ' . $class . ';';
155+
}, $imports));
91156
}
92157
}

src/Lexers/StatementLexer.php

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,7 @@ public function analyze(array $tokens): array
2424
foreach ($tokens as $command => $statement) {
2525
switch ($command) {
2626
case 'query':
27-
case 'find':
28-
$statements[] = new QueryStatement();
27+
$statements[] = $this->analyzeQuery($statement);
2928
break;
3029
case 'render':
3130
$statements[] = $this->analyzeRender($statement);
@@ -48,6 +47,7 @@ public function analyze(array $tokens): array
4847
case 'save':
4948
case 'update':
5049
case 'delete':
50+
case 'find':
5151
$statements[] = new EloquentStatement($command, $statement);
5252
break;
5353
case 'flash':
@@ -126,8 +126,18 @@ private function analyzeValidate($statement)
126126
return new ValidateStatement(preg_split('/,([ \t]+)?/', $statement));
127127
}
128128

129-
private function extractTokens(string $statement, int $limit)
129+
private function extractTokens(string $statement, int $limit = -1)
130130
{
131131
return array_pad(preg_split('/[ \t]+/', $statement, $limit), $limit, null);
132132
}
133+
134+
private function analyzeQuery($statement)
135+
{
136+
$found = preg_match('/^all:(\\S+)$/', $statement, $matches);
137+
if ($found) {
138+
return new QueryStatement('all', $matches[1]);
139+
}
140+
141+
return new QueryStatement('get', '', $this->extractTokens($statement));
142+
}
133143
}

src/Models/Statements/DispatchStatement.php

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,4 +34,26 @@ public function data(): array
3434
{
3535
return $this->data;
3636
}
37+
38+
public function output()
39+
{
40+
$code = $this->job() . '::dispatch(';
41+
42+
if ($this->data()) {
43+
$code .= $this->buildParameters($this->data());
44+
}
45+
46+
$code .= ');';
47+
48+
return $code;
49+
}
50+
51+
private function buildParameters(array $data)
52+
{
53+
$parameters = array_map(function ($parameter) {
54+
return '$' . $parameter;
55+
}, $data);
56+
57+
return implode(', ', $parameters);
58+
}
3759
}

src/Models/Statements/EloquentStatement.php

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
namespace Blueprint\Models\Statements;
55

66

7+
use Illuminate\Support\Str;
8+
79
class EloquentStatement
810
{
911
/**
@@ -31,4 +33,51 @@ public function reference(): string
3133
{
3234
return $this->reference;
3335
}
36+
37+
public function output()
38+
{
39+
if ($this->operation() == 'save') {
40+
$code = "$" . Str::lower(Str::singular($this->reference()));
41+
$code .= ' = ';
42+
$code .= Str::studly($this->reference());
43+
$code .= '::create($request->all());';
44+
}
45+
46+
if ($this->operation() == 'find') {
47+
if ($this->usesQualifiedReference()) {
48+
$model = $this->extractModel();
49+
} else {
50+
// TODO: this needs to be a real model reference
51+
$model = 'Model';
52+
}
53+
$code = "$" . Str::lower(Str::singular($model));
54+
$code .= ' = ';
55+
$code .= $model;
56+
$code .= '::find($' . $this->columnName($this->reference()) . ');';
57+
}
58+
59+
// TODO: handle other operations: destroy
60+
61+
return $code;
62+
}
63+
64+
// TODO: Share this so all other places can use it (Column::columnName($qualifiedName))
65+
private function columnName($value)
66+
{
67+
if (Str::contains($value, '.')) {
68+
return Str::after($value, '.');
69+
}
70+
71+
return $value;
72+
}
73+
74+
private function usesQualifiedReference()
75+
{
76+
return Str::contains($this->reference(), '.');
77+
}
78+
79+
private function extractModel()
80+
{
81+
return Str::studly(Str::before($this->reference(), '.'));
82+
}
3483
}

src/Models/Statements/FireStatement.php

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,4 +39,38 @@ public function isNamedEvent(): bool
3939
{
4040
return preg_match('/^[a-z0-9.]+$/', $this->event) === 1;
4141
}
42+
43+
44+
public function output()
45+
{
46+
$code = 'event(';
47+
48+
if ($this->isNamedEvent()) {
49+
$code .= "'" . $this->event() . "'";
50+
51+
if ($this->data()) {
52+
$code .= ', [' . $this->buildParameters($this->data()) . ']';
53+
}
54+
} else {
55+
$code .= 'new ' . $this->event() . '(';
56+
if ($this->data()) {
57+
$code .= $this->buildParameters($this->data());
58+
}
59+
60+
$code .= ')';
61+
}
62+
63+
$code .= ');';
64+
65+
return $code;
66+
}
67+
68+
private function buildParameters(array $data)
69+
{
70+
$parameters = array_map(function ($parameter) {
71+
return '$' . $parameter;
72+
}, $data);
73+
74+
return implode(', ', $parameters);
75+
}
4276
}

0 commit comments

Comments
 (0)