filamentphp / filament

A collection of beautiful full-stack components for Laravel. The perfect starting point for your next app. Using Livewire, Alpine.js and Tailwind CSS.
https://filamentphp.com
MIT License
19.14k stars 2.95k forks source link

TypeError Only arrays and Traversables can be unpacked #8533

Closed Pen-y-Fan closed 1 year ago

Pen-y-Fan commented 1 year ago

Package

filament/widgets

Package Version

v3.0.51

Laravel Version

v10.23.1

Livewire Version

v3.0.3

PHP Version

PHP v8.2.3

Problem description

I'm following the documentation for dispatching events from Widgets and getting the following error:

Only arrays and Traversables can be unpacked

Expected behavior

When dispatching events from Widgets the listener on the resource should be called with the data provided

Steps to reproduce

Create a Widget and add extra attributes. This is the current documentation:

protected function getStats(): array
{
    return [
        Stat::make('Processed', '192.1k')
            ->color('success')
            ->extraAttributes([
                'class' => 'cursor-pointer',
                'wire:click' => '$dispatch("setStatusFilter", "processed")',
            ]),
        // ...
    ];
}

The Livewire documentation has been updated, the "processed" value can not be a string, Livewire is expecting an array:

https://livewire.laravel.com/docs/events#dispatching-events-from-blade-templates

<button wire:click="$dispatch('show-post-modal', { id: {{ $post->id }} })">
    EditPost
</button>

Note the value is now a javascript object syntax wrapped in curly brackets {}.

The fix is to update the documentation. After a little experimenting this worked:

'wire:click' => sprintf("\$dispatch('setStatusFilter', { filter: '%s'})", OrderStatus::PROCESSED->getLabel()),

My preference is to escape the $ in the text, as the output in the browser is much cleaner and easier to read:

If the attribute is prepared from the current documentation '$dispatch("setStatusFilter", {filter: "processed"})' the output is escaped:

The output is escaped:

<div class="... cursor-pointer" wire:click="$dispatch(\" setstatusfilter,  {filter: \"Complete\"})">

This doesn't work!

I propose the domination be updated to the Livewire syntax, open the string with ", use ' inside the string, and escape the $.

'wire:click' => "\$dispatch('setStatusFilter', { filter: 'Complete'})"

The method of listening for events has also been updated in Livewire v3, it may be worth an extra example to demonstrate, e.g. this is a complete example of adding a Widget to the ListOrder Page:

<?php

declare(strict_types=1);

namespace App\Filament\Resources\OrderResource\Pages;

use App\Filament\Resources\OrderResource;
use Filament\Actions;
use Filament\Actions\CreateAction;
use Filament\Resources\Pages\ListRecords;
use Livewire\Attributes\On;

class ListOrders extends ListRecords
{
    protected static string $resource = OrderResource::class;

    #[On('setStatusFilter')]
    public function setStatusFilter(string $filter): void
    {
        $this->tableFilters[$filter]['isActive'] = true;
    }
    protected function getHeaderActions(): array
    {
        return [
            CreateAction::make(),
        ];
    }

    protected function getHeaderWidgets(): array
    {
        return [
            OrderResource\Widgets\OrdersOverview::class,
        ];
    }
}

I welcome your feedback and can make the time to update the documentation.

Reproduction repository

https://github.com/filamentphp/filament/blob/3.x/packages/widgets/

Relevant log output

[2023-09-16 07:24:02] local.ERROR: Only arrays and Traversables can be unpacked {"userId":2,"exception":"[object] (TypeError(code: 0): Only arrays and Traversables can be unpacked at F:\\laragon\\www\\stranding\\vendor\\livewire\\livewire\\src\\Features\\SupportEvents\\SupportEvents.php:29)
[stacktrace]
#0 F:\\laragon\\www\\stranding\\vendor\\livewire\\livewire\\src\\ComponentHook.php(41): Livewire\\Features\\SupportEvents\\SupportEvents->call('incrementPostCo...', 'processed', Object(Closure))
#1 F:\\laragon\\www\\stranding\\vendor\\livewire\\livewire\\src\\ComponentHookRegistry.php(108): Livewire\\ComponentHook->callCall('__dispatch', Array, Object(Closure))
#2 F:\\laragon\\www\\stranding\\vendor\\livewire\\livewire\\src\\ComponentHookRegistry.php(65): Livewire\\ComponentHookRegistry::Livewire\\{closure}('__dispatch', Array, Object(Closure))
#3 F:\\laragon\\www\\stranding\\vendor\\livewire\\livewire\\src\\EventBus.php(60): Livewire\\ComponentHookRegistry::Livewire\\{closure}(Object(App\\Filament\\Resources\\OrderResource\\Pages\\ListOrders), '__dispatch', Array, Object(Livewire\\Mechanisms\\HandleComponents\\ComponentContext), Object(Closure))
#4 F:\\laragon\\www\\stranding\\vendor\\livewire\\livewire\\src\\helpers.php(100): Livewire\\EventBus->trigger('call', Object(App\\Filament\\Resources\\OrderResource\\Pages\\ListOrders), '__dispatch', Array, Object(Livewire\\Mechanisms\\HandleComponents\\ComponentContext), Object(Closure))
#5 F:\\laragon\\www\\stranding\\vendor\\livewire\\livewire\\src\\Mechanisms\\HandleComponents\\HandleComponents.php(444): Livewire\\trigger('call', Object(App\\Filament\\Resources\\OrderResource\\Pages\\ListOrders), '__dispatch', Array, Object(Livewire\\Mechanisms\\HandleComponents\\ComponentContext), Object(Closure))
#6 F:\\laragon\\www\\stranding\\vendor\\livewire\\livewire\\src\\Mechanisms\\HandleComponents\\HandleComponents.php(108): Livewire\\Mechanisms\\HandleComponents\\HandleComponents->callMethods(Object(App\\Filament\\Resources\\OrderResource\\Pages\\ListOrders), Array, Object(Livewire\\Mechanisms\\HandleComponents\\ComponentContext))
#7 F:\\laragon\\www\\stranding\\vendor\\livewire\\livewire\\src\\LivewireManager.php(96): Livewire\\Mechanisms\\HandleComponents\\HandleComponents->update(Array, Array, Array)
#8 F:\\laragon\\www\\stranding\\vendor\\livewire\\livewire\\src\\Mechanisms\\HandleRequests\\HandleRequests.php(85): Livewire\\LivewireManager->update(Array, Array, Array)
#9 F:\\laragon\\www\\stranding\\vendor\\laravel\\framework\\src\\Illuminate\\Routing\\ControllerDispatcher.php(46): Livewire\\Mechanisms\\HandleRequests\\HandleRequests->handleUpdate()
#10 F:\\laragon\\www\\stranding\\vendor\\laravel\\framework\\src\\Illuminate\\Routing\\Route.php(259): Illuminate\\Routing\\ControllerDispatcher->dispatch(Object(Illuminate\\Routing\\Route), Object(Livewire\\Mechanisms\\HandleRequests\\HandleRequests), 'handleUpdate')
#11 F:\\laragon\\www\\stranding\\vendor\\laravel\\framework\\src\\Illuminate\\Routing\\Route.php(205): Illuminate\\Routing\\Route->runController()
#12 F:\\laragon\\www\\stranding\\vendor\\laravel\\framework\\src\\Illuminate\\Routing\\Router.php(799): Illuminate\\Routing\\Route->run()
#13 F:\\laragon\\www\\stranding\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php(141): Illuminate\\Routing\\Router->Illuminate\\Routing\\{closure}(Object(Illuminate\\Http\\Request))
#14 F:\\laragon\\www\\stranding\\vendor\\laravel\\framework\\src\\Illuminate\\Routing\\Middleware\\SubstituteBindings.php(50): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#15 F:\\laragon\\www\\stranding\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php(180): Illuminate\\Routing\\Middleware\\SubstituteBindings->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#16 F:\\laragon\\www\\stranding\\vendor\\laravel\\framework\\src\\Illuminate\\Foundation\\Http\\Middleware\\VerifyCsrfToken.php(78): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#17 F:\\laragon\\www\\stranding\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php(180): Illuminate\\Foundation\\Http\\Middleware\\VerifyCsrfToken->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#18 F:\\laragon\\www\\stranding\\vendor\\laravel\\framework\\src\\Illuminate\\View\\Middleware\\ShareErrorsFromSession.php(49): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#19 F:\\laragon\\www\\stranding\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php(180): Illuminate\\View\\Middleware\\ShareErrorsFromSession->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#20 F:\\laragon\\www\\stranding\\vendor\\laravel\\framework\\src\\Illuminate\\Session\\Middleware\\StartSession.php(121): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#21 F:\\laragon\\www\\stranding\\vendor\\laravel\\framework\\src\\Illuminate\\Session\\Middleware\\StartSession.php(64): Illuminate\\Session\\Middleware\\StartSession->handleStatefulRequest(Object(Illuminate\\Http\\Request), Object(Illuminate\\Session\\Store), Object(Closure))
#22 F:\\laragon\\www\\stranding\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php(180): Illuminate\\Session\\Middleware\\StartSession->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#23 F:\\laragon\\www\\stranding\\vendor\\laravel\\framework\\src\\Illuminate\\Cookie\\Middleware\\AddQueuedCookiesToResponse.php(37): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#24 F:\\laragon\\www\\stranding\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php(180): Illuminate\\Cookie\\Middleware\\AddQueuedCookiesToResponse->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#25 F:\\laragon\\www\\stranding\\vendor\\laravel\\framework\\src\\Illuminate\\Cookie\\Middleware\\EncryptCookies.php(67): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#26 F:\\laragon\\www\\stranding\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php(180): Illuminate\\Cookie\\Middleware\\EncryptCookies->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#27 F:\\laragon\\www\\stranding\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php(116): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#28 F:\\laragon\\www\\stranding\\vendor\\laravel\\framework\\src\\Illuminate\\Routing\\Router.php(798): Illuminate\\Pipeline\\Pipeline->then(Object(Closure))
#29 F:\\laragon\\www\\stranding\\vendor\\laravel\\framework\\src\\Illuminate\\Routing\\Router.php(777): Illuminate\\Routing\\Router->runRouteWithinStack(Object(Illuminate\\Routing\\Route), Object(Illuminate\\Http\\Request))
#30 F:\\laragon\\www\\stranding\\vendor\\laravel\\framework\\src\\Illuminate\\Routing\\Router.php(741): Illuminate\\Routing\\Router->runRoute(Object(Illuminate\\Http\\Request), Object(Illuminate\\Routing\\Route))
#31 F:\\laragon\\www\\stranding\\vendor\\laravel\\framework\\src\\Illuminate\\Routing\\Router.php(730): Illuminate\\Routing\\Router->dispatchToRoute(Object(Illuminate\\Http\\Request))
#32 F:\\laragon\\www\\stranding\\vendor\\laravel\\framework\\src\\Illuminate\\Foundation\\Http\\Kernel.php(200): Illuminate\\Routing\\Router->dispatch(Object(Illuminate\\Http\\Request))
#33 F:\\laragon\\www\\stranding\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php(141): Illuminate\\Foundation\\Http\\Kernel->Illuminate\\Foundation\\Http\\{closure}(Object(Illuminate\\Http\\Request))
#34 F:\\laragon\\www\\stranding\\vendor\\livewire\\livewire\\src\\Features\\SupportDisablingBackButtonCache\\DisableBackButtonCacheMiddleware.php(19): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#35 F:\\laragon\\www\\stranding\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php(180): Livewire\\Features\\SupportDisablingBackButtonCache\\DisableBackButtonCacheMiddleware->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#36 F:\\laragon\\www\\stranding\\vendor\\barryvdh\\laravel-debugbar\\src\\Middleware\\InjectDebugbar.php(66): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#37 F:\\laragon\\www\\stranding\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php(180): Barryvdh\\Debugbar\\Middleware\\InjectDebugbar->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#38 F:\\laragon\\www\\stranding\\vendor\\laravel\\framework\\src\\Illuminate\\Foundation\\Http\\Middleware\\ConvertEmptyStringsToNull.php(27): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#39 F:\\laragon\\www\\stranding\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php(180): Illuminate\\Foundation\\Http\\Middleware\\ConvertEmptyStringsToNull->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#40 F:\\laragon\\www\\stranding\\vendor\\laravel\\framework\\src\\Illuminate\\Foundation\\Http\\Middleware\\TrimStrings.php(36): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#41 F:\\laragon\\www\\stranding\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php(180): Illuminate\\Foundation\\Http\\Middleware\\TrimStrings->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#42 F:\\laragon\\www\\stranding\\vendor\\laravel\\framework\\src\\Illuminate\\Foundation\\Http\\Middleware\\ValidatePostSize.php(27): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#43 F:\\laragon\\www\\stranding\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php(180): Illuminate\\Foundation\\Http\\Middleware\\ValidatePostSize->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#44 F:\\laragon\\www\\stranding\\vendor\\laravel\\framework\\src\\Illuminate\\Foundation\\Http\\Middleware\\PreventRequestsDuringMaintenance.php(89): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#45 F:\\laragon\\www\\stranding\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php(180): Illuminate\\Foundation\\Http\\Middleware\\PreventRequestsDuringMaintenance->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#46 F:\\laragon\\www\\stranding\\vendor\\laravel\\framework\\src\\Illuminate\\Http\\Middleware\\HandleCors.php(49): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#47 F:\\laragon\\www\\stranding\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php(180): Illuminate\\Http\\Middleware\\HandleCors->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#48 F:\\laragon\\www\\stranding\\vendor\\laravel\\framework\\src\\Illuminate\\Http\\Middleware\\TrustProxies.php(39): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#49 F:\\laragon\\www\\stranding\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php(180): Illuminate\\Http\\Middleware\\TrustProxies->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#50 F:\\laragon\\www\\stranding\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php(116): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#51 F:\\laragon\\www\\stranding\\vendor\\laravel\\framework\\src\\Illuminate\\Foundation\\Http\\Kernel.php(175): Illuminate\\Pipeline\\Pipeline->then(Object(Closure))
#52 F:\\laragon\\www\\stranding\\vendor\\laravel\\framework\\src\\Illuminate\\Foundation\\Http\\Kernel.php(144): Illuminate\\Foundation\\Http\\Kernel->sendRequestThroughRouter(Object(Illuminate\\Http\\Request))
#53 F:\\laragon\\www\\stranding\\public\\index.php(53): Illuminate\\Foundation\\Http\\Kernel->handle(Object(Illuminate\\Http\\Request))
#54 {main}
"}
danharrin commented 1 year ago

You are correct, please fix the docs with the JSON instead.