diff --git a/config/system.php b/config/system.php index f4705eb5d8..a8777f1406 100644 --- a/config/system.php +++ b/config/system.php @@ -269,4 +269,21 @@ 'layout' => env('STATAMIC_LAYOUT', 'layout'), + /* + |-------------------------------------------------------------------------- + | Blueprint Templates + |-------------------------------------------------------------------------- + | + | When an entry's template is set to `@blueprint`, Statamic will look for + | a view named `{collection}.{blueprint}`. You may override this logic + | on a per-collection basis here. + | + | https://statamic.dev/content-modeling/collections#templates + | + */ + + 'blueprint_templates' => [ + // + ], + ]; diff --git a/src/Entries/Entry.php b/src/Entries/Entry.php index 95bc7406f3..c8315f48ef 100644 --- a/src/Entries/Entry.php +++ b/src/Entries/Entry.php @@ -583,7 +583,10 @@ public function template($template = null) protected function inferTemplateFromBlueprint() { - $template = $this->collection()->handle().'.'.$this->blueprint(); + $handle = $this->collection()->handle(); + $prefix = config('statamic.system.blueprint_templates.'.$handle, $handle); + + $template = $prefix.'.'.$this->blueprint(); $slugifiedTemplate = str_replace('_', '-', $template); diff --git a/tests/Data/Entries/EntryTest.php b/tests/Data/Entries/EntryTest.php index 3e71792c47..e93fce2ddb 100644 --- a/tests/Data/Entries/EntryTest.php +++ b/tests/Data/Entries/EntryTest.php @@ -2048,6 +2048,35 @@ public function it_gets_and_sets_an_inferred_template_from_blueprint() $this->assertEquals('articles.custom', $entry->template()); } + #[Test] + public function it_respects_custom_blueprint_template_path_per_collection() + { + config(['statamic.system.blueprint_templates' => [ + 'articles' => 'custom.path', + ]]); + + $articles = tap(Collection::make('articles')->template('@blueprint'))->save(); + $pages = tap(Collection::make('pages')->template('@blueprint'))->save(); + $blueprint = tap(Blueprint::make('standard_article')->setNamespace('collections.articles'))->save(); + + $articleEntry = Entry::make('test')->collection($articles)->blueprint($blueprint->handle()); + $pageEntry = Entry::make('test')->collection($pages)->blueprint($blueprint->handle()); + + // mapped collection uses the mapped prefix instead of the collection handle + $this->assertEquals('custom.path.standard_article', $articleEntry->template()); + + // unmapped collection still uses its handle as the prefix + $this->assertEquals('pages.standard_article', $pageEntry->template()); + + // mapped collection uses slugified prefix when that template exists + View::shouldReceive('exists')->with('custom.path.standard-article')->andReturn(true); + $this->assertEquals('custom.path.standard-article', $articleEntry->template()); + + // entry level template still overrides @blueprint + $articleEntry->template('articles.custom'); + $this->assertEquals('articles.custom', $articleEntry->template()); + } + #[Test] public function it_gets_and_sets_the_layout() {