diff --git a/demo/app/AgGrid/VirtualColumns/FlamingoKeeperColumn.php b/demo/app/AgGrid/VirtualColumns/FlamingoKeeperColumn.php new file mode 100644 index 0000000..712ac19 --- /dev/null +++ b/demo/app/AgGrid/VirtualColumns/FlamingoKeeperColumn.php @@ -0,0 +1,38 @@ + */ +class FlamingoKeeperColumn extends AgGridVirtualColumn +{ + public function getValue($row): mixed + { + return $row->keeper_name; + } + + public function getSetValues(AgGridQueryBuilder $builder): Collection + { + return $builder + ->distinct() + ->select('keeper_id', 'keeper_name') + ->orderBy('keeper_name') + ->get() + ->map(fn ($data) => new AgGridSetValue($data->keeper_id, $data->keeper_name)); + } + + public function getOrderColumns(): array + { + return ['keeper_name']; + } + + public function getFilterColumn(): string + { + return 'keeper_id'; + } +} diff --git a/demo/app/Models/FlamingoAgGridView.php b/demo/app/Models/FlamingoAgGridView.php index 15c5349..54309f1 100644 --- a/demo/app/Models/FlamingoAgGridView.php +++ b/demo/app/Models/FlamingoAgGridView.php @@ -2,9 +2,13 @@ namespace App\Models; +use App\AgGrid\VirtualColumns\FlamingoKeeperColumn; +use Clickbar\AgGrid\AgGridColumnDefinition; +use Clickbar\AgGrid\Contracts\AgGridExportable; +use Clickbar\AgGrid\Contracts\AgGridHasVirtualColumns; use Clickbar\AgGrid\Model\AgGridViewModel; -class FlamingoAgGridView extends AgGridViewModel +class FlamingoAgGridView extends AgGridViewModel implements AgGridExportable, AgGridHasVirtualColumns { protected $table = 'flamingo_ag_grid_view'; @@ -24,4 +28,32 @@ class FlamingoAgGridView extends AgGridViewModel 'zoo_created_at' => 'immutable_datetime', 'zoo_updated_at' => 'immutable_datetime', ]; + + public function getVirtualColumns(): array + { + return ['keeper' => new FlamingoKeeperColumn()]; + } + + public static function getAgGridColumnDefinitions(): array + { + return [ + new AgGridColumnDefinition('id', 'ID'), + new AgGridColumnDefinition('flamingo_name', 'Flamingo Name'), + new AgGridColumnDefinition('flamingo_species', 'Flamingo Species'), + new AgGridColumnDefinition('flamingo_weight', 'Flamingo Weight'), + new AgGridColumnDefinition('flamingo_preferred_food_types', 'Flamingo Preferred Food Types'), + new AgGridColumnDefinition('flamingo_custom_properties', 'Flamingo Custom Properties'), + new AgGridColumnDefinition('flamingo_is_hungry', 'Flamingo is Hungry'), + new AgGridColumnDefinition('flamingo_last_vaccinated_on', 'Flamingo last vaccinated on'), + new AgGridColumnDefinition('keeper_id', 'Keeper ID'), + new AgGridColumnDefinition('keeper', 'Keeper Name'), + new AgGridColumnDefinition('zoo_id', 'Zoo ID'), + new AgGridColumnDefinition('zoo_name', 'Zoo Name'), + new AgGridColumnDefinition('zoo_address.street', 'Zoo Street'), + new AgGridColumnDefinition('zoo_address.city', 'Zoo City'), + new AgGridColumnDefinition('zoo_address.email', 'Zoo Mail'), + new AgGridColumnDefinition('zoo_address.phone', 'Zoo Phone'), + ]; + + } } diff --git a/demo/app/Providers/AppServiceProvider.php b/demo/app/Providers/AppServiceProvider.php index 452e6b6..081d011 100644 --- a/demo/app/Providers/AppServiceProvider.php +++ b/demo/app/Providers/AppServiceProvider.php @@ -2,6 +2,7 @@ namespace App\Providers; +use Illuminate\Database\Eloquent\Model; use Illuminate\Support\ServiceProvider; class AppServiceProvider extends ServiceProvider @@ -19,6 +20,6 @@ public function register(): void */ public function boot(): void { - // + Model::shouldBeStrict(); } } diff --git a/demo/resources/js/Components/AgGrid/AgGrid.vue b/demo/resources/js/Components/AgGrid/AgGrid.vue index 1bd54ca..ba98001 100644 --- a/demo/resources/js/Components/AgGrid/AgGrid.vue +++ b/demo/resources/js/Components/AgGrid/AgGrid.vue @@ -201,7 +201,7 @@ async function exportServerSide(format: 'excel' | 'csv', onlySelected: boolean) ...parameters, ...(onlySelected ? api?.getServerSideSelectionState() : {}), exportFormat: format, - exportCols: cols, + exportColumns: cols, customFilters: props.customFilters, }, { diff --git a/demo/resources/js/Models/Flamingo.ts b/demo/resources/js/Models/Flamingo.ts index a3a4008..bf866ac 100644 --- a/demo/resources/js/Models/Flamingo.ts +++ b/demo/resources/js/Models/Flamingo.ts @@ -213,9 +213,14 @@ export const flamingoViewColumnDefinition = [ { headerValueGetter: () => 'Custom properties', field: 'flamingo_custom_properties', - filter: false, + filter: true, sortable: false, - suppressMenu: true + suppressMenu: false, + ...getSetFilterParametersFor( + 'flamingo_custom_properties', + route('api.view.flamingos.set-values' + ) + ) }, { headerValueGetter: () => 'Is hungry', @@ -243,11 +248,11 @@ export const flamingoViewColumnDefinition = [ children: [ { headerValueGetter: () => 'Name', - field: 'keeper_name', + field: 'keeper', sortable: true, filter: true, ...getSetFilterParametersFor( - 'keeper_name', + 'keeper', route('api.view.flamingos.set-values' ) ) diff --git a/demo/resources/js/Utils/ag-grid.ts b/demo/resources/js/Utils/ag-grid.ts index bc3a9e7..b42b7c8 100644 --- a/demo/resources/js/Utils/ag-grid.ts +++ b/demo/resources/js/Utils/ag-grid.ts @@ -22,6 +22,24 @@ export function getSetFilterParametersFor( }) }, refreshValuesOnOpen, + keyCreator: (params) => { + if (typeof params.value === 'string'){ + return params.value + } + if (params.value === null){ + return null + } + return params.value.value + }, + valueFormatter: (params) => { + if (typeof params.value === 'string'){ + return params.value + } + if (params.value === null){ + return null + } + return params.value.label + } }, } } diff --git a/src/AgGridExport.php b/src/AgGridExport.php index 99336ac..a4fed2a 100644 --- a/src/AgGridExport.php +++ b/src/AgGridExport.php @@ -4,6 +4,7 @@ use Clickbar\AgGrid\Contracts\AgGridExportable; use Clickbar\AgGrid\Contracts\AgGridExportTimezoneProvider; +use Clickbar\AgGrid\Contracts\AgGridHasVirtualColumns; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Relations\Relation; use Illuminate\Support\Collection; @@ -26,6 +27,9 @@ class AgGridExport implements FromQuery, ShouldAutoSize, WithColumnFormatting, W private readonly array $columnsToExport; + /** @var array */ + private array $virtualColumns = []; + public function __construct( private readonly Builder|Relation $queryBuilder, ?array $columnsToExport = null @@ -36,8 +40,11 @@ public function __construct( throw new \InvalidArgumentException('The model must implement the AgGridExportable interface.'); } - $this->columnDefinitions = collect($model->getAgGridColumnDefinitions())->keyBy('id'); + if ($model instanceof AgGridHasVirtualColumns) { + $this->virtualColumns = $model->getVirtualColumns(); + } + $this->columnDefinitions = collect($model->getAgGridColumnDefinitions())->keyBy('id'); $this->columnsToExport = $columnsToExport ?? $this->columnDefinitions->keys()->all(); $timezone = null; @@ -72,6 +79,10 @@ public function map($row): array /** @var AgGridColumnDefinition $columDefinition */ $columDefinition = $this->columnDefinitions[$column]; + if (array_key_exists($column, $this->virtualColumns)) { + $row[$column] = $this->virtualColumns[$column]->getValue($row); + } + if ($columDefinition->valueGetter !== null) { $value = $columDefinition->valueGetter->call($row, $row); } else { diff --git a/src/AgGridQueryBuilder.php b/src/AgGridQueryBuilder.php index 64b2abc..fdd1bd3 100644 --- a/src/AgGridQueryBuilder.php +++ b/src/AgGridQueryBuilder.php @@ -3,6 +3,7 @@ namespace Clickbar\AgGrid; use Clickbar\AgGrid\Contracts\AgGridCustomFilterable; +use Clickbar\AgGrid\Contracts\AgGridHasVirtualColumns; use Clickbar\AgGrid\Enums\AgGridDateFilterType; use Clickbar\AgGrid\Enums\AgGridExportFormat; use Clickbar\AgGrid\Enums\AgGridFilterType; @@ -40,6 +41,8 @@ class AgGridQueryBuilder implements Responsable /** @var class-string | null */ protected ?string $resourceClass = null; + protected array $virtualColumns = []; + /** * @param EloquentBuilder|Relation|Model|class-string $subject */ @@ -53,6 +56,10 @@ public function __construct(array $params, EloquentBuilder|Relation|Model|string $this->subject = $subject; $model = $subject->getModel(); + if ($model instanceof AgGridHasVirtualColumns) { + $this->virtualColumns = $model->getVirtualColumns(); + } + if ($model instanceof AgGridCustomFilterable) { $model->applyAgGridCustomFilters($this->subject, $this->params['customFilters'] ?? []); } @@ -124,6 +131,10 @@ public function toSetValues(array $allowedColumns = []): Collection throw InvalidSetValueOperation::make(); } + if ($this->hasVirtualColumnFor($colId)) { + return $this->virtualColumns[$colId]->getSetValues($this); + } + if (collect($allowedColumns)->first() !== '*' && ! in_array($colId, $allowedColumns)) { throw UnauthorizedSetFilterColumn::make($colId); } @@ -204,6 +215,14 @@ public function toResponse($request): mixed $data = $this->get(); + if (! empty($this->virtualColumns)) { + foreach ($data as $row) { + foreach ($this->virtualColumns as $key => $virtualColumn) { + $row[$key] = $virtualColumn->getValue($row); + } + } + } + // wrap in a resource if ($this->resourceClass !== null) { /** @var class-string $resourceClass */ @@ -266,7 +285,12 @@ protected function addFiltersToQuery(): void foreach ($filters as $colId => $filter) { - $column = Column::fromColId($this->subject, $colId); + if ($this->hasVirtualColumnFor($colId)) { + $column = Column::fromColId($this->subject, $this->virtualColumns[$colId]->getFilterColumn()); + + } else { + $column = Column::fromColId($this->subject, $colId); + } if ($column->hasRelations()) { $this->subject->whereHas($column->getDottedRelation(), function (EloquentBuilder $builder) use ($column, $filter) { @@ -292,8 +316,15 @@ protected function addSortsToQuery(): void } foreach ($sorts as $sort) { - $column = Column::fromColId($this->subject, $sort['colId']); - $this->subject->orderBy($column->getNameAsJsonPath(), $sort['sort']); + $colId = $sort['colId']; + if ($this->hasVirtualColumnFor($colId)) { + $columnNames = $this->virtualColumns[$colId]->getOrderColumns(); + } else { + $columnNames = [Column::fromColId($this->subject, $colId)->getNameAsJsonPath()]; + } + foreach ($columnNames as $columnName) { + $this->subject->orderBy($columnName, $sort['sort']); + } } // we need an additional sort condition so that the order is stable in all cases @@ -428,4 +459,9 @@ protected function traverse($model, $key, $default = null): Model return $model; } + + protected function hasVirtualColumnFor(string $colId): bool + { + return array_key_exists($colId, $this->virtualColumns); + } } diff --git a/src/AgGridVirtualColumn.php b/src/AgGridVirtualColumn.php new file mode 100644 index 0000000..1fb6590 --- /dev/null +++ b/src/AgGridVirtualColumn.php @@ -0,0 +1,28 @@ + */ + public function getVirtualColumns(): array; +}