masmerise / livewire-toaster

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

Add Octane support #23

Closed yehorherasymchuk closed 9 months ago

yehorherasymchuk commented 9 months ago

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);
        });
    }

Thank you guys, Hope this will help others too.

mabdullahsari commented 9 months ago

Hey @yehorherasymchuk 👋

Thank you for the due diligence. I went ahead and converted the DI cases to use SSL. Could you please verify if it's still working for you?

Thanks!

mabdullahsari commented 9 months ago

This should also close #21.

yehorherasymchuk commented 9 months ago

Hi @mabdullahsari !

Thank you for a quick review. Im not sure that ssl will work correctly with octane. and there is not an easy way to check it on production(

mabdullahsari commented 9 months ago

Hi @mabdullahsari !

Thank you for a quick review. Im not sure that ssl will work correctly with octane. and there is not an easy way to check it on production(

Hi, I'm pretty confident this is going to work so let's try it.