laravel / nova-issues

557 stars 34 forks source link

Partition metrics in combination with spatie/laravel-model-states #4985

Closed MrAtiebatie closed 2 years ago

MrAtiebatie commented 2 years ago

Description:

I'm using the great spatie/laravel-model-states package. This will cast a model's string attribute to a State class. However, this produces a bug in Nova when creating a metric. For example, when working with a column invoice_status that will be casted to a State.

class Invoice extends Model {
    protected $casts = [
        'invoice_status' => InvoiceState::class,
    ];
}

With the following metric:

class CountPartition extends Partition {
    public function calculate(NovaRequest $request)
    {
        return $this->count($request, Invoice::class, 'invoice_status');
    }
}

However, Nova tries to map the column 'invoice_status' to an array with the count result. But since invoice_status is mapped to a State class it throws an 'Illegal offset error'. It's produced in Laravel\Nova\Metrics\Partition in this function:

protected function formatAggregateResult($result, $groupBy)
{
    $key = with($result->{last(explode('.', $groupBy))}, function ($key) {
        return Util::value($key);
    });

    // This line ⬇️
    return [$key => $result->aggregate];
}

Changing it to use raw original attributes will fix this:

$key = with($result->getRawOriginal(last(explode('.', $groupBy))), function ($key) {
    return Util::value($key);
});

Detailed steps to reproduce the issue on a fresh Nova installation:

crynobone commented 2 years ago

The suggested changes going to cause breaking change to BackedEnum casted attribute. That's not acceptable.

RhysLees commented 2 years ago

i just convert the Enum to a collection for nova with this trait:

<?php

namespace RhysLees\EnumTraits\Traits;

use Illuminate\Support\Collection;
use Illuminate\Support\Str;

trait HasOptions
{
    public static function options(): Collection
    {
        return Collection::make(self::cases())->mapWithKeys(
            fn ($case) => [$case->value => Str::of($case->value)->replace('_', ' ')->value()]
        );
    }

Not sure if that helps but it fixed the Illegal offset for me.

<?php

namespace App\Enums;

use RhysLees\EnumTraits\Traits\HasOptions;

enum RoleEnum: string
{
    use HasOptions;

    case ARRANGER = 'Arranger';
    case COMPOSER = 'Composer';
    case LYRICIST = 'Lyricist';
}

Then i loop over each Enum and filter the relation to build the results:

class ChartsPerRolePartitionMetric extends Partition
{
    public function calculate(NovaRequest $request)
    {
        $person = Person::find($request->resourceId);

        $results = [];

        foreach (RoleEnum::options() as $role) {
            $results[$role] = $person->charts->filter(function ($chart) use ($role) {
                return $chart->pivot->role->value === $role;
            })->count();
        }

        return $this->result($results);
    }
crynobone commented 2 years ago

image

I believe this changes should be enough.

github-actions[bot] commented 2 years ago

This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.