Skip to content

Commit cc17b29

Browse files
[5.x] Ability to configure blueprint storage paths (#10639)
Co-authored-by: Jason Varga <[email protected]>
1 parent 558a5ee commit cc17b29

File tree

5 files changed

+111
-17
lines changed

5 files changed

+111
-17
lines changed

config/system.php

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,28 @@
4545

4646
'addons_path' => base_path('addons'),
4747

48+
/*
49+
|--------------------------------------------------------------------------
50+
| Blueprints Path
51+
|--------------------------------------------------------------------------
52+
|
53+
| Where your blueprint YAML files are stored.
54+
|
55+
*/
56+
57+
'blueprints_path' => resource_path('blueprints'),
58+
59+
/*
60+
|--------------------------------------------------------------------------
61+
| Fieldsets Path
62+
|--------------------------------------------------------------------------
63+
|
64+
| Where your fieldset YAML files are stored.
65+
|
66+
*/
67+
68+
'fieldsets_path' => resource_path('fieldsets'),
69+
4870
/*
4971
|--------------------------------------------------------------------------
5072
| Send the Powered-By Header

src/Fields/Blueprint.php

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -138,9 +138,8 @@ public function path()
138138
$namespace = 'vendor/'.$namespace;
139139
}
140140

141-
return Path::tidy(vsprintf('%s/%s/%s.yaml', [
142-
Facades\Blueprint::directory(),
143-
$namespace,
141+
return Path::tidy(vsprintf('%s/%s.yaml', [
142+
Facades\Blueprint::namespaceDirectory($namespace),
144143
$this->handle(),
145144
]));
146145
}

src/Fields/BlueprintRepository.php

Lines changed: 57 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
namespace Statamic\Fields;
44

55
use Closure;
6+
use Exception;
67
use Statamic\Exceptions\BlueprintNotFoundException;
78
use Statamic\Facades\Blink;
89
use Statamic\Facades\File;
@@ -17,20 +18,43 @@ class BlueprintRepository
1718
protected const BLINK_FROM_FILE = 'blueprints.from-file';
1819
protected const BLINK_NAMESPACE_PATHS = 'blueprints.paths-in-namespace';
1920

20-
protected $directory;
21+
protected $directories = ['default' => null];
2122
protected $fallbacks = [];
2223
protected $additionalNamespaces = [];
2324

24-
public function setDirectory(string $directory)
25+
public function setDirectories(string|array $directories)
2526
{
26-
$this->directory = Path::tidy($directory);
27+
if (is_string($directories)) {
28+
$directories = ['default' => $directories];
29+
}
30+
31+
$this->directories = collect($directories)
32+
->map(fn ($directory) => Path::tidy($directory))
33+
->all();
34+
35+
if (! isset($this->directories['default'])) {
36+
throw new Exception('Default blueprint directory not provided');
37+
}
2738

2839
return $this;
2940
}
3041

42+
/** @deprecated */
43+
public function setDirectory(string $directory)
44+
{
45+
return $this->setDirectories($directory);
46+
}
47+
3148
public function directory()
3249
{
33-
return $this->directory;
50+
return $this->directories['default'];
51+
}
52+
53+
public function namespaceDirectory($namespace)
54+
{
55+
return isset($this->directories[$namespace])
56+
? $this->directories[$namespace]
57+
: $this->directories['default'].'/'.$namespace;
3458
}
3559

3660
public function find($blueprint): ?Blueprint
@@ -69,7 +93,14 @@ public function findStandardBlueprintPath($handle)
6993
return null;
7094
}
7195

72-
return $this->directory.'/'.str_replace('.', '/', $handle).'.yaml';
96+
if (! Str::contains($handle, '.')) {
97+
return $this->directory().'/'.str_replace('.', '/', $handle).'.yaml';
98+
}
99+
100+
$namespace = Str::before($handle, '.');
101+
$handle = Str::after($handle, '.');
102+
103+
return $this->namespaceDirectory($namespace).'/'.str_replace('.', '/', $handle).'.yaml';
73104
}
74105

75106
public function findNamespacedBlueprintPath($handle)
@@ -79,7 +110,7 @@ public function findNamespacedBlueprintPath($handle)
79110
$handle = str_replace('/', '.', $handle);
80111
$path = str_replace('.', '/', $handle);
81112

82-
$overridePath = "{$this->directory}/vendor/{$namespaceDir}/{$path}.yaml";
113+
$overridePath = "{$this->directory()}/vendor/{$namespaceDir}/{$path}.yaml";
83114

84115
if (File::exists($overridePath)) {
85116
return $overridePath;
@@ -233,7 +264,7 @@ protected function filesIn($namespace)
233264
return Blink::store(self::BLINK_NAMESPACE_PATHS)->once($namespace, function () use ($namespace) {
234265
$namespace = str_replace('/', '.', $namespace);
235266
$namespaceDir = str_replace('.', '/', $namespace);
236-
$directory = $this->directory.'/'.$namespaceDir;
267+
$directory = $this->directory().'/'.$namespaceDir;
237268

238269
if (isset($this->additionalNamespaces[$namespace])) {
239270
$directory = "{$this->additionalNamespaces[$namespace]}";
@@ -243,7 +274,7 @@ protected function filesIn($namespace)
243274
->getFilesByType($directory, 'yaml')
244275
->mapWithKeys(fn ($path) => [Str::after($path, $directory.'/') => $path]);
245276

246-
if (File::exists($directory = $this->directory.'/vendor/'.$namespaceDir)) {
277+
if (File::exists($directory = $this->directory().'/vendor/'.$namespaceDir)) {
247278
$overrides = File::withAbsolutePaths()
248279
->getFilesByType($directory, 'yaml')
249280
->mapWithKeys(fn ($path) => [Str::after($path, $directory.'/') => $path]);
@@ -259,9 +290,7 @@ protected function makeBlueprintFromFile($path, $namespace = null)
259290
{
260291
return Blink::store(self::BLINK_FROM_FILE)->once($path, function () use ($path, $namespace) {
261292
if (! $namespace || ! isset($this->additionalNamespaces[$namespace])) {
262-
[$namespace, $handle] = $this->getNamespaceAndHandle(
263-
Str::after(Str::before($path, '.yaml'), $this->directory.'/')
264-
);
293+
[$namespace, $handle] = $this->getNamespaceAndHandleFromPath($path);
265294
} else {
266295
$handle = Str::of($path)->afterLast('/')->before('.');
267296
}
@@ -287,4 +316,21 @@ protected function getNamespaceAndHandle($blueprint)
287316

288317
return [$namespace, $handle];
289318
}
319+
320+
public function getNamespaceAndHandleFromPath($path)
321+
{
322+
$namespace = collect($this->directories)
323+
->search(function ($directory) use ($path) {
324+
return Str::startsWith($path, $directory);
325+
});
326+
327+
if ($namespace === 'default') {
328+
return $this->getNamespaceAndHandle(Str::after(Str::before($path, '.yaml'), $this->directory().'/'));
329+
}
330+
331+
$directory = $this->directories[$namespace];
332+
$handle = Str::after(Str::before($path, '.yaml'), $directory.'/');
333+
334+
return [$namespace, $handle];
335+
}
290336
}

src/Providers/AppServiceProvider.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,7 @@ public function register()
150150

151151
$this->app->singleton(\Statamic\Fields\BlueprintRepository::class, function () {
152152
return (new \Statamic\Fields\BlueprintRepository)
153-
->setDirectory(resource_path('blueprints'))
153+
->setDirectories(config('statamic.system.blueprints_path'))
154154
->setFallback('default', function () {
155155
return \Statamic\Facades\Blueprint::makeFromFields([
156156
'content' => ['type' => 'markdown', 'localizable' => true],
@@ -160,7 +160,7 @@ public function register()
160160

161161
$this->app->singleton(\Statamic\Fields\FieldsetRepository::class, function () {
162162
return (new \Statamic\Fields\FieldsetRepository)
163-
->setDirectory(resource_path('fieldsets'));
163+
->setDirectory(config('statamic.system.fieldsets_path'));
164164
});
165165

166166
$this->app->singleton(FieldsetRecursionStack::class);

tests/Fields/BlueprintRepositoryTest.php

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ public function setUp(): void
2121
parent::setUp();
2222

2323
$this->repo = app(BlueprintRepository::class)
24-
->setDirectory('/path/to/resources/blueprints');
24+
->setDirectories('/path/to/resources/blueprints');
2525

2626
Facades\Blueprint::swap($this->repo);
2727
}
@@ -439,4 +439,31 @@ public function find_or_fail_throws_exception_when_blueprint_does_not_exist()
439439

440440
$this->repo->findOrFail('does-not-exist');
441441
}
442+
443+
/** @test */
444+
public function it_gets_a_blueprint_from_split_repository()
445+
{
446+
$repo = (new BlueprintRepository())
447+
->setDirectories([
448+
'default' => '/path/to/resources/blueprints',
449+
'forms' => '/path/to/content/forms/blueprints',
450+
]);
451+
452+
$contents = <<<'EOT'
453+
title: Test
454+
tabs:
455+
main:
456+
fields:
457+
- one
458+
- two
459+
EOT;
460+
File::shouldReceive('exists')->with('/path/to/resources/blueprints/globals/test.yaml')->once()->andReturnTrue();
461+
File::shouldReceive('get')->with('/path/to/resources/blueprints/globals/test.yaml')->once()->andReturn($contents);
462+
463+
File::shouldReceive('exists')->with('/path/to/content/forms/blueprints/test.yaml')->once()->andReturnTrue();
464+
File::shouldReceive('get')->with('/path/to/content/forms/blueprints/test.yaml')->once()->andReturn($contents);
465+
466+
$repo->find('globals.test');
467+
$repo->find('forms.test');
468+
}
442469
}

0 commit comments

Comments
 (0)