Livewire - Array as query string

I have an array property topic on a livewire component that I want to use as query string, the query string looks like this currently example.com/?topic[0]=livewire&topic[1]=laravel,

what I want the url to look like is example.com/?topic=livewire,laravel

currently I introduce a different property queryStringTopic and convert the topic array to string

public function updatedTopic()
{
   $this->queryStringTopic = implode(',',$this->topic);
 }

and convert it back to array in the mount method and assign it to the topic property.

Is there a better way to archive this.

selorm
selorm
0
7
193
alex
alex
Moderator

Hey Selorm. Is this method currently working for you?

Off the top of my head, I can't think of a better way to do this, but if you've having an issues with it, let me know and I'll play around locally with this!

selorm
selorm

Thanks, Alex, currently it works as intended but it gets a little messy when you have a lot of array properties you want to use as a query string, for every array query string you have to introduce a new property, a method to convert the array to comma separated string and an extra line or two in the mount method to convert back to an array.

class TopicList extends Component
{
    
    public array $topics = [];

    #[Url(as: 'topic', except: '')]
    public string $queryStringTopic = '';

    public function mount()
    {
        if (! empty($this->queryStringTopic)) {
            $this->topics = explode(',', $this->queryStringTopic);
        }
    }

    public function updatedTopics(): void
    {
        $this->queryStringTopic= implode(',', $this->topics);
    }

   
}


as you can see it becomes a little nuance if you have a lot of array properties you to use as query strings.

side note:

Alex, can you please place the markdown preview button below the input field (beside Markdown is supported) so I don't scroll to the top every time I want to preview

alex
alex
Moderator

Alex, can you please place the markdown preview button below the input field (beside Markdown is supported) so I don't scroll to the top every time I want to preview

Just wanted to let you know I've applied a max height to the textarea so you'll be able to access the markdown preview more easily now!

alex
alex
Moderator

Ah yes that makes sense. If you give me a little time, I'll figure out a way to handle this automatically and drop the code here.

Appreciate the side note about the markdown preview — we'll get that addressed!

alex
alex
Moderator
Solution

Ok, so I've spent a good hour on this figuring out the cleanest way to do this without overriding any Url attribute methods with extension, and without modifying anything else that could cause a Livewire upgrade to break things.

The bad news is you'll still have to define each array/query string separately within your component:

class TopicList extends Component
{
    use ImplodesUrlParams;

    protected function urlParamsToImplode()
    {
        return ['topics', 'difficulty'];
    }

    public array $topics = [];
    public array $difficulty = [];

    #[Url(as: 'topics', except: '')]
    public string $queryStringTopics = '';

    #[Url(as: 'difficulty', except: '')]
    public string $queryStringTopicsDifficulty = '';

   //...
}

However, the ImplodesUrlParams trait takes care of whatever you want to transform (returned by the urlParamsToImplode method).

trait ImplodesUrlParams
{
    public function mountImplodesUrlParams()
    {
        foreach ($this->urlParamsToImplode() as $param) {
            if (!empty($this->{'queryString' . ucfirst($param)})) {
                $this->{$param} = explode(',', $this->{'queryString' . ucfirst($param)});
            }
        }
    }

    public function dehydrateImplodesUrlParams()
    {
        foreach ($this->urlParamsToImplode() as $param) {
            $this->{'queryString' . ucfirst($param)} = implode(',', $this->{$param});
        }
    }
}

There may be an even cleaner way to do this, so I'll keep it in mind. But this has massively reduced the amount of duplication you need to do.

selorm
selorm

Thanks, Alex, This is much cleaner than defining individual methods.

alex
alex
Moderator

Glad it helps!