Skip to content

Commit 563af3f

Browse files
Merge pull request #52 from laravel-shift/config
Nested components and configurable namespaces
2 parents 076f226 + a4e4728 commit 563af3f

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

46 files changed

+2871
-364
lines changed

composer.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@
1515
},
1616
"require-dev": {
1717
"phpunit/phpunit": "^8.0",
18-
"mockery/mockery": "^1.2"
18+
"mockery/mockery": "^1.2",
19+
"orchestra/testbench": "^4.0"
1920
},
2021
"autoload": {
2122
"psr-4": {

composer.lock

Lines changed: 1962 additions & 184 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

config/blueprint.php

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
<?php
2+
3+
return [
4+
5+
/*
6+
|--------------------------------------------------------------------------
7+
| Application Namespace
8+
|--------------------------------------------------------------------------
9+
|
10+
| Blueprint assumes a default Laravel application namespace of 'App'.
11+
| However, you may configure Blueprint to use a custom namespace.
12+
| Ultimately this should match the PSR-4 autoload value set
13+
| within the composer.json file of your application.
14+
|
15+
*/
16+
'namespace' => 'App',
17+
18+
19+
/*
20+
|--------------------------------------------------------------------------
21+
| Component Namespaces
22+
|--------------------------------------------------------------------------
23+
|
24+
| Blueprint promotes following Laravel conventions. As such, it generates
25+
| components under the default namespaces. For example, models are under
26+
| the `App` namespace. However, you may configure Blueprint to use
27+
| a custom namespace when generating these components.
28+
|
29+
*/
30+
'models_namespace' => '',
31+
'controllers_namespace' => 'Http\\Controllers',
32+
33+
34+
/*
35+
|--------------------------------------------------------------------------
36+
| Application Path
37+
|--------------------------------------------------------------------------
38+
|
39+
| Here you may customize the path where Blueprint stores generated
40+
| components. By default, Blueprint will store files under the
41+
| `app` folder However, you may change the path to store
42+
| generated component elsewhere.
43+
|
44+
*/
45+
'app_path' => app_path(),
46+
47+
];

src/Blueprint.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,11 @@ class Blueprint
1111
private $lexers = [];
1212
private $generators = [];
1313

14+
public static function relativeNamespace(string $fullyQualifiedClassName)
15+
{
16+
return ltrim(str_replace(config('blueprint.namespace'), '', $fullyQualifiedClassName), '\\');
17+
}
18+
1419
public function parse($content)
1520
{
1621
$content = preg_replace_callback('/^(\s+)(id|timestamps(Tz)?|softDeletes(Tz)?)$/mi', function ($matches) {

src/BlueprintServiceProvider.php

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
namespace Blueprint;
44

55
use Illuminate\Contracts\Support\DeferrableProvider;
6+
use Illuminate\Support\Facades\File;
67
use Illuminate\Support\ServiceProvider;
78

89
class BlueprintServiceProvider extends ServiceProvider implements DeferrableProvider
@@ -17,6 +18,10 @@ public function boot()
1718
if (!defined('STUBS_PATH')) {
1819
define('STUBS_PATH', dirname(__DIR__) . '/stubs');
1920
}
21+
22+
$this->publishes([
23+
__DIR__.'/../config/blueprint.php' => config_path('blueprint.php'),
24+
]);
2025
}
2126

2227
/**
@@ -26,6 +31,12 @@ public function boot()
2631
*/
2732
public function register()
2833
{
34+
$this->mergeConfigFrom(
35+
__DIR__.'/../config/blueprint.php', 'blueprint'
36+
);
37+
38+
File::mixin(new FileMixins());
39+
2940
$this->app->bind('command.blueprint.build',
3041
function ($app) {
3142
return new BlueprintCommand($app['files']);

src/FileMixins.php

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<?php
2+
3+
4+
namespace Blueprint;
5+
6+
7+
class FileMixins
8+
{
9+
private $stubs = [];
10+
11+
public function stub()
12+
{
13+
return function ($path) {
14+
if (!isset($this->stubs[$path])) {
15+
$this->stubs[$path] = $this->get(STUBS_PATH . DIRECTORY_SEPARATOR . $path);
16+
}
17+
18+
return $this->stubs[$path];
19+
};
20+
}
21+
}

src/Generators/ControllerGenerator.php

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

33
namespace Blueprint\Generators;
44

5+
use Blueprint\Blueprint;
56
use Blueprint\Contracts\Generator;
67
use Blueprint\Models\Controller;
78
use Blueprint\Models\Statements\DispatchStatement;
@@ -33,7 +34,7 @@ public function output(array $tree): array
3334
{
3435
$output = [];
3536

36-
$stub = $this->files->get(STUBS_PATH . '/controller/class.stub');
37+
$stub = $this->files->stub('controller/class.stub');
3738

3839
/** @var \Blueprint\Models\Controller $controller */
3940
foreach ($tree['controllers'] as $controller) {
@@ -52,7 +53,7 @@ public function output(array $tree): array
5253

5354
protected function populateStub(string $stub, Controller $controller)
5455
{
55-
$stub = str_replace('DummyNamespace', 'App\\Http\\Controllers', $stub);
56+
$stub = str_replace('DummyNamespace', $controller->fullyQualifiedNamespace(), $stub);
5657
$stub = str_replace('DummyClass', $controller->className(), $stub);
5758
$stub = str_replace('// methods...', $this->buildMethods($controller), $stub);
5859
$stub = str_replace('// imports...', $this->buildImports($controller), $stub);
@@ -62,7 +63,7 @@ protected function populateStub(string $stub, Controller $controller)
6263

6364
private function buildMethods(Controller $controller)
6465
{
65-
$template = $this->methodStub();
66+
$template = $this->files->stub('controller/method.stub');
6667

6768
$methods = '';
6869

@@ -71,7 +72,7 @@ private function buildMethods(Controller $controller)
7172

7273
if (in_array($name, ['edit', 'update', 'show', 'destroy'])) {
7374
$context = Str::singular($controller->prefix());
74-
$reference = 'App\\' . $context;
75+
$reference = config('blueprint.namespace') . '\\' . $context;
7576
$variable = '$' . Str::camel($context);
7677

7778
// TODO: verify controller prefix references a model
@@ -88,21 +89,23 @@ private function buildMethods(Controller $controller)
8889
if ($statement instanceof SendStatement) {
8990
$body .= self::INDENT . $statement->output() . PHP_EOL;
9091
$this->addImport($controller, 'Illuminate\\Support\\Facades\\Mail');
91-
$this->addImport($controller, 'App\\Mail\\' . $statement->mail());
92+
$this->addImport($controller, config('blueprint.namespace') . '\\Mail\\' . $statement->mail());
9293
} elseif ($statement instanceof ValidateStatement) {
93-
$class = $controller->name() . Str::studly($name) . 'Request';
94+
$class_name = $controller->name() . Str::studly($name) . 'Request';
9495

95-
$method = str_replace('\Illuminate\Http\Request $request', '\\App\\Http\\Requests\\' . $class . ' $request', $method);
96-
$method = str_replace('(Request $request', '(' . $class . ' $request', $method);
96+
$fqcn = config('blueprint.namespace') . '\\Http\\Requests\\' . ($controller->namespace() ? $controller->namespace() . '\\' : '') . $class_name;
9797

98-
$this->addImport($controller, 'App\\Http\\Requests\\' . $class);
98+
$method = str_replace('\Illuminate\Http\Request $request', '\\' . $fqcn . ' $request', $method);
99+
$method = str_replace('(Request $request', '(' . $class_name . ' $request', $method);
100+
101+
$this->addImport($controller, $fqcn);
99102
} elseif ($statement instanceof DispatchStatement) {
100103
$body .= self::INDENT . $statement->output() . PHP_EOL;
101-
$this->addImport($controller, 'App\\Jobs\\' . $statement->job());
104+
$this->addImport($controller, config('blueprint.namespace') . '\\Jobs\\' . $statement->job());
102105
} elseif ($statement instanceof FireStatement) {
103106
$body .= self::INDENT . $statement->output() . PHP_EOL;
104107
if (!$statement->isNamedEvent()) {
105-
$this->addImport($controller, 'App\\Events\\' . $statement->event());
108+
$this->addImport($controller, config('blueprint.namespace') . '\\Events\\' . $statement->event());
106109
}
107110
} elseif ($statement instanceof RenderStatement) {
108111
$body .= self::INDENT . $statement->output() . PHP_EOL;
@@ -112,10 +115,10 @@ private function buildMethods(Controller $controller)
112115
$body .= self::INDENT . $statement->output() . PHP_EOL;
113116
} elseif ($statement instanceof EloquentStatement) {
114117
$body .= self::INDENT . $statement->output($controller->prefix(), $name) . PHP_EOL;
115-
$this->addImport($controller, 'App\\' . $this->determineModel($controller->prefix(), $statement->reference()));
118+
$this->addImport($controller, config('blueprint.namespace') . '\\' . ($controller->namespace() ? $controller->namespace() . '\\' : '') . $this->determineModel($controller, $statement->reference()));
116119
} elseif ($statement instanceof QueryStatement) {
117120
$body .= self::INDENT . $statement->output($controller->prefix()) . PHP_EOL;
118-
$this->addImport($controller, 'App\\' . $this->determineModel($controller->prefix(), $statement->model()));
121+
$this->addImport($controller, config('blueprint.namespace') . '\\' . ($controller->namespace() ? $controller->namespace() . '\\' : '') . $this->determineModel($controller, $statement->model()));
119122
}
120123

121124
$body .= PHP_EOL;
@@ -133,18 +136,9 @@ private function buildMethods(Controller $controller)
133136

134137
protected function getPath(Controller $controller)
135138
{
136-
return 'app/Http/Controllers/' . $controller->className() . '.php';
137-
}
138-
139-
private function methodStub()
140-
{
141-
static $stub = '';
139+
$path = str_replace('\\', '/', Blueprint::relativeNamespace($controller->fullyQualifiedClassName()));
142140

143-
if (empty($stub)) {
144-
$stub = $this->files->get(STUBS_PATH . '/controller/method.stub');
145-
}
146-
147-
return $stub;
141+
return config('blueprint.app_path') . '/' . $path . '.php';
148142
}
149143

150144
private function addImport(Controller $controller, $class)
@@ -162,10 +156,10 @@ private function buildImports(Controller $controller)
162156
}, $imports));
163157
}
164158

165-
private function determineModel(string $prefix, ?string $reference)
159+
private function determineModel(Controller $controller, ?string $reference)
166160
{
167161
if (empty($reference) || $reference === 'id') {
168-
return Str::studly(Str::singular($prefix));
162+
return Str::studly(Str::singular($controller->prefix()));
169163
}
170164

171165
if (Str::contains($reference, '.')) {

src/Generators/FactoryGenerator.php

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ public function output(array $tree): array
2222
{
2323
$output = [];
2424

25-
$stub = $this->files->get(STUBS_PATH . '/factory.stub');
25+
$stub = $this->files->stub('factory.stub');
2626

2727
/** @var \Blueprint\Models\Model $model */
2828
foreach ($tree['models'] as $model) {
@@ -40,12 +40,17 @@ public function output(array $tree): array
4040

4141
protected function getPath(Model $model)
4242
{
43-
return 'database/factories/' . $model->name() . 'Factory.php';
43+
$path = $model->name();
44+
if ($model->namespace()) {
45+
$path = str_replace('\\', '/', $model->namespace()) . '/' . $path;
46+
}
47+
48+
return 'database/factories/' . $path . 'Factory.php';
4449
}
4550

4651
protected function populateStub(string $stub, Model $model)
4752
{
48-
$stub = str_replace('DummyNamespace', 'App', $stub);
53+
$stub = str_replace('DummyModel', $model->fullyQualifiedClassName(), $stub);
4954
$stub = str_replace('DummyClass', $model->name(), $stub);
5055
$stub = str_replace('// definition...', $this->buildDefinition($model), $stub);
5156

@@ -67,7 +72,7 @@ protected function buildDefinition(Model $model)
6772
$class = Str::studly($column->attributes()[0] ?? $name);
6873

6974
$definition .= self::INDENT . "'{$column->name()}' => ";
70-
$definition .= sprintf("factory(\App\%s::class)", $class);
75+
$definition .= sprintf("factory(%s::class)", '\\' . $model->fullyQualifiedNamespace() . '\\' . $class);
7176
$definition .= ',' . PHP_EOL;
7277
} else {
7378
$definition .= self::INDENT . "'{$column->name()}' => ";

src/Generators/MigrationGenerator.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ public function output(array $tree): array
2323
{
2424
$output = [];
2525

26-
$stub = $this->files->get(STUBS_PATH . '/migration.stub');
26+
$stub = $this->files->stub('migration.stub');
2727

2828
$sequential_timestamp = \Carbon\Carbon::now()->subSeconds(count($tree['models']));
2929

src/Generators/ModelGenerator.php

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

33
namespace Blueprint\Generators;
44

5+
use Blueprint\Blueprint;
56
use Blueprint\Contracts\Generator;
67
use Blueprint\Models\Column;
78
use Blueprint\Models\Model;
@@ -21,7 +22,7 @@ public function output(array $tree): array
2122
{
2223
$output = [];
2324

24-
$stub = $this->files->get(STUBS_PATH . '/model/class.stub');
25+
$stub = $this->files->stub('model/class.stub');
2526

2627
/** @var \Blueprint\Models\Model $model */
2728
foreach ($tree['models'] as $model) {
@@ -39,7 +40,7 @@ public function output(array $tree): array
3940

4041
protected function populateStub(string $stub, Model $model)
4142
{
42-
$stub = str_replace('DummyNamespace', 'App', $stub);
43+
$stub = str_replace('DummyNamespace', $model->fullyQualifiedNamespace(), $stub);
4344
$stub = str_replace('DummyClass', $model->name(), $stub);
4445

4546
$body = $this->buildProperties($model);
@@ -58,19 +59,19 @@ private function buildProperties(Model $model)
5859

5960
$columns = $this->fillableColumns($model->columns());
6061
if (!empty($columns)) {
61-
$properties .= PHP_EOL . str_replace('[]', $this->pretty_print_array($columns, false), $this->getStub('fillable'));
62+
$properties .= PHP_EOL . str_replace('[]', $this->pretty_print_array($columns, false), $this->files->stub('model/fillable.stub'));
6263
} else {
63-
$properties .= $this->getStub('fillable');
64+
$properties .= $this->files->stub('model/fillable.stub');
6465
}
6566

6667
$columns = $this->castableColumns($model->columns());
6768
if (!empty($columns)) {
68-
$properties .= PHP_EOL . str_replace('[]', $this->pretty_print_array($columns), $this->getStub('casts'));
69+
$properties .= PHP_EOL . str_replace('[]', $this->pretty_print_array($columns), $this->files->stub('model/casts.stub'));
6970
}
7071

7172
$columns = $this->dateColumns($model->columns());
7273
if (!empty($columns)) {
73-
$properties .= PHP_EOL . str_replace('[]', $this->pretty_print_array($columns, false), $this->getStub('dates'));
74+
$properties .= PHP_EOL . str_replace('[]', $this->pretty_print_array($columns, false), $this->files->stub('model/dates.stub'));
7475
}
7576

7677
return trim($properties);
@@ -87,13 +88,13 @@ private function buildRelationships(Model $model)
8788
}
8889

8990
$methods = '';
90-
$template = $this->getStub('method');
91+
$template = $this->files->stub('model/method.stub');
9192

9293
/** @var Column $column */
9394
foreach ($columns as $column) {
9495
$name = Str::beforeLast($column->name(), '_id');
9596
$class = Str::studly($column->attributes()[0] ?? $name);
96-
$relationship = sprintf("\$this->belongsTo(\App\%s::class)", $class);
97+
$relationship = sprintf("\$this->belongsTo(%s::class)", '\\' . $model->fullyQualifiedNamespace() . '\\' . $class);
9798

9899
$method = str_replace('DummyName', Str::camel($name), $template);
99100
$method = str_replace('null', $relationship, $method);
@@ -106,7 +107,9 @@ private function buildRelationships(Model $model)
106107

107108
protected function getPath(Model $model)
108109
{
109-
return 'app/' . $model->name() . '.php';
110+
$path = str_replace('\\', '/', Blueprint::relativeNamespace($model->fullyQualifiedClassName()));
111+
112+
return config('blueprint.app_path') . '/' . $path . '.php';
110113
}
111114

112115
private function fillableColumns(array $columns)
@@ -174,17 +177,6 @@ private function pretty_print_array(array $data, $assoc = true)
174177
return trim($output);
175178
}
176179

177-
private function getStub(string $stub)
178-
{
179-
static $stubs = [];
180-
181-
if (empty($stubs[$stub])) {
182-
$stubs[$stub] = $this->files->get(STUBS_PATH . '/model/' . $stub . '.stub');
183-
}
184-
185-
return $stubs[$stub];
186-
}
187-
188180
private function addTraits(Model $model, $stub)
189181
{
190182
if (!$model->usesSoftDeletes()) {

0 commit comments

Comments
 (0)