nova-mega-filter icon indicating copy to clipboard operation
nova-mega-filter copied to clipboard

Bug: column filtering not working anymore on HasMany relational table

Open didix16 opened this issue 2 years ago • 6 comments

Hi,

I just updated mega-filter to v0.6.0 from v0.2.6 and I realized that column filtering is not working anymore on HasMany relationships table.

An example can be seen here:

This is from version 0.2.6 image

This is from version 0.6.0 image

A you can see from the last image, the scroll is much bigger and it loads all the columns...

Waiting for a feedback.

Thanks in advance!

didix16 avatar Oct 27 '21 08:10 didix16

Hi, can you share some sample code? I'm not sure what you mean by on HasMany Relationship table, I have only tested this package when using it on the main INDEX view of a resource... not on hasMany tables... I'm unsure how it worked before as I had never that use case in mind...

milewski avatar Oct 27 '21 09:10 milewski

Sure, here you have:

Suppose I have a class BusinessUnit which is a Nova Resource and is something like:


<?php

namespace App\Nova;

use Illuminate\Http\Request;
use Laravel\Nova\Fields\HasMany;
use Laravel\Nova\Fields\Text;

class BusinessUnit extends Resource {

    /**
     * The model the resource corresponds to.
     *
     * @var string
     */
    public static $model = 'App\Models\BusinessUnit';

    // ....

    /**
     * Get the fields displayed by the resource.
     *
     * @param Request $request
     * @return array
     */
    public function fields(Request $request)
    {
        return [
            Text::make(__("name"), "name")
                ->rules([
                    'required',
                    'max:255'
                ])
                ->canSeeWhen("viewField", [$this, "name"])
                ->canEditWhen("editField", [$this, "name"]),
            HasMany::make(__("Events"), "events", Event::class)
                ->withMeta([
                    "name" => __("Events that has this business unit")
                ])
                ->canSeeWhen("viewRelation", [$this, "events"])
                ->canEditWhen("editRelation", [$this, "events"]),
        ];
    }

// ...

}

Also suppose I have a class Event that is a Nova Resource like this:


<?php

namespace App\Nova;

use App\Nova\Actions\EventFavoriteAction;
use App\Nova\Fields\Event\ActorEventFields;
use App\Nova\Fields\Event\ArtistEventFields;
use App\Nova\Fields\Event\BudgetControlFields;
use App\Nova\Fields\Event\CreativeFields;
use App\Nova\Fields\Event\FileFields;
use App\Nova\Fields\Event\FinanceFields;
use App\Nova\Fields\Event\ImageFields;
use App\Nova\Fields\Event\MainFields;
use App\Nova\Fields\Event\MarketingFields;
use App\Nova\Fields\Event\MerchandisingFields;
use App\Nova\Fields\Event\ProductionFields;
use App\Nova\Fields\Event\ThemeFields;
use App\Nova\Fields\Event\TicketFields;
use App\Nova\Fields\Event\TransportFields;
use App\Nova\Fields\Event\VipFields;
use App\Services\CacheService;
//...
use DigitalCreative\MegaFilter\Column;
use DigitalCreative\MegaFilter\HasMegaFilterTrait;
use DigitalCreative\MegaFilter\MegaFilter;
// ...
use Laravel\Nova\Fields\HasMany;
use Laravel\Nova\Http\Requests\NovaRequest;
use Laravel\Nova\Panel;

class Event extends Resource
{
    use HasMegaFilterTrait;

    /**
     * The model the resource corresponds to.
     *
     * @var string
     */
    public static $model = 'App\Models\Event';

    /**
     * Get the fields displayed by the resource.
     *
     * @param Request $request
     * @return array
     */
    public function fields(Request $request)
    {
        return [
            new Panel(__("Event Details"), new MainFields($this, $request)),
            new Panel(__("Files"), new FileFields($this, $request)),
            new Panel(__("Themes"), new ThemeFields($this, $request)),
            new Panel(__("Images"), new ImageFields($this, $request)),
            new Panel(__("Tickets"), new TicketFields($this, $request)),
            new Panel(__("Transport"), new TransportFields($this, $request)),
            new Panel(__("Creative"), new CreativeFields($this, $request)),
            new Panel(__("Marketing"), new MarketingFields($this, $request)),
            new Panel(__("Production"), new ProductionFields($this, $request)),
            new Panel(__("Finance"), new FinanceFields($this, $request)),
            new Panel(__("VIP"), new VipFields($this, $request)),
            new Panel(__("Merchandising"), new MerchandisingFields($this, $request)),
            new Panel(__("Budgets Control"), new BudgetControlFields($this, $request)),

            BelongsToMany::make("Artists")
                ->withMeta([
                    "name" => __("Booked artists")
                ])
                ->canSeeWhen("viewRelation", [$this, "artists"])
                ->canEditWhen("editRelation", [$this, 'artists'])
                ->fields(new ArtistEventFields($this, $request)),

            // ....

            Boolean::make("Has sponsors", function (){
                return $this->resource->sponsors()->get()->count() > 0;
            })->withMeta([
                "name" => __("Has sponsors")
            ])
            ->onlyOnIndex(),
            HasMany::make(__("sponsors"), "sponsors", Sponsor::class)
                ->showOnIndex(false),
        ];
    }

    /**
     * Get the cards available for the request.
     *
     * @param Request $request
     * @return array
     */
    public function cards(Request $request)
    {
        /**
         * @var CacheService $cacheService
         */
        $cacheService = app("cache.service");

        $user = $request->user();
        $resource = class_basename($this->resource);

        $CACHE_KEY = $cacheService->generateKey("megafilter", "resource", $resource, $user->id);

        // Check CACHE. If we have stored some data then get that data, else compute the data
        if ($cacheService->isHit($CACHE_KEY)){

            // Also check if the user has modified the megafilter. If it has, then invalidate the cache
            $MEGA_FILTER_CACHE_KEY = $cacheService->generateKey("megafilterHash", "resource", $resource, $user->id);

            $megafilterHash = $request->get("megaFilter"); // *<== I'VE CHANGED THIS IN version 0.6.0 but nothing changed*
            $currentMegafilterHash = $cacheService->rememberForever($MEGA_FILTER_CACHE_KEY, function () use($megafilterHash){
                return $megafilterHash;
            });

            if ($megafilterHash === $currentMegafilterHash){

                $columns = $cacheService->get($CACHE_KEY);
                return [
                    MegaFilter::make([
                        "columns" => $columns
                    ]),
                ];

            }else {
                $cacheService->delete($MEGA_FILTER_CACHE_KEY);
                $cacheService->delete($CACHE_KEY);
            }
        }


        // Get event field list
        $fields = Schema::getColumnListing("events");

        // Remove unwanted fields
        $fields = array_diff($fields, ["deleted_at", "show_form"]);


        // filter only the fields that the user can see
        $fields = array_filter($fields, function ($v) use(&$user){
            return
                (
                    $user->hasPermission("VIEW_FIELD_" . strtoupper($v) . "_EVENTS") ||
                    $user->hasPermission("VIEW_RELATION_" . strtoupper($v) . "_EVENTS")
                );

        });

        // Replace original field name for model name to properly get the related entity as value and show it to index list
        $repl = $this->resource->getRenamedFields();

        $fields = array_map(function ($v) use ($repl){
            return $repl[$v] ?? $v;
        }, $fields);


        // Define which field should be checked by default
        $checked = [
            "ideo", "code", "city", "capacity", "start_at", "country", "eventstatus"
        ];

        // The array containing Column instances to apply on MegaFilter
        $columns = [];
        foreach ($fields as $field){
            if (!empty($checked) && ($idx = array_search($field, $checked)) !== FALSE ){
                $columns[] = Column::make(model_field_trans($this, $field), $field)->checked();
                array_splice($checked, $idx, 1);
            }else{
                $columns[] = Column::make(model_field_trans($this, $field), $field);
            }

        }

        /**
         * ****************************
         * COMPUTED COLUMNS
         * ****************************
         */
        $columns[] = Column::make("Theme", "theme")->checked();
        $columns[] = Column::make("Has sponsors")->permanent();
        $columns[] = Column::make('CM', 'cm')->checked(false);

        $columns = $cacheService->rememberForever($CACHE_KEY, function () use($columns){
            return $columns;
        });

        return [
            MegaFilter::make([
                "columns" => $columns
            ]),
        ];
    }
    // ....
}

didix16 avatar Oct 27 '21 09:10 didix16

Does it work when you open /resources/events?

/resources/business-units <-- this package was not meant to be working on this page, imagine you could have multiple hasMany fields on your BusinessUnits resource... how the filters would be merged... how to know a specific column is targeting the first hasMany not the second.. etc...

milewski avatar Oct 27 '21 10:10 milewski

It works on /resources/events and /resources/business-units. If it has multiple HasMany fields, it applies each filter correctly. For example: if i have and entity A with HasMany entity B, if I define the columns on B, then the HasMany on A works. Also if A has HasMany to C and C has the megafilter with its columns, also applies it. I think the filter that is applying is the last made on /resources/B or /resources/C.

If it was not meant to be working like this, well, it does and for me its a nice feature because i don't want to list all the columns since my Event model has a lot, like 30 or more....

didix16 avatar Oct 27 '21 10:10 didix16

Yes I understand, I guess you are only relying on the columns being hidden/visible on the HasMany relation tables, not the actual "Mega Filter" card that is injected on the top of the page... I'm not sure what changed since the version you had it working... it probably was on this commit/ release: https://github.com/dcasia/nova-mega-filter/compare/v0.3.0...master

I think you could try to return true on this function here to see if it fixes the issue for you... if so then might just be a matter of figuring out a way to identify what is on the request that is being done to fetch the HasMany relation and return true..

https://github.com/dcasia/nova-mega-filter/blob/faa396321e5de26926fd00a7a386451252291380/src/HasMegaFilterTrait.php#L97-L119

milewski avatar Oct 27 '21 15:10 milewski

Ok then, I'll try it later, test it and I'll tell you if it worked.

Thanks :+1:

didix16 avatar Oct 28 '21 07:10 didix16