masmerise / livewire-toaster

Beautiful toast notifications for Laravel / Livewire.
MIT License
342 stars 22 forks source link

Support for Laravel Octane #21

Closed idealerror closed 9 months ago

idealerror commented 10 months ago

Laravel version: 10.22.0 PHP version: 8.2 Livewire version 3.0 livewire-toaster version: 2.0.1

Is there any plans to support Laravel Octane with this package? In my dev environment, this package works fine as I'm not utilizing Octane. In production, Toaster messages only show up after a page has been refreshed/redirected. I believe this is due to how Octane does not allow modifications to the service container after it's been initialized.

// Will only show if user loads a new page
Toaster::success("Updated {$this->item->name}!");

// Will show immediately, as designed
$this->dispatch('toaster:received', duration: 3000, message: "Updated {$this->item->name}!", type: 'success');

When inspecting the response from Livewire, the toaster:received dispatch is missing when using Toaster::success() with Laravel Octane.

mabdullahsari commented 10 months ago

Please check out this issue and tell me whether you are still experiencing problems. I didn't receive any negative feedback so I assumed it was fixed.

idealerror commented 10 months ago

I tested adding the class to the octane warm configuration and that did not work.

If I find some free time, I'll fork the package and add specific Octane support. For now, I'll just use the manual dispatching.

mabdullahsari commented 10 months ago

I tested adding the class to the octane warm configuration and that did not work.

If I find some free time, I'll fork the package and add specific Octane support. For now, I'll just use the manual dispatching.

Thanks! That would be very helpful as I don't use Octane myself.

yehorherasymchuk commented 9 months ago

I had the same problem. Messages were not shown after livewire update request, only after page reload or redirect. Only with Laravel Octane. I found and solved the problem. Waiting for PR to be merged :)

Laravel version: 10.23 Livewire version: 3.0.5 livewire-toaster version: 2.01 And everything is in AWS Lambdas with Vapor :)

The issue is in ToastServiceProvifder while registering listener of dehydrate event with LivewireRelay. Collector used in Listener is empty. Below I describe why.

From the laravel doc

Since Octane boots your application once and keeps it in memory while serving requests, there are a few caveats you should consider while building your application. For example, the register and boot methods of your application's service providers will only be executed once when the request worker initially boots. On subsequent requests, the same application instance will be reused.

We have registered Collector as singleton in ToasterServiceProvider::register

$this->app->scoped(Collector::class, QueuingCollector::class);

And event listener was registered in ToasterServiceProvider::boot

    private function relayToLivewire(): void
    {
        $this->app[LivewireManager::class]->listen('dehydrate', $this->app[LivewireRelay::class]);
    }

And LivewireRelay has dependencies in constructor, and with that injected instances it would resolve each event.

public function __construct(
        private DataStore $store,
        private LivewireManager $livewire,
        private Collector $toasts,
    ) {}

So after you load your page first time, private Collector $toasts is injected on each following request the same instance first instance. However app will boot a new singleton instance of Collector for each request. That is why when livewire update request is called and dehydrate event is triggered our Collector $toasts->release() is empty (it uses first bind singleton instance from the first request).

To fix this issue we need to use the same Collector instance on each request. From the Laravel Octane doc

As a work-around, you could either stop registering the binding as a singleton, or you could inject a container resolver closure into the service that always resolves the current container instance

And I fix it by adding this in boot method of AppServiceProvider

    public function boot(): void
    {
        // your code
        $this->callAfterResolving(Collector::class, $this->relayToLivewire(...));
    }

    private function relayToLivewire(): void
    {
        $this->app[LivewireManager::class]->listen('dehydrate', function (...$params) {
            app(LivewireRelay::class)->__invoke(...$params);
        });
    }

And also created PR with fix

Thank you guys, Hope this will help you too.

mabdullahsari commented 9 months ago

This should be fixed with 2.0.2!

Please give it a try.