Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[5.x] Ability to configure blueprint storage paths #10639

Open
wants to merge 8 commits into
base: 5.x
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions config/system.php
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,28 @@

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

/*
|--------------------------------------------------------------------------
| Blueprints Path
|--------------------------------------------------------------------------
|
| Where your blueprint YAML files are stored.
|
*/

'blueprints_path' => resource_path('blueprints'),

/*
|--------------------------------------------------------------------------
| Fieldsets Path
|--------------------------------------------------------------------------
|
| Where your fieldset YAML files are stored.
|
*/

'fieldsets_path' => resource_path('fieldsets'),

/*
|--------------------------------------------------------------------------
| Send the Powered-By Header
Expand Down
5 changes: 2 additions & 3 deletions src/Fields/Blueprint.php
Original file line number Diff line number Diff line change
Expand Up @@ -123,9 +123,8 @@ public function path()
$namespace = 'vendor/'.$namespace;
}

return Path::tidy(vsprintf('%s/%s/%s.yaml', [
Facades\Blueprint::directory(),
$namespace,
return Path::tidy(vsprintf('%s/%s.yaml', [
Facades\Blueprint::namespaceDirectory($namespace),
$this->handle(),
]));
}
Expand Down
68 changes: 57 additions & 11 deletions src/Fields/BlueprintRepository.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace Statamic\Fields;

use Closure;
use Exception;
use Statamic\Exceptions\BlueprintNotFoundException;
use Statamic\Facades\Blink;
use Statamic\Facades\File;
Expand All @@ -17,20 +18,43 @@ class BlueprintRepository
protected const BLINK_FROM_FILE = 'blueprints.from-file';
protected const BLINK_NAMESPACE_PATHS = 'blueprints.paths-in-namespace';

protected $directory;
protected $directories = ['default' => null];
protected $fallbacks = [];
protected $additionalNamespaces = [];

public function setDirectory(string $directory)
public function setDirectories(string|array $directories)
{
$this->directory = Path::tidy($directory);
if (is_string($directories)) {
$directories = ['default' => $directories];
}

$this->directories = collect($directories)
->map(fn ($directory) => Path::tidy($directory))
->all();

if (! isset($this->directories['default'])) {
throw new Exception('Default blueprint directory not provided');
}

return $this;
}

/** @deprecated */
public function setDirectory(string $directory)
{
return $this->setDirectories($directory);
}

public function directory()
{
return $this->directory;
return $this->directories['default'];
}

public function namespaceDirectory($namespace)
{
return isset($this->directories[$namespace])
? $this->directories[$namespace]
: $this->directories['default'].'/'.$namespace;
}

public function find($blueprint): ?Blueprint
Expand Down Expand Up @@ -69,7 +93,14 @@ public function findStandardBlueprintPath($handle)
return null;
}

return $this->directory.'/'.str_replace('.', '/', $handle).'.yaml';
if (! Str::contains($handle, '.')) {
return $this->directory().'/'.str_replace('.', '/', $handle).'.yaml';
}

$namespace = Str::before($handle, '.');
$handle = Str::after($handle, '.');

return $this->namespaceDirectory($namespace).'/'.str_replace('.', '/', $handle).'.yaml';
}

public function findNamespacedBlueprintPath($handle)
Expand All @@ -79,7 +110,7 @@ public function findNamespacedBlueprintPath($handle)
$handle = str_replace('/', '.', $handle);
$path = str_replace('.', '/', $handle);

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

if (File::exists($overridePath)) {
return $overridePath;
Expand Down Expand Up @@ -233,7 +264,7 @@ protected function filesIn($namespace)
return Blink::store(self::BLINK_NAMESPACE_PATHS)->once($namespace, function () use ($namespace) {
$namespace = str_replace('/', '.', $namespace);
$namespaceDir = str_replace('.', '/', $namespace);
$directory = $this->directory.'/'.$namespaceDir;
$directory = $this->directory().'/'.$namespaceDir;

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

if (File::exists($directory = $this->directory.'/vendor/'.$namespaceDir)) {
if (File::exists($directory = $this->directory().'/vendor/'.$namespaceDir)) {
$overrides = File::withAbsolutePaths()
->getFilesByType($directory, 'yaml')
->mapWithKeys(fn ($path) => [Str::after($path, $directory.'/') => $path]);
Expand All @@ -259,9 +290,7 @@ protected function makeBlueprintFromFile($path, $namespace = null)
{
return Blink::store(self::BLINK_FROM_FILE)->once($path, function () use ($path, $namespace) {
if (! $namespace || ! isset($this->additionalNamespaces[$namespace])) {
[$namespace, $handle] = $this->getNamespaceAndHandle(
Str::after(Str::before($path, '.yaml'), $this->directory.'/')
);
[$namespace, $handle] = $this->getNamespaceAndHandleFromPath($path);
} else {
$handle = Str::of($path)->afterLast('/')->before('.');
}
Expand All @@ -287,4 +316,21 @@ protected function getNamespaceAndHandle($blueprint)

return [$namespace, $handle];
}

public function getNamespaceAndHandleFromPath($path)
{
$namespace = collect($this->directories)
->search(function ($directory) use ($path) {
return Str::startsWith($path, $directory);
});

if ($namespace === 'default') {
return $this->getNamespaceAndHandle(Str::after(Str::before($path, '.yaml'), $this->directory().'/'));
}

$directory = $this->directories[$namespace];
$handle = Str::after(Str::before($path, '.yaml'), $directory.'/');

return [$namespace, $handle];
}
}
4 changes: 2 additions & 2 deletions src/Providers/AppServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ public function register()

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

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

$this->app->singleton(FieldsetRecursionStack::class);
Expand Down
29 changes: 28 additions & 1 deletion tests/Fields/BlueprintRepositoryTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public function setUp(): void
parent::setUp();

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

Facades\Blueprint::swap($this->repo);
}
Expand Down Expand Up @@ -439,4 +439,31 @@ public function find_or_fail_throws_exception_when_blueprint_does_not_exist()

$this->repo->findOrFail('does-not-exist');
}

/** @test */
public function it_gets_a_blueprint_from_split_repository()
{
$repo = (new BlueprintRepository())
->setDirectories([
'default' => '/path/to/resources/blueprints',
'forms' => '/path/to/content/forms/blueprints',
]);

$contents = <<<'EOT'
title: Test
tabs:
main:
fields:
- one
- two
EOT;
File::shouldReceive('exists')->with('/path/to/resources/blueprints/globals/test.yaml')->once()->andReturnTrue();
File::shouldReceive('get')->with('/path/to/resources/blueprints/globals/test.yaml')->once()->andReturn($contents);

File::shouldReceive('exists')->with('/path/to/content/forms/blueprints/test.yaml')->once()->andReturnTrue();
File::shouldReceive('get')->with('/path/to/content/forms/blueprints/test.yaml')->once()->andReturn($contents);

$repo->find('globals.test');
$repo->find('forms.test');
}
}
Loading