Skip to content

Commit 9ec9e6b

Browse files
authored
Merge pull request #17998 from craftcms/feature/routes
[6.x] Routes
2 parents 6a2f2a0 + cccef8f commit 9ec9e6b

File tree

27 files changed

+720
-544
lines changed

27 files changed

+720
-544
lines changed

CHANGELOG-WIP.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,11 @@ Moved the following controllers:
276276
- Removed `craft\models\ReadOnlyProjectConfigData` in favor of `CraftCms\Cms\ProjectConfig\Data\ReadOnlyProjectConfigData`
277277
- Deprecated `craft\helpers\ProjectConfig`. `CraftCms\Cms\ProjectConfig\ProjectConfigHelper` should be used instead.
278278

279+
## Routes
280+
281+
- Deprecated `craft\services\Routes`. `CraftCms\Cms\Route\Routes` should be used instead.
282+
- Using routes in `config/routes.php` is no longer supported. Register routes using [Laravel's routing](https://laravel.com/docs/12.x/routing) instead.
283+
279284
## Sections
280285

281286
- Deprecated the section related methods in `craft\services\Entries`. `CraftCms\Cms\Section\Sections` should be used instead.

packages/craftcms-asset-bundles/bundles/routes/dist/routes.js

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

resources/templates/settings/routes.twig

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
{% from '_includes/forms.twig' import button %}
2-
{% requireAdmin false %}
32

43
{% extends "_layouts/cp" %}
54
{% set title = "Routes"|t('app') %}
6-
{% set readOnly = not app.config.craft.general.allowAdminChanges %}
75

86
{% block actionButton %}
97
{% if not readOnly %}
@@ -15,8 +13,6 @@
1513
{ label: "Settings"|t('app'), url: url('settings') }
1614
] %}
1715

18-
{% do view.registerAssetBundle("craft\\web\\assets\\routes\\RoutesAsset") %}
19-
2016
{% do view.registerTranslations('app', [
2117
"Add a token",
2218
"Are you sure you want to delete this route?",
@@ -33,9 +29,6 @@
3329
"The URI can’t begin with the {setting} config setting.",
3430
]) %}
3531

36-
37-
{% set routes = craft.routes.getProjectConfigRoutes() %}
38-
3932
{% set actionMenuItems = [
4033
{
4134
icon: 'arrow-up',

routes/actions.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
use CraftCms\Cms\Http\Controllers\PluginStore\RemoveController;
2424
use CraftCms\Cms\Http\Controllers\Settings\EntryTypesController;
2525
use CraftCms\Cms\Http\Controllers\Settings\GeneralSettingsController;
26+
use CraftCms\Cms\Http\Controllers\Settings\RoutesController;
2627
use CraftCms\Cms\Http\Controllers\Settings\SectionsController;
2728
use CraftCms\Cms\Http\Controllers\Settings\SiteGroupsController;
2829
use CraftCms\Cms\Http\Controllers\Settings\SitesController;
@@ -189,6 +190,13 @@
189190
Route::post(BaseUpdaterController::ACTION_FINISH, [ConfigSyncController::class, 'finish']);
190191
});
191192

193+
// Routes
194+
Route::middleware([RequireAdminChanges::class])->group(function () {
195+
Route::post('routes/save-route', [RoutesController::class, 'store']);
196+
Route::post('routes/delete-route', [RoutesController::class, 'destroy']);
197+
Route::post('routes/update-route-order', [RoutesController::class, 'reorder']);
198+
});
199+
192200
// Sections
193201
Route::get('sections/table-data', [SectionsController::class, 'tableData']);
194202
Route::get('sections/edit/{sectionId?}', [SectionsController::class, 'edit']);

routes/cp.php

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
use CraftCms\Cms\Http\Controllers\PluginStore\PluginStoreController;
1313
use CraftCms\Cms\Http\Controllers\Settings\EntryTypesController;
1414
use CraftCms\Cms\Http\Controllers\Settings\GeneralSettingsController;
15+
use CraftCms\Cms\Http\Controllers\Settings\RoutesController;
1516
use CraftCms\Cms\Http\Controllers\Settings\SectionsController;
1617
use CraftCms\Cms\Http\Controllers\Settings\SitesController;
1718
use CraftCms\Cms\Http\Controllers\Updates\UpdaterController;
@@ -45,8 +46,8 @@
4546
Route::get('entries/{section}/new', CreateEntryController::class);
4647

4748
Route::get('content', EntriesIndexController::class);
48-
Route::view('content/{page}', 'craftcms::entries.index');
49-
Route::view('content/{page}/{sectionHandle}', 'craftcms::entries.index');
49+
Route::view('content/{page}', 'craftcms::entries.index')->where('page', '[^\/]+');
50+
Route::view('content/{page}/{sectionHandle}', 'craftcms::entries.index')->where('page', '[^\/]+');
5051
Route::get('content/{section}/new', CreateEntryController::class);
5152

5253
/**
@@ -73,6 +74,9 @@
7374
Route::get('settings/plugins/{handle}', [PluginsController::class, 'editSettings']);
7475
Route::get('plugin-store{any?}', [PluginStoreController::class, 'index'])->where('any', '.*');
7576

77+
// Routes
78+
Route::get('settings/routes', [RoutesController::class, 'index']);
79+
7680
// Sections
7781
Route::get('settings/sections', [SectionsController::class, 'index']);
7882
Route::middleware(RequireAdminChanges::class)->get('settings/sections/new', [SectionsController::class, 'create']);
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace CraftCms\Cms\Http\Controllers\Settings;
6+
7+
use craft\web\assets\routes\RoutesAsset;
8+
use CraftCms\Cms\Cms;
9+
use CraftCms\Cms\Http\RespondsWithFlash;
10+
use CraftCms\Cms\Route\Data\Route;
11+
use CraftCms\Cms\Route\Routes;
12+
use Illuminate\Contracts\View\View;
13+
use Illuminate\Http\Request;
14+
use Symfony\Component\HttpFoundation\Response;
15+
16+
final readonly class RoutesController
17+
{
18+
use RespondsWithFlash;
19+
20+
public function __construct(
21+
private Routes $routes,
22+
) {}
23+
24+
public function index(): View
25+
{
26+
\Craft::$app->getView()->registerAssetBundle(RoutesAsset::class);
27+
28+
return view('craftcms::settings.routes', [
29+
'tokens' => $this->routes->tokens,
30+
'routes' => $this->routes->getProjectConfigRoutes(),
31+
'readOnly' => ! Cms::config()->allowAdminChanges,
32+
]);
33+
}
34+
35+
public function store(Route $route): Response
36+
{
37+
$routeUid = $this->routes->saveRoute($route);
38+
39+
return $this->asSuccess(data: [
40+
'routeUid' => $routeUid,
41+
'siteUid' => $route->siteUid,
42+
]);
43+
}
44+
45+
public function destroy(Request $request): Response
46+
{
47+
$routeUid = $request->validate([
48+
'routeUid' => ['required', 'string'],
49+
])['routeUid'];
50+
51+
$this->routes->deleteRouteByUid($routeUid);
52+
53+
return $this->asSuccess();
54+
}
55+
56+
public function reorder(Request $request): Response
57+
{
58+
$routeUids = $request->validate([
59+
'routeUids' => ['required', 'array'],
60+
'routeUids.*' => ['required', 'string'],
61+
])['routeUids'];
62+
63+
$this->routes->updateRouteOrder($routeUids);
64+
65+
return $this->asSuccess();
66+
}
67+
}

src/Providers/AppServiceProvider.php

Lines changed: 0 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -4,36 +4,24 @@
44

55
namespace CraftCms\Cms\Providers;
66

7-
use Craft;
87
use craft\helpers\FileHelper;
98
use CraftCms\Aliases\Aliases;
109
use CraftCms\Cms\Cms;
1110
use CraftCms\Cms\Edition;
1211
use CraftCms\Cms\GarbageCollection\GarbageCollection;
13-
use CraftCms\Cms\Http\Middleware\CheckForUpdates;
14-
use CraftCms\Cms\Http\Middleware\CheckRequirements;
15-
use CraftCms\Cms\Http\Middleware\CheckSchemaVersion;
16-
use CraftCms\Cms\Http\Middleware\ExtractNamespace;
17-
use CraftCms\Cms\Http\Middleware\FlushProjectConfig;
18-
use CraftCms\Cms\Http\Middleware\HandleActionRequest;
19-
use CraftCms\Cms\Http\Middleware\RequireCpRequest;
20-
use CraftCms\Cms\Http\Middleware\SendPoweredByHeader;
21-
use CraftCms\Cms\Http\Middleware\UpdateLocale;
2212
use CraftCms\Cms\ProjectConfig\ProjectConfig;
2313
use CraftCms\Cms\Shared\Models\Info;
2414
use CraftCms\Cms\Support\Env;
2515
use CraftCms\Cms\Support\Facades\Updates;
2616
use CraftCms\Cms\User\Models\User;
2717
use GuzzleHttp\Utils;
2818
use Illuminate\Auth\Middleware\Authenticate;
29-
use Illuminate\Contracts\Http\Kernel as HttpKernel;
3019
use Illuminate\Foundation\Application;
3120
use Illuminate\Foundation\Console\AboutCommand;
3221
use Illuminate\Foundation\Events\LocaleUpdated;
3322
use Illuminate\Http\Client\Factory;
3423
use Illuminate\Http\Client\PendingRequest;
3524
use Illuminate\Http\Request;
36-
use Illuminate\Routing\Router;
3725
use Illuminate\Support\Collection;
3826
use Illuminate\Support\Facades\Config;
3927
use Illuminate\Support\Facades\Event;
@@ -65,17 +53,6 @@ public function register(): void
6553
Config::set('auth.providers.users.model', User::class);
6654
}
6755

68-
/**
69-
* HandleActionRequest is special and needs to run
70-
* before any other middleware as it rewrites
71-
* which path needs to get used.
72-
*/
73-
$kernel = $this->app->get(HttpKernel::class);
74-
$kernel->setGlobalMiddleware(array_merge([
75-
ExtractNamespace::class,
76-
HandleActionRequest::class,
77-
], $kernel->getGlobalMiddleware()));
78-
7956
Authenticate::redirectUsing(function () {
8057
if (\request()->isCpRequest()) {
8158
return Cms::config()->cpTrigger.'/login';
@@ -105,9 +82,6 @@ public function boot(): void
10582
$this->setTimezone();
10683
$this->setNamespace();
10784
$this->bootAliases();
108-
$this->bootMiddleware();
109-
110-
$this->loadRoutesFrom("{$this->root}/routes/routes.php");
11185

11286
$this->app->booted(function () {
11387
if (Info::isInstalled() && ! Updates::isCraftUpdatePending()) {
@@ -217,28 +191,6 @@ private function registerMacros(): void
217191
));
218192
}
219193

220-
protected function bootMiddleware(): void
221-
{
222-
$router = $this->app->make(Router::class);
223-
224-
collect([
225-
UpdateLocale::class,
226-
CheckSchemaVersion::class,
227-
CheckForUpdates::class,
228-
SendPoweredByHeader::class,
229-
FlushProjectConfig::class,
230-
])->each(fn ($middleware) => $router->pushMiddlewareToGroup('craft', $middleware));
231-
232-
collect([
233-
RequireCpRequest::class,
234-
CheckRequirements::class,
235-
])->each(fn ($middleware) => $router->pushMiddlewareToGroup('craft.cp', $middleware));
236-
237-
collect([
238-
'web',
239-
])->each(fn ($middleware) => $router->pushMiddlewareToGroup('craft.web', $middleware));
240-
}
241-
242194
private function setTimezone(): void
243195
{
244196
$timezone = app(ProjectConfig::class)->get('system.timeZone')

src/Providers/CraftServiceProvider.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
use CraftCms\Cms\License\LicenseServiceProvider;
1515
use CraftCms\Cms\Plugin\PluginServiceProvider;
1616
use CraftCms\Cms\ProjectConfig\ProjectConfigServiceProvider;
17+
use CraftCms\Cms\Route\RouteServiceProvider;
1718
use CraftCms\Cms\Section\SectionServiceProvider;
1819
use CraftCms\Cms\Structure\StructureServiceProvider;
1920
use CraftCms\Cms\Translation\TranslationServiceProvider;
@@ -33,6 +34,7 @@ final class CraftServiceProvider extends AggregateServiceProvider
3334
ProjectConfigServiceProvider::class,
3435
DeprecatorServiceProvider::class,
3536
LicenseServiceProvider::class,
37+
RouteServiceProvider::class,
3638
AppServiceProvider::class,
3739
IconServiceProvider::class,
3840
ConsoleServiceProvider::class,

src/Route/Data/Route.php

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace CraftCms\Cms\Route\Data;
6+
7+
use CraftCms\Cms\Support\Html;
8+
use Spatie\LaravelData\Dto;
9+
10+
final class Route extends Dto
11+
{
12+
public function __construct(
13+
/**
14+
* @var array $uriParts The URI as defined by the user. This is an array where each element is either a
15+
* string or an array containing the name of a subpattern and the subpattern
16+
*/
17+
public array $uriParts {
18+
get => array_filter($this->uriParts);
19+
set(array $value) => $this->uriParts = $value;
20+
},
21+
22+
/**
23+
* @var string $template The template to route matching requests to
24+
*/
25+
public string $template,
26+
27+
/**
28+
* @var string|null The site UID the route should be limited to, if any
29+
*/
30+
public ?string $siteUid,
31+
32+
/**
33+
* @var string $uid The route UID.
34+
*/
35+
public ?string $uid = null,
36+
37+
public ?int $sortOrder = null,
38+
) {
39+
}
40+
41+
public function configData(): array
42+
{
43+
return [
44+
'template' => $this->template,
45+
'uriParts' => $this->uriParts,
46+
'siteUid' => $this->siteUid,
47+
];
48+
}
49+
50+
public function getUri(): string
51+
{
52+
return collect($this->uriParts)->map(function (string|array|null $part) {
53+
if (is_string($part)) {
54+
return $part;
55+
}
56+
57+
return "{{$part[0]}}";
58+
})->implode('');
59+
}
60+
61+
public function uriDisplayHtml(): string
62+
{
63+
if (empty($this->uriParts)) {
64+
return '';
65+
}
66+
67+
$uriDisplayHtml = '';
68+
69+
foreach ($this->uriParts as $part) {
70+
if (is_string($part)) {
71+
$uriDisplayHtml .= Html::encode($part);
72+
73+
continue;
74+
}
75+
76+
$uriDisplayHtml .= Html::encodeParams(
77+
'<span class="token" data-name="{name}" data-value="{value}"><span>{name}</span></span>',
78+
[
79+
'name' => $part[0],
80+
'value' => $part[1],
81+
],
82+
);
83+
}
84+
85+
return $uriDisplayHtml;
86+
}
87+
}

src/Route/Events/DeletingRoute.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace CraftCms\Cms\Route\Events;
6+
7+
final class DeletingRoute extends RouteEvent {}

0 commit comments

Comments
 (0)