code16 / sharp

Laravel 10+ Content management framework
https://sharp.code16.fr/
MIT License
727 stars 73 forks source link

Filter Usage leads to List not displaying and error in console #245

Closed b0rt closed 4 years ago

b0rt commented 4 years ago

Hi,

i have dificulties using a filter and am not sure what i am doing wrong or if it is possibly related to a bug..

when accessing a list with a filter value e.g.

http:/domain.tld/admin/list/organizations?filter_user=1&page=1

the resultset is empty and there is error in the console

2020-04-11 17_35_47-

another observation is data that is retrieved by the URL

http://domain.tld/admin/api/list/organizations?filter_user=1&page=1

seems to be alright and is containing my expected resultset containing 1 organization

2020-04-11 17_37_40-Kita Portal Nord _ Sharp 4 2 6 - Brave

I do think i did everything correct accoring do the docs at https://sharp.code16.fr/docs/guide/filters.html#configure-the-filter

here is some further context

laravel/framework = v6.18.6
code16/sharp = 4.2.6 (new assets copied is confirmed by me)
BrowserTested = [
'Brave Version 1.5.123 Chromium: 80.0.3987.163 (Official Build) (64-bit)', 
'Chromium Version 80.0.3987.163 (Offizieller Build) (64-Bit)']

app\Sharp\OrganizationList.php

class OrganizationList extends SharpEntityList
{
    /**
     * Build list containers using ->addDataContainer()
     *
     * @return void
     */
    public function buildListDataContainers()
    {
        $this->
        addDataContainer(
            EntityListDataContainer::make('title')->setLabel('Titel')->setHtml()
        )->addDataContainer(
        EntityListDataContainer::make('user_id')->setLabel('Nutzer ID')->setHtml()
    );
    }

    /**
     * Build list layout using ->addColumn()
     *
     * @return void
     */
    public function buildListLayout()
    {
        $this->addColumn('title', 5);
        $this->addColumn('user_id', 1);
    }

    /**
     * Build list config
     *
     * @return void
     */
    public function buildListConfig()
    {
        $this->setInstanceIdAttribute('id')
            ->addFilter("user", OrganizationByUserFilter::class)
            ->setPaginated();
    }

    /**
     * Retrieve all rows data as array.
     *
     * @param EntityListQueryParams $params
     * @return array
     */
    public function getListData(EntityListQueryParams $params)
    {
        $entries = Organization::all();
        if($params->filterFor("user")) {
            $entries = $entries->where("user_id", $params->filterFor("user"));
        }
        return $this
            ->transform(
                $entries
            );
    }
}

app/Sharp/Filter/OrganizationByUserFilter.php

namespace App\Sharp\Filter;

use App\Models\Organization;
use Code16\Sharp\EntityList\EntityListFilter;

class OrganizationByUserFilter implements EntityListFilter
{
    /**
     * @return array
     */
    public function values()
    {
        return Organization::orderBy("user_id")
            ->pluck("user_id", "user_id");
    }

    public function defaultValue()
    {
        return ['default', 1];
    }

    public function label()
    {
        return "User ID";
    }

    public function retainValueInSession()
    {
        return false;
    }

}

I am wondering what i am doing wrong .. my investigation of the error message led to

sharp\resources\assets\js\components\list\EntityList.vue

and the computed property hasActionColumn()

hasActionsColumn() {
                return this.items.some(instance =>
                    this.instanceHasState(instance) ||
                    this.instanceHasCommands(instance)
                );
            },

sofar.. thank you for any hints or for maybe taking the time to confirm this

dvlpp commented 4 years ago

I spotted a mistake in you code: OrganizationByUserFilter ::defaultValue() should return a simple id, and not an array, which must be present in the values() dataset. And in fact, since it's not a RequiredFilter but a simple Filter, you should remove this function entirely.

Could you do that and, and if the issue is still there post the whole content of the config.filters key og the json returned by you GET request?

b0rt commented 4 years ago

I removed defaultValue() but the issue persists thanks for pointing that out. this is the config.filters key of the json that is being returned

"config": {
    "instanceIdAttribute": "id",
    "multiformAttribute": null,
    "searchable": false,
    "paginated": true,
    "reorderable": false,
    "defaultSort": null,
    "defaultSortDir": null,
    "hasShowPage": false,
    "filters": [
      {
        "key": "user",
        "default": null,
        "label": "User ID",
        "type": "select",
        "multiple": false,
        "required": false,
        "values": [
          {
            "id": 1,
            "label": 1
          },
          {
            "id": 2,
            "label": 2
          },
         // abbrevieated to be more readable 
        ],
        "master": false,
        "searchable": false,
        "searchKeys": [
          "label"
        ],
        "template": "{{label}}"
      }
  ]
  },
b0rt commented 4 years ago

Observation: if i do not use a filter the GET response contains data.items as an array containing objects

 "data": {
    "items": [
      {
        "title": "Organization 1",
        "user_id": 3,
        "id": 1
      },
      {
        "title": "Organzation 2",
        "user_id": 1,
        "id": 2
      }]
}

and with the filter it returns

"data": {
    "items": {
      "0": {
        "title": "Org 1",
        "user_id": 3,
        "id": 1
      },
      "3": {
        "title": "Org 2",
        "user_id": 3,
        "id": 4
      }
    }
  },

so i guess my error lies in getListData() of OrganizationList which is not generating the desired / expected Collection, i assume now

dvlpp commented 4 years ago

Yes, items should be an array. Change your values() method to this (since pluck returns a Collection):

   /**
     * @return array
     */
    public function values()
    {
        return Organization::orderBy("user_id")
            ->pluck("user_id", "user_id")
            ->toArray();
    }
b0rt commented 4 years ago

Got it.. it was not related to filters per se

the issue was the collection i was creating in app\Sharp\OrganizationList.php, I had to flatten() this collection before sending it to the transform() method

the working function looks like this app\Sharp\OrganizationList.php

/**
     * Retrieve rows data as array.
     *
     * @param EntityListQueryParams $params
     * @return array
     */
    public function getListData(EntityListQueryParams $params)
    {
        $entries = Organization::all();
        if($params->filterFor("user")) {
            $entries = $entries->where("user_id", $params->filterFor("user"))->flatten();
        }
        return $this
            ->transform(
                $entries
            );
    }

Investigationwise in tinker

 $orgs = Organization::all()
=> Illuminate\Database\Eloquent\Collection {#3675
     all: [
       App\Models\Organization {#3666
         id: 1,
         title: "Org 1",
         user_id: 3,
         deleted_at: null,
         created_at: "2020-04-08 18:06:07",
         updated_at: "2020-04-08 18:06:07",
         addresses: Illuminate\Database\Eloquent\Collection {#3660
           all: [],
         },
         contact_mechanisms: Illuminate\Database\Eloquent\Collection {#3692
           all: [],
         },
       },
       App\Models\Organization {#3676
         id: 2,
         title: "Org 2",
// and so on

 $filtered = $orgs->where('user_id', 17)
=> Illuminate\Database\Eloquent\Collection {#3763
     all: [
       14 => App\Models\Organization {#3775
         id: 17,
         title: "org 17",
         user_id: 17,
         deleted_at: null,
         created_at: "2020-04-09 13:01:43",
         updated_at: "2020-04-09 13:01:43",

// and so on, which is wrong, because of the leading array key '14'
// this format caused sharp to 'choke' on the results as they couldn't be consumed in EntityList.vue
// so removing the layer of array_keys did the trick
$filtered = $orgs->where('user_id', 17)->flatten()
=> Illuminate\Support\Collection {#3761
     all: [
       App\Models\Organization {#3775
         id: 17,
         title: "momo GmbH",
         user_id: 17,
         deleted_at: null,
//

closing this as it is not a bug