diff --git a/config/routes.php b/config/routes.php index aa40a8b16b..4f78ccc93c 100644 --- a/config/routes.php +++ b/config/routes.php @@ -16,6 +16,18 @@ 'enabled' => true, + /* + |-------------------------------------------------------------------------- + | Enable Route Binding + |-------------------------------------------------------------------------- + | + | Do you want to enable route binding on Statamic Repositories + | (entry, taxonomy, collection etc) on front end routes? + | + */ + + 'binding_enabled' => true, + /* |-------------------------------------------------------------------------- | Action Route Prefix diff --git a/src/Providers/RouteServiceProvider.php b/src/Providers/RouteServiceProvider.php index 551fe06a92..92863811cb 100644 --- a/src/Providers/RouteServiceProvider.php +++ b/src/Providers/RouteServiceProvider.php @@ -46,12 +46,17 @@ public function boot() protected function bindCollections() { Route::bind('collection', function ($handle, $route = null) { - if (! $this->isCpOrApiRoute($route)) { - return $handle; + if (! $this->isFrontendBindingEnabled()) { + if (! $this->isCpOrApiRoute($route)) { + return $handle; + } } + $field = $route->bindingFields()['collection'] ?? 'handle'; + $collection = $field == 'handle' ? Collection::findByHandle($handle) : Collection::all()->firstWhere($field, $handle); + throw_unless( - $collection = Collection::findByHandle($handle), + $collection, new NotFoundHttpException("Collection [$handle] not found.") ); @@ -62,12 +67,17 @@ protected function bindCollections() protected function bindEntries() { Route::bind('entry', function ($handle, $route = null) { - if ($this->isApiRoute($route) || ! $this->isCpRoute($route)) { - return $handle; + if (! $this->isFrontendBindingEnabled()) { + if ($this->isApiRoute($route) || ! $this->isCpRoute($route)) { + return $handle; + } } + $field = $route->bindingFields()['entry'] ?? 'id'; + $entry = $field == 'id' ? Entry::find($handle) : Entry::query()->where($field, $handle)->first(); + throw_if( - ! ($entry = Entry::find($handle)) || $entry->collection()->id() !== $route->parameter('collection')->id(), + ! ($entry) || ($route->parameter('collection') && ($entry->collection()->id() !== $route->parameter('collection')->id())), new NotFoundHttpException("Entry [$handle] not found.") ); @@ -78,12 +88,17 @@ protected function bindEntries() protected function bindTaxonomies() { Route::bind('taxonomy', function ($handle, $route = null) { - if (! $this->isCpOrApiRoute($route)) { - return $handle; + if (! $this->isFrontendBindingEnabled()) { + if (! $this->isCpOrApiRoute($route)) { + return $handle; + } } + $field = $route->bindingFields()['taxonomy'] ?? 'handle'; + $taxonomy = $field == 'handle' ? Taxonomy::findByHandle($handle) : Taxonomy::all()->firstWhere($field, $handle); + throw_unless( - $taxonomy = Taxonomy::findByHandle($handle), + $taxonomy, new NotFoundHttpException("Taxonomy [$handle] not found.") ); @@ -94,15 +109,21 @@ protected function bindTaxonomies() protected function bindTerms() { Route::bind('term', function ($handle, $route = null) { - if ($this->isApiRoute($route) || ! $this->isCpRoute($route)) { - return $handle; + if (! $this->isFrontendBindingEnabled()) { + if ($this->isApiRoute($route) || ! $this->isCpRoute($route)) { + return $handle; + } } - $id = $route->parameter('taxonomy')->handle().'::'.$handle; + $field = $route->bindingFields()['term'] ?? 'id'; + + $handle = ($field == 'id' && $route->parameter('taxonomy') ? $route->parameter('taxonomy')->handle().'::' : '').$handle; $site = $route->parameter('site') ?? Site::default()->handle(); + $term = $field == 'id' ? Term::find($handle)->in($site) : Term::query()->where($field, $handle)->where('site', $site)->when($route->parameter('taxonomy'), fn ($query) => $query->where('taxonomy', $route->parameter('taxonomy')->handle()))->first(); + throw_unless( - ($term = Term::find($id)->in($site)) && $term->taxonomy()->id() === $route->parameter('taxonomy')->id(), + $term && ($route->parameter('taxonomy') && $term->taxonomy()->id() === $route->parameter('taxonomy')->id()), new NotFoundHttpException("Taxonomy term [$handle] not found.") ); @@ -113,12 +134,17 @@ protected function bindTerms() protected function bindAssetContainers() { Route::bind('asset_container', function ($handle, $route = null) { - if (! $this->isCpOrApiRoute($route)) { - return $handle; + if (! $this->isFrontendBindingEnabled()) { + if (! $this->isCpOrApiRoute($route)) { + return $handle; + } } + $field = $route->bindingFields()['asset_container'] ?? 'handle'; + $container = $field == 'handle' ? AssetContainer::findByHandle($handle) : AssetContainer::all()->firstWhere($field, $handle); + throw_unless( - $container = AssetContainer::findByHandle($handle), + $container, new NotFoundHttpException("Asset container [$handle] not found.") ); @@ -129,14 +155,20 @@ protected function bindAssetContainers() protected function bindAssets() { Route::bind('asset', function ($handle, $route = null) { - if (! $this->isCpOrApiRoute($route)) { - return $handle; + if (! $this->isFrontendBindingEnabled()) { + if (! $this->isCpOrApiRoute($route)) { + return $handle; + } } - $id = $route->parameter('asset_container')->handle().'::'.$handle; + $field = $route->bindingFields()['asset'] ?? 'id'; + + $handle = ($field == 'id' && $route->parameter('asset_container') ? $route->parameter('asset_container')->handle().'::' : '').$handle; + + $asset = $field == 'id' ? Asset::find($handle) : Asset::query()->where($field, $handle)->when($route->parameter('asset_container'), fn ($query) => $query->where('container', $route->parameter('container')->handle()))->first(); throw_unless( - $asset = Asset::find($id), + $asset, new NotFoundHttpException("Asset [$handle] not found.") ); @@ -147,14 +179,19 @@ protected function bindAssets() protected function bindGlobalSets() { Route::bind('global', function ($handle, $route = null) { - if (! $this->isCpOrApiRoute($route)) { - return $handle; + if (! $this->isFrontendBindingEnabled()) { + if (! $this->isCpOrApiRoute($route)) { + return $handle; + } } + $field = $route->bindingFields()['global'] ?? 'handle'; + $global = $field == 'handle' ? GlobalSet::findByHandle($handle) : GlobalSet::all()->firstWhere($field, $handle); + $site = Site::default()->handle(); throw_unless( - $globalSet = GlobalSet::findByHandle($handle)->in($site), + $globalSet = $global->in($site), new NotFoundHttpException("Global set [$handle] not found.") ); @@ -165,12 +202,17 @@ protected function bindGlobalSets() protected function bindSites() { Route::bind('site', function ($handle, $route = null) { - if (! $this->isCpOrApiRoute($route)) { - return $handle; + if (! $this->isFrontendBindingEnabled()) { + if (! $this->isCpOrApiRoute($route)) { + return $handle; + } } + $field = $route->bindingFields()['site'] ?? 'handle'; + $site = $field == 'handle' ? Site::get($handle) : Site::all()->firstWhere($field, $handle); + throw_unless( - $site = Site::get($handle), + $site, new NotFoundHttpException("Site [$handle] not found.") ); @@ -181,8 +223,10 @@ protected function bindSites() protected function bindRevisions() { Route::bind('revision', function ($reference, $route = null) { - if (! $this->isCpOrApiRoute($route)) { - return $reference; + if (! $this->isFrontendBindingEnabled()) { + if (! $this->isCpOrApiRoute($route)) { + return $reference; + } } if ($route->hasParameter('entry')) { @@ -205,13 +249,18 @@ protected function bindRevisions() protected function bindForms() { Route::bind('form', function ($handle, $route = null) { - if (! $this->isCpOrApiRoute($route) - && ! Str::startsWith($route->uri(), config('statamic.routes.action').'/forms/')) { - return $handle; + if (! $this->isFrontendBindingEnabled()) { + if (! $this->isCpOrApiRoute($route) + && ! Str::startsWith($route->uri(), config('statamic.routes.action').'/forms/')) { + return $handle; + } } + $field = $route->bindingFields()['form'] ?? 'handle'; + $form = $field == 'handle' ? Form::find($handle) : Form::all()->firstWhere($field, $handle); + throw_unless( - $form = Form::find($handle), + $form, new NotFoundHttpException("Form [$handle] not found.") ); @@ -219,6 +268,11 @@ protected function bindForms() }); } + private function isFrontendBindingEnabled() + { + return config('statamic.routes.binding_enabled', false); + } + private function isApiRoute(\Illuminate\Routing\Route $route = null) { if (is_null($route)) {