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

Laravel Nova on Laravel 11 and Spatie Once #6282

Closed
martio opened this issue Mar 17, 2024 · 3 comments
Closed

Laravel Nova on Laravel 11 and Spatie Once #6282

martio opened this issue Mar 17, 2024 · 3 comments
Labels
bug Verified bug by the Nova team fix incoming A fix is in review

Comments

@martio
Copy link

martio commented Mar 17, 2024

  • Laravel Version: 11.0.7
  • Nova Version: 4.33.0
  • PHP Version: 8.3.3

Description:

I have a problem with Laravel Nova using the Laravel 11 framework.

Error: Class "Spatie\Once\Cache" not found in /var/www/html/vendor/laravel/nova/src/NovaCoreServiceProvider.php:102
Stack trace:
#0 /var/www/html/vendor/laravel/framework/src/Illuminate/Events/Dispatcher.php(458): Laravel\Nova\NovaCoreServiceProvider->Laravel\Nova\{closure}()
#1 /var/www/html/vendor/laravel/framework/src/Illuminate/Events/Dispatcher.php(286): Illuminate\Events\Dispatcher->Illuminate\Events\{closure}()
#2 /var/www/html/vendor/laravel/framework/src/Illuminate/Events/Dispatcher.php(266): Illuminate\Events\Dispatcher->invokeListeners()
#3 /var/www/html/vendor/laravel/octane/src/DispatchesEvents.php(18): Illuminate\Events\Dispatcher->dispatch()
#4 /var/www/html/vendor/laravel/octane/src/ApplicationGateway.php(30): Laravel\Octane\ApplicationGateway->dispatchEvent()
#5 /var/www/html/vendor/laravel/octane/src/Worker.php(84): Laravel\Octane\ApplicationGateway->handle()
#6 /var/www/html/vendor/laravel/octane/bin/swoole-server(120): Laravel\Octane\Worker->handle()
#7 [internal function]: {closure}()
#8 /var/www/html/vendor/laravel/octane/bin/swoole-server(170): Swoole\Server->start()
#9 {main}

Composer does not install the "spatie/once" package. Hence, the "Spatie\Once\Cache" class is missing.

Laravel 11 now provides its own once function to ensure that a given closure is only executed once.

Detailed steps to reproduce the issue on a fresh Nova installation:

Install Laravel Nova on Laravel 11.

@codebarista
Copy link

This is probably a duplicate of #6278. Do you have laravel/octane running? Unfortunately, I don't think there is a workaround for this yet.

@martio
Copy link
Author

martio commented Mar 17, 2024

The issue is with the duplication of the "once" function in Laravel 11. You should replace Cache::getInstance()->flush(); with Once::flush();.

https://github.com/laravel/framework/pull/49744/files

The workaround is to copy the provider and correct this line of code:

composer.json

    "extra": {
        "laravel": {
            "dont-discover": [
                "laravel/nova"
            ]
        }
    },

app/Providers/NovaCoreServiceProvider.php

<?php

namespace App\Providers;

use Illuminate\Auth\Events\Attempting;
use Illuminate\Auth\Events\Logout;
use Illuminate\Container\Container;
use Illuminate\Contracts\Http\Kernel as HttpKernel;
use Illuminate\Foundation\Http\Events\RequestHandled;
use Illuminate\Routing\Middleware\SubstituteBindings;
use Illuminate\Support\Facades\Route;
use Illuminate\Support\Once;
use Illuminate\Support\ServiceProvider;
use Laravel\Nova\Auth\Adapters\SessionImpersonator;
use Laravel\Nova\Contracts\ImpersonatesUsers;
use Laravel\Nova\Contracts\QueryBuilder;
use Laravel\Nova\Events\ServingNova;
use Laravel\Nova\Http\Middleware\ServeNova;
use Laravel\Nova\Http\Requests\NovaRequest;
use Laravel\Nova\Listeners\BootNova;
use Laravel\Nova\Query\Builder;
use Laravel\Octane\Events\RequestReceived;
use Laravel\Nova\Nova;
use Laravel\Nova\NovaServiceProvider as BaseNovaServiceProvider;

/**
* The primary purpose of this service provider is to push the ServeNova
* middleware onto the middleware stack so we only need to register a
* minimum number of resources for all other incoming app requests.
*/
class NovaCoreServiceProvider extends ServiceProvider
{
   /**
    * Bootstrap any package services.
    *
    * @return void
    */
   public function boot()
   {
       Nova::booted(BootNova::class);

       if ($this->app->runningInConsole()) {
           $this->app->register(BaseNovaServiceProvider::class);
       }

       if (! $this->app->configurationIsCached()) {
           $this->mergeConfigFrom(__DIR__.'/../../config/nova.php', 'nova');
       }

       Route::middlewareGroup('nova', config('nova.middleware', []));
       Route::middlewareGroup('nova:api', config('nova.api_middleware', []));

       $this->app->make(HttpKernel::class)
                   ->pushMiddleware(ServeNova::class);

       $this->app->afterResolving(NovaRequest::class, function ($request, $app) {
           if (! $app->bound(NovaRequest::class)) {
               $app->instance(NovaRequest::class, $request);
           }
       });

       $this->registerEvents();
       $this->registerResources();
       $this->registerJsonVariables();
   }

   /**
    * Register any application services.
    *
    * @return void
    */
   public function register()
   {
       if (! defined('NOVA_PATH')) {
           define('NOVA_PATH', realpath(__DIR__.'/../../'));
       }

       $this->app->singleton(ImpersonatesUsers::class, SessionImpersonator::class);

       $this->app->bind(QueryBuilder::class, function ($app, $parameters) {
           return new Builder(...$parameters);
       });
   }

   /**
    * Register the package events.
    *
    * @return void
    */
   protected function registerEvents()
   {
       tap($this->app['events'], function ($event) {
           $event->listen(Attempting::class, function () {
               app(ImpersonatesUsers::class)->flushImpersonationData(request());
           });

           $event->listen(Logout::class, function () {
               app(ImpersonatesUsers::class)->flushImpersonationData(request());
           });

           $event->listen(RequestReceived::class, function ($event) {
               Nova::flushState();
               Once::flush();

               $event->sandbox->forgetInstance(ImpersonatesUsers::class);
           });

           $event->listen(RequestHandled::class, function ($event) {
               Container::getInstance()->forgetInstance(NovaRequest::class);
           });
       });
   }

   /**
    * Register the package resources such as routes, templates, etc.
    *
    * @return void
    */
   protected function registerResources()
   {
       $this->loadViewsFrom(__DIR__.'/../../resources/views', 'nova');
       $this->loadTranslationsFrom(__DIR__.'/../../resources/lang', 'nova');

       if (Nova::runsMigrations()) {
           $this->loadMigrationsFrom(__DIR__.'/../../database/migrations');
       }

       $this->registerRoutes();
   }

   /**
    * Register the package routes.
    *
    * @return void
    */
   protected function registerRoutes()
   {
       Route::group($this->routeConfiguration(), function () {
           $this->loadRoutesFrom(__DIR__.'/../../routes/api.php');
       });
   }

   /**
    * Get the Nova route group configuration array.
    *
    * @return array{domain: string|null, as: string, prefix: string, middleware: string}
    */
   protected function routeConfiguration()
   {
       return [
           'domain' => config('nova.domain', null),
           'as' => 'nova.api.',
           'prefix' => 'nova-api',
           'middleware' => 'nova:api',
           'excluded_middleware' => [SubstituteBindings::class],
       ];
   }

   /**
    * Register the Nova JSON variables.
    *
    * @return void
    */
   protected function registerJsonVariables()
   {
       Nova::serving(function (ServingNova $event) {
           // Load the default Nova translations.
           Nova::translations(
               lang_path('vendor/nova/'.app()->getLocale().'.json')
           );

           Nova::provideToScript([
               'appName' => Nova::name() ?? config('app.name', 'Laravel Nova'),
               'timezone' => config('app.timezone', 'UTC'),
               'translations' => function () {
                   return Nova::allTranslations();
               },
               'userTimezone' => function ($request) {
                   return Nova::resolveUserTimezone($request);
               },
               'pagination' => config('nova.pagination', 'links'),
               'locale' => config('app.locale', 'en'),
               'algoliaAppId' => config('services.algolia.appId'),
               'algoliaApiKey' => config('services.algolia.apiKey'),
               'version' => Nova::version(),
           ]);
       });
   }
}

bootstrap/providers.php

<?php

return [
   App\Providers\NovaCoreServiceProvider::class,
   App\Providers\NovaServiceProvider::class,
];

@crynobone crynobone added bug Verified bug by the Nova team fix incoming A fix is in review labels Mar 18, 2024
Copy link

This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Mar 24, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
bug Verified bug by the Nova team fix incoming A fix is in review
Projects
None yet
Development

No branches or pull requests

3 participants