diff --git a/CHANGELOG.md b/CHANGELOG.md index 3e1072b7988..eb4cc8597d3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## Unreleased +- Fixed some errors that could occur when running Craft through Laravel Octane ([#18921](https://github.com/craftcms/cms/pull/18921)) - Fixed an error that occurred when Updates were cached and deserialized. ## 6.0.0-alpha.4 - 2026-05-19 diff --git a/src/Http/Middleware/RequireCpRequest.php b/src/Http/Middleware/RequireCpRequest.php index 588576ce3bf..e5e50f83e82 100644 --- a/src/Http/Middleware/RequireCpRequest.php +++ b/src/Http/Middleware/RequireCpRequest.php @@ -5,6 +5,10 @@ namespace CraftCms\Cms\Http\Middleware; use Closure; +use CraftCms\Cms\Support\Facades\TemplateHooks; +use CraftCms\Cms\View\Hooks\PrepareElementIndexVariables; +use CraftCms\Cms\View\Hooks\PrepareElementSourcesVariables; +use CraftCms\Cms\View\Hooks\PrepareElementToolbarVariables; use CraftCms\Cms\View\TemplateMode; use Illuminate\Http\Request; @@ -18,6 +22,15 @@ public function handle(Request $request, Closure $next): mixed TemplateMode::set(TemplateMode::Cp); + $this->registerCpTemplateHooks(); + return $next($request); } + + public function registerCpTemplateHooks(): void + { + TemplateHooks::register('cp.layouts.elementindex', PrepareElementIndexVariables::class); + TemplateHooks::register('cp.elements.toolbar', PrepareElementToolbarVariables::class); + TemplateHooks::register('cp.elements.sources', PrepareElementSourcesVariables::class); + } } diff --git a/src/View/TemplateMode.php b/src/View/TemplateMode.php index 4ba607d62e0..df7f7bf97f7 100644 --- a/src/View/TemplateMode.php +++ b/src/View/TemplateMode.php @@ -9,6 +9,7 @@ use CraftCms\Cms\View\Events\CpTemplateRootsResolving; use CraftCms\Cms\View\Events\SiteTemplateRootsResolving; use Illuminate\Support\Facades\Context; +use Illuminate\View\FileViewFinder; enum TemplateMode: string { @@ -31,6 +32,29 @@ public static function get(): self public static function set(self $mode): void { Context::addHidden(self::class, $mode); + + if ($mode === self::Cp) { + /** + * Prepend the Craft CMS Control panel views when + * we're in CP Template mode. This makes view() + * work without a 'craftcms::' prefix. + */ + if (TemplateMode::is(TemplateMode::Cp)) { + $templates = dirname(__DIR__, 2).'/resources/templates'; + $views = dirname(__DIR__, 2).'/resources/views'; + + /** @var FileViewFinder $finder */ + $finder = view()->getFinder(); + + if (! in_array($templates, $finder->getPaths())) { + $finder->prependLocation($templates); + } + + if (! in_array($views, $finder->getPaths())) { + $finder->prependLocation($views); + } + } + } } public static function is(self $mode): bool diff --git a/src/View/ViewServiceProvider.php b/src/View/ViewServiceProvider.php index 0e7e584d066..a3c91352c83 100644 --- a/src/View/ViewServiceProvider.php +++ b/src/View/ViewServiceProvider.php @@ -5,9 +5,6 @@ namespace CraftCms\Cms\View; use CraftCms\Cms\View\Events\ViewAssetsRendering; -use CraftCms\Cms\View\Hooks\PrepareElementIndexVariables; -use CraftCms\Cms\View\Hooks\PrepareElementSourcesVariables; -use CraftCms\Cms\View\Hooks\PrepareElementToolbarVariables; use CraftCms\Cms\View\LegacyAssets\InternalAssetRegistry; use Illuminate\Contracts\View\Factory as ViewFactory; use Illuminate\Support\Facades\Event; @@ -33,7 +30,7 @@ public function register(): void ); } - public function boot(TemplateHooks $hooks): void + public function boot(): void { Event::listen(function (ViewAssetsRendering $event) { app(InternalAssetRegistry::class)->flush(); @@ -48,23 +45,6 @@ public function boot(TemplateHooks $hooks): void $this->registerTemplateRoots(); $this->registerTemplateGlobals(); - - $hooks->register('cp.layouts.elementindex', PrepareElementIndexVariables::class); - $hooks->register('cp.elements.toolbar', PrepareElementToolbarVariables::class); - $hooks->register('cp.elements.sources', PrepareElementSourcesVariables::class); - - $this->app->booted(function () { - /** - * This ensures that when Laravel tries to find an error view, - * it will look in the CP templates for it as well. - */ - if (request()->isCpRequest()) { - config()->set('view.paths', array_merge( - config('view.paths'), - [dirname(__DIR__, 2).'/resources/templates'] - )); - } - }); } private function registerTemplateGlobals(): void @@ -82,16 +62,6 @@ private function registerTemplateRoots(): void /** @var Factory $factory */ $factory = $this->app->make(ViewFactory::class); - /** - * Prepend the Craft CMS Control panel views when - * we're in CP Template mode. This makes view() - * work without a 'craftcms::' prefix. - */ - if (TemplateMode::is(TemplateMode::Cp)) { - $factory->prependLocation("{$this->root}/resources/templates"); - $factory->prependLocation("{$this->root}/resources/views"); - } - foreach (TemplateMode::get()->templateRoots() as $namespace => $roots) { $factory->addNamespace($namespace, $roots); diff --git a/tests/Feature/Cp/ElementIndexHtmlTest.php b/tests/Feature/Cp/ElementIndexHtmlTest.php index 404a7b7efe7..781ea8c3461 100644 --- a/tests/Feature/Cp/ElementIndexHtmlTest.php +++ b/tests/Feature/Cp/ElementIndexHtmlTest.php @@ -4,12 +4,15 @@ use CraftCms\Cms\Cp\Html\ElementIndexHtml; use CraftCms\Cms\Entry\Elements\Entry; +use CraftCms\Cms\Http\Middleware\RequireCpRequest; use CraftCms\Cms\User\Elements\User; use function Pest\Laravel\actingAs; beforeEach(function () { actingAs(User::findOne()); + + app(RequireCpRequest::class)->registerCpTemplateHooks(); }); it('renders an element index shell with toolbar and elements container', function () { diff --git a/yii2-adapter/src/Http/LegacyMiddleware.php b/yii2-adapter/src/Http/LegacyMiddleware.php index 04606a26e06..8d73fcefcec 100644 --- a/yii2-adapter/src/Http/LegacyMiddleware.php +++ b/yii2-adapter/src/Http/LegacyMiddleware.php @@ -53,6 +53,8 @@ public function handle(Request $request, Closure $next): mixed $this->restoreEmptyStrings($request); try { + $this->ensureCraftApp(); + /** @var \craft\web\Request $yiiRequest */ $yiiRequest = Craft::createObject(App::webRequestConfig()); $yiiRequest->csrfCookie = Craft::cookieConfig([], $yiiRequest); @@ -128,6 +130,19 @@ public static function cleanup(): void }); } + private function ensureCraftApp(): void + { + if (class_exists(Craft::class, false) && Craft::$app) { + return; + } + + $craftApp = $this->app->make('Craft'); + + if (!Craft::$app) { + Craft::$app = $craftApp; + } + } + private function restoreEmptyStrings(Request $request): void { $parameters = $request->isJson() diff --git a/yii2-adapter/src/Http/PrepareLegacyCraftApp.php b/yii2-adapter/src/Http/PrepareLegacyCraftApp.php new file mode 100644 index 00000000000..50dc65a1505 --- /dev/null +++ b/yii2-adapter/src/Http/PrepareLegacyCraftApp.php @@ -0,0 +1,53 @@ +restoreCraftApp(); + $this->refreshRequestScopedComponents($request); + + return $next($request); + } + + private function restoreCraftApp(): void + { + if (class_exists(Craft::class, false) && Craft::$app) { + return; + } + + $craftApp = $this->app->make('Craft'); + + if (!Craft::$app) { + Craft::$app = $craftApp; + } + } + + private function refreshRequestScopedComponents(Request $request): void + { + $this->app->instance('request', $request); + + /** @var \craft\web\Request $yiiRequest */ + $yiiRequest = Craft::createObject(App::webRequestConfig()); + $yiiRequest->csrfCookie = Craft::cookieConfig([], $yiiRequest); + + Craft::$app->set('request', $yiiRequest); + Craft::$app->set('view', Craft::createObject(App::viewConfig())); + Craft::$app->set('user', Craft::createObject(App::userConfig())); + } +} diff --git a/yii2-adapter/src/Yii2ServiceProvider.php b/yii2-adapter/src/Yii2ServiceProvider.php index c1ff190021f..fcb9ac83d34 100644 --- a/yii2-adapter/src/Yii2ServiceProvider.php +++ b/yii2-adapter/src/Yii2ServiceProvider.php @@ -28,12 +28,14 @@ use CraftCms\Yii2Adapter\HtmlPurifier\LegacyHtmlPurifierConfigRegistrar; use CraftCms\Yii2Adapter\Http\CaptureOriginalActionRequestUri; use CraftCms\Yii2Adapter\Http\LegacyMiddleware; +use CraftCms\Yii2Adapter\Http\PrepareLegacyCraftApp; use CraftCms\Yii2Adapter\I18N\I18NCompatibility; use CraftCms\Yii2Adapter\Mail\TestToEmailAddressCompatibility; use CraftCms\Yii2Adapter\Mixins\CraftVariableMixin; use Illuminate\Contracts\Debug\ExceptionHandler; use Illuminate\Contracts\Http\Kernel as HttpKernel; use Illuminate\Foundation\Exceptions\Handler; +use Illuminate\Routing\Router; use Illuminate\Support\Facades\Artisan; use Illuminate\Support\Facades\Config; use Illuminate\Support\Facades\Event; @@ -182,6 +184,7 @@ private function toLegacyException(Throwable $exception): Throwable public function boot(): void { $this->app->make(HttpKernel::class)->prependMiddleware(CaptureOriginalActionRequestUri::class); + $this->app->make(Router::class)->pushMiddlewareToGroup('craft', PrepareLegacyCraftApp::class); $this->commands([ AddCategoriesSupportCommand::class,