statamic / cms

The core Laravel CMS Composer package
https://statamic.com
Other
4.08k stars 533 forks source link

Error when filtering collection for multiple ids #7484

Closed wm-simon closed 1 year ago

wm-simon commented 1 year ago

Bug description

I've tried to filter a references collection with livewire. I get this error: foreach() argument must be of type array|object, null given

How to reproduce

The references entries have a field with related services entries:

selected:
  - 87fd29f0-dff9-4aad-b8a6-35fba2de3530
  - be6439fd-ed8e-4928-8ada-69cecc670a8b
  - e79d1c7b-913f-49f4-ab2e-e3d603856fca

From livewire I get the selected services:

selected:services
  - be6439fd-ed8e-4928-8ada-69cecc670a8b
  - e79d1c7b-913f-49f4-ab2e-e3d603856fca

Now I want to show the entries with this tag: {{ collection from="references" not_listed:is="false" taxonomy:branchen="{selected:branches}" services:in="{selected:services|option_list}" }}

I've tried multiple versions, but nothing worked:

{{ collection from="references" services:in="{selected:services}" }}
{{ collection from="references" services:in="{selected:services|option_list}" }}
{{ collection from="references" services:contains="{selected:services}" }}
{{ collection from="references" services:contains="{selected:services|option_list}" }}

This error is shown: foreach() argument must be of type array|object, null given

I think this should work, like it is explained in the docs: https://statamic.dev/conditions#passing-multiple-values

The filter for branches with taxonomies working fine without an error.

Logs

No response

Environment

Environment
Application Name: test
Laravel Version: 9.48.0
PHP Version: 8.0.12
Composer Version: 2.1.3
Environment: local
Debug Mode: ENABLED
URL: domain.tld
Maintenance Mode: OFF

Cache
Config: NOT CACHED
Events: NOT CACHED
Routes: NOT CACHED
Views: CACHED

Drivers
Broadcasting: log
Cache: statamic
Database: mysql
Logs: stack / single
Mail: smtp
Queue: sync
Session: file

Statamic
Addons: 10
Antlers: runtime
Stache Watcher: Enabled
Static Caching: Disabled
Version: 3.3.67 PRO

Statamic Addons
anakadote/statamic-recaptcha: 1.0.8
aryehraber/statamic-logbook: 2.1.0
doublethreedigital/duplicator: 2.3.4
heidkaemper/statamic-toolbar: 1.0.3
jezzdk/statamic-google-maps: 1.2.1
jonassiewertsen/statamic-live-search: 1.6.0
jonassiewertsen/statamic-livewire: 2.11.0
weareframework/pinpoint-image: 0.0.10

Installation

Fresh statamic/statamic site via CLI

Antlers Parser

runtime (new)

Additional details

No response

jasonvarga commented 1 year ago

Can you better explain what selected:services is supposed to be?

I don't understand this:

From livewire I get the selected services:

selected:services
  - be6439fd-ed8e-4928-8ada-69cecc670a8b
  - e79d1c7b-913f-49f4-ab2e-e3d603856fca
wm-simon commented 1 year ago

It's a nested array:

selected
  - services  // collection entries
      - be6439fd-ed8e-4928-8ada-69cecc670a8b
      - e79d1c7b-913f-49f4-ab2e-e3d603856fca
  - branches:  // taxonomy entries
    - gastronomie

I've tried it with a not nested array selected_services as well, but there was the same error.

selected_services
  - be6439fd-ed8e-4928-8ada-69cecc670a8b
  - e79d1c7b-913f-49f4-ab2e-e3d603856fca
jasonvarga commented 1 year ago

What you're looking for is an intersect, which is supported on the query builder level with whereJsonContains. However there's currently not a collection tag parameter equivalent.

You can use a custom query scope at the moment. Here's one:

<?php

namespace App\Scopes;

use Statamic\Query\Scopes\Scope;

class Intersect extends Scope
{
    public function apply($query, $values)
    {
        $query->whereJsonContains(
            $values->get('intersect_field'),
            $values->explode('intersect_value'),
        );
    }
}

Put that in app/Scopes/Intersect.php then use it like this:

{{ collection from="references" query_scope="intersect" intersect_field="services" :intersect_value="selected" }}
  ...
{{ /collection }}

Notice that :intersect_value has a : prefix.

wm-simon commented 1 year ago

Thanks for your help. I'm not sure, if I'm doing right.

That's my tag right now:

{{ collection 
    from="references" 
    not_listed:is="false" 
    query_scope="intersect" 
    intersect_field="services"
    intersect_value="{selected:services}" 
    taxonomy:branchen="{selected:branches}" 
    sort="{sort}" 
}}

But now there are no entries shown anymore, when selected:services is empty. When I select a service, there is shown this error:

explode(): Argument #2 ($string) must be of type string, array given

If it helps, I use the checkboxes like it is explained here: https://laravel-livewire.com/screencasts/form-checkboxes

Thats's my code:

Generates the checkpoints:

{{ collection:leistungen }}    
        <input type="checkbox" value="{{ id }}" wire:model="selected.services">
{{ /collection:leistungen }}

Livewire component:

<?php
namespace App\Http\Livewire;
use Livewire\Component;
class FilterCollection extends Component
{
    public $selected = [
        'services' => [],
        'branches' => [],
    ];
    public $sort = "order:desc";
    public function render()
    {
        return view('livewire.filter-collection');
    }
}
wm-simon commented 1 year ago

Hey @jasonvarga , I'm not sure, if you get a notification, when the issue was closed before. Is it possible for you to take a look on it again?

edalzell commented 1 year ago

Looks like the scope is expecting a string but the data is an array. Try changing the scope to not explode (that expects a string and turns it into an array), as you already have an array.

wm-simon commented 1 year ago

Hey, could we check this again, perhaps I'm missing something.

I have 2 arrays with entry ids. With them I want to check, if both arrays contain the same ids.

I've added to the intersect.

        echo '<pre>';
        var_dump($values->get('intersect_field'));
        var_dump($values->get('intersect_value'));
        echo '</pre>';

This is the output:

string(8) "services"
array(3) {
  ["services"]=&gt;
  array(0) {
  }
  ["branches"]=&gt;
  array(0) {
  }
  ["date"]=&gt;
  string(0) ""
}

But string(8) "services" should be the ids from the the services field inside the collection entries, right?

For testing I changed the intersect to this:

            $values->get('intersect_field'),
            $values->get('intersect_value'),

If there is nothing selected, there are no entries shown. When I select a service, it loads endless.