From d4a643d92f1364b1e872580f23604db3835bd259 Mon Sep 17 00:00:00 2001 From: Devansh Bawari Date: Fri, 25 Aug 2023 18:38:31 +0530 Subject: [PATCH] feat: datagrid code refinement --- packages/Webkul/DataGrid/src/Action.php | 20 ++ packages/Webkul/DataGrid/src/Column.php | 128 ++++++++ packages/Webkul/DataGrid/src/DataGrid.php | 283 +++++++++--------- .../DataGrid/src/Enums/ColumnTypeEnum.php | 21 ++ packages/Webkul/DataGrid/src/MassAction.php | 20 ++ 5 files changed, 337 insertions(+), 135 deletions(-) create mode 100644 packages/Webkul/DataGrid/src/Action.php create mode 100644 packages/Webkul/DataGrid/src/Column.php create mode 100644 packages/Webkul/DataGrid/src/Enums/ColumnTypeEnum.php create mode 100644 packages/Webkul/DataGrid/src/MassAction.php diff --git a/packages/Webkul/DataGrid/src/Action.php b/packages/Webkul/DataGrid/src/Action.php new file mode 100644 index 00000000000..9dff6cdbba0 --- /dev/null +++ b/packages/Webkul/DataGrid/src/Action.php @@ -0,0 +1,20 @@ +setDatabaseColumnName(); + } + + /** + * Define the database column name. Initially, it will match the index. However, after adding an alias, + * the column name may change. + */ + public function setDatabaseColumnName(?string $databaseColumnName = null): void + { + $this->databaseColumnName = $databaseColumnName ?: $this->index; + } + + /** + * Set column type. + */ + public function setColumnType(): void + { + switch ($this->type) { + case ColumnTypeEnum::DATE_RANGE->value: + $this->formInputType = 'date'; + + $this->formOptions = $this->getDateOptions(); + + break; + + case ColumnTypeEnum::DATE_TIME_RANGE->value: + $this->formInputType = 'datetime-local'; + + $this->formOptions = $this->getDateOptions('Y-m-d H:i:s'); + + break; + } + } + + /** + * Get date options. + */ + public function getDateOptions(string $format = 'Y-m-d'): array + { + return [ + [ + 'name' => 'today', + 'label' => 'Today', + 'from' => now()->today()->format($format), + 'to' => now()->today()->format($format), + ], + [ + 'name' => 'yesterday', + 'label' => 'Yesterday', + 'from' => now()->yesterday()->format($format), + 'to' => now()->yesterday()->format($format), + ], + [ + 'name' => 'this_week', + 'label' => 'This Week', + 'from' => now()->startOfWeek()->format($format), + 'to' => now()->endOfWeek()->format($format), + ], + [ + 'name' => 'this_month', + 'label' => 'This Month', + 'from' => now()->startOfMonth()->format($format), + 'to' => now()->endOfMonth()->format($format), + ], + [ + 'name' => 'last_month', + 'label' => 'Last Month', + 'from' => now()->subMonth(1)->startOfMonth()->format($format), + 'to' => now()->subMonth(1)->endOfMonth()->format($format), + ], + [ + 'name' => 'last_three_months', + 'label' => 'Last 3 Months', + 'from' => now()->subMonth(3)->startOfMonth()->format($format), + 'to' => now()->subMonth(1)->endOfMonth()->format($format), + ], + [ + 'name' => 'last_six_months', + 'label' => 'Last 6 Months', + 'from' => now()->subMonth(6)->startOfMonth()->format($format), + 'to' => now()->subMonth(1)->endOfMonth()->format($format), + ], + [ + 'name' => 'this_year', + 'label' => 'This Year', + 'from' => now()->startOfYear()->format($format), + 'to' => now()->endOfYear()->format($format), + ], + ]; + } +} diff --git a/packages/Webkul/DataGrid/src/DataGrid.php b/packages/Webkul/DataGrid/src/DataGrid.php index e30b91a6a05..bda3b4c88a3 100644 --- a/packages/Webkul/DataGrid/src/DataGrid.php +++ b/packages/Webkul/DataGrid/src/DataGrid.php @@ -2,6 +2,10 @@ namespace Webkul\DataGrid; +use Illuminate\Http\JsonResponse; +use Illuminate\Pagination\LengthAwarePaginator; +use Webkul\DataGrid\Enums\ColumnTypeEnum; + abstract class DataGrid { /** @@ -53,6 +57,11 @@ abstract class DataGrid */ protected $queryBuilder; + /** + * Paginator instance. + */ + protected LengthAwarePaginator $paginator; + /** * Prepare query builder. */ @@ -78,143 +87,206 @@ public function prepareMassActions() } /** - * Map your filter. + * Add column. */ - public function addFilter(string $datagridColumn, string $queryColumn): void + public function addColumn(array $column): void { - foreach ($this->columns as &$column) { - if ($column['index'] === $datagridColumn) { - $column['column_name'] = $queryColumn; - break; - } - } + $this->columns[] = new Column( + index: $column['index'], + label: $column['label'], + type: $column['type'], + searchable: $column['searchable'], + filterable: $column['filterable'], + sortable: $column['sortable'], + closure: $column['closure'] ?? null, + ); } /** - * Add column. - * - * @param array $column - * @return void + * Add action. */ - public function addColumn($column) + public function addAction(array $action): void { - $column['column_name'] = $column['index']; + $this->actions[] = new Action( + icon: $action['icon'] ?? '', + title: $action['title'], + method: $action['method'], + url: $action['url'], + ); + } - if ($column['type'] === 'date_range') { - $column['input_type'] = 'date'; + /** + * Add mass action. + */ + public function addMassAction(array $massAction): void + { + $this->massActions[] = new MassAction( + icon: $massAction['icon'] ?? '', + title: $massAction['title'], + method: $massAction['method'], + url: $massAction['url'], + ); + } - $column['options'] = $this->getDateOptions(); - } elseif ($column['type'] === 'datetime_range') { - $column['input_type'] = 'datetime-local'; + /** + * Map your filter. + */ + public function addFilter(string $datagridColumn, string $queryColumn): void + { + foreach ($this->columns as $column) { + if ($column->index === $datagridColumn) { + $column->setDatabaseColumnName($queryColumn); - $column['options'] = $this->getDateOptions('Y-m-d H:i:s'); + break; + } } - - $this->columns[] = $column; } /** - * Add action. + * Set query builder. + * + * @param mixed $queryBuilder */ - public function addAction(array $action): void + public function setQueryBuilder($queryBuilder = null): void { - $this->actions[] = $action; + $this->queryBuilder = $queryBuilder ?: $this->prepareQueryBuilder(); } /** - * Add mass action. + * Validated request. */ - public function addMassAction(array $massAction): void + public function validatedRequest(): array { - $this->massActions[] = $massAction; + request()->validate([ + 'filters' => ['sometimes', 'required', 'array'], + 'sort' => ['sometimes', 'required', 'array'], + 'pagination' => ['sometimes', 'required', 'array'], + ]); + + return request()->only(['filters', 'sort', 'pagination']); } /** - * Prepare data for json response. + * Process all requested filters. * - * @return array + * @return \Illuminate\Database\Query\Builder */ - public function prepareData() + public function processRequestedFilters(array $requestedFilters) { - // need to refactor - $queryBuilder = $this->queryBuilder; - - $requestedFilters = request('filters', []); - foreach ($requestedFilters as $requestedColumn => $requestedValues) { if ($requestedColumn === 'all') { - $queryBuilder->where(function ($scopeQueryBuilder) use ($requestedValues) { + $this->queryBuilder->where(function ($scopeQueryBuilder) use ($requestedValues) { foreach ($requestedValues as $value) { collect($this->columns) - ->filter(fn ($column) => $column['searchable'] && $column['type'] !== 'boolean') - ->each(fn ($column) => $scopeQueryBuilder->orWhere($column['column_name'], $value)); + ->filter(fn ($column) => $column->searchable && $column->type !== ColumnTypeEnum::BOOLEAN->value) + ->each(fn ($column) => $scopeQueryBuilder->orWhere($column->databaseColumnName, $value)); } }); } else { - $column = collect($this->columns)->first(fn ($c) => $c['index'] === $requestedColumn); + $column = collect($this->columns)->first(fn ($c) => $c->index === $requestedColumn); - switch ($column['type']) { - case 'date_range': - case 'datetime_range': - $queryBuilder->where(function ($scopeQueryBuilder) use ($column, $requestedValues) { + switch ($column->type) { + case ColumnTypeEnum::DATE_RANGE->value: + case ColumnTypeEnum::DATE_TIME_RANGE->value: + $this->queryBuilder->where(function ($scopeQueryBuilder) use ($column, $requestedValues) { foreach ($requestedValues as $value) { - $scopeQueryBuilder->whereBetween($column['column_name'], [$value[0] ?? '', $value[1] ?? '']); + $scopeQueryBuilder->whereBetween($column->databaseColumnName, [$value[0] ?? '', $value[1] ?? '']); } }); + break; default: - $queryBuilder->where(function ($scopeQueryBuilder) use ($column, $requestedValues) { + $this->queryBuilder->where(function ($scopeQueryBuilder) use ($column, $requestedValues) { foreach ($requestedValues as $value) { - $scopeQueryBuilder->orWhere($column['column_name'], $value); + $scopeQueryBuilder->orWhere($column->databaseColumnName, $value); } }); + break; } } } - // need to make good search column method, currently this is working but still need work here... - $queryBuilder->orderBy(request('sort.column', $this->primaryColumn), request('sort.order', $this->sortOrder)); + return $this->queryBuilder; + } - $paginator = $queryBuilder->paginate( - request('pagination.per_page', $this->itemsPerPage), + /** + * Process requested sorting. + * + * @return \Illuminate\Database\Query\Builder + */ + public function processRequestedSorting($requestedSort) + { + return $this->queryBuilder->orderBy($requestedSort['column'] ?? $this->primaryColumn, $requestedSort['order'] ?? $this->sortOrder); + } + + /** + * Process requested pagination. + */ + public function processRequestedPagination($requestedPagination): LengthAwarePaginator + { + return $this->queryBuilder->paginate( + $requestedPagination['per_page'] ?? $this->itemsPerPage, ['*'], 'page', - request('pagination.page', 1) - )->toArray(); + $requestedPagination['page'] ?? 1 + ); + } - foreach ($paginator['data'] as $data) { + /** + * Process request. + */ + public function processRequest(): void + { + /** + * Store all request parameters in this variable; avoid using direct request helpers afterward. + */ + $requestedParams = $this->validatedRequest(); + + $this->queryBuilder = $this->processRequestedFilters($requestedParams['filters'] ?? []); + + $this->queryBuilder = $this->processRequestedSorting($requestedParams['sort'] ?? []); + + $this->paginator = $this->processRequestedPagination($requestedParams['pagination'] ?? []); + } + + /** + * Format data. + */ + public function formatData(): array + { + $paginator = $this->paginator->toArray(); + + foreach ($paginator['data'] as $record) { foreach ($this->columns as $column) { - if (isset($column['closure'])) { - $data->{$column['index']} = $column['closure']($data); - - $data->is_closure = true; + if ($closure = $column->closure) { + $record->{$column->index} = $closure($record); + + $record->is_closure = true; } } - $data->actions = []; + $record->actions = []; foreach ($this->actions as $action) { - $data->actions[] = [ - ...$action, + $getUrl = $action->url; - 'url' => $action['url']($data), + $record->actions[] = [ + 'icon' => $action->icon, + 'title' => $action->title, + 'method' => $action->method, + 'url' => $getUrl($record), ]; } } - // refactor return [ - 'columns' => $this->columns, - - 'actions' => $this->actions, - + 'columns' => $this->columns, + 'actions' => $this->actions, 'mass_actions' => $this->massActions, - - 'records' => $paginator['data'], - - 'meta' => [ + 'records' => $paginator['data'], + 'meta' => [ 'primary_column' => $this->primaryColumn, 'from' => $paginator['from'], 'to' => $paginator['to'], @@ -229,10 +301,8 @@ public function prepareData() /** * Get json data. - * - * @return \Illuminate\Http\JsonResponse */ - public function toJson() + public function toJson(): JsonResponse { $this->prepareColumns(); @@ -240,67 +310,10 @@ public function toJson() $this->prepareMassActions(); - $this->queryBuilder = $this->prepareQueryBuilder(); + $this->setQueryBuilder(); - return response()->json($this->prepareData()); - } + $this->processRequest(); - /** - * Get date options. - * - * @return array - */ - public function getDateOptions($format = 'Y-m-d') - { - return [ - [ - 'name' => 'today', - 'label' => 'Today', - 'from' => now()->today()->format($format), - 'to' => now()->today()->format($format), - ], - [ - 'name' => 'yesterday', - 'label' => 'Yesterday', - 'from' => now()->yesterday()->format($format), - 'to' => now()->yesterday()->format($format), - ], - [ - 'name' => 'this_week', - 'label' => 'This Week', - 'from' => now()->startOfWeek()->format($format), - 'to' => now()->endOfWeek()->format($format), - ], - [ - 'name' => 'this_month', - 'label' => 'This Month', - 'from' => now()->startOfMonth()->format($format), - 'to' => now()->endOfMonth()->format($format), - ], - [ - 'name' => 'last_month', - 'label' => 'Last Month', - 'from' => now()->subMonth(1)->startOfMonth()->format($format), - 'to' => now()->subMonth(1)->endOfMonth()->format($format), - ], - [ - 'name' => 'last_three_months', - 'label' => 'Last 3 Months', - 'from' => now()->subMonth(3)->startOfMonth()->format($format), - 'to' => now()->subMonth(1)->endOfMonth()->format($format), - ], - [ - 'name' => 'last_six_months', - 'label' => 'Last 6 Months', - 'from' => now()->subMonth(6)->startOfMonth()->format($format), - 'to' => now()->subMonth(1)->endOfMonth()->format($format), - ], - [ - 'name' => 'this_year', - 'label' => 'This Year', - 'from' => now()->startOfYear()->format($format), - 'to' => now()->endOfYear()->format($format), - ], - ]; + return response()->json($this->formatData()); } -} \ No newline at end of file +} diff --git a/packages/Webkul/DataGrid/src/Enums/ColumnTypeEnum.php b/packages/Webkul/DataGrid/src/Enums/ColumnTypeEnum.php new file mode 100644 index 00000000000..5209bc13ac2 --- /dev/null +++ b/packages/Webkul/DataGrid/src/Enums/ColumnTypeEnum.php @@ -0,0 +1,21 @@ +