From cb95bce9da0caf0676874ce66109cb65749f6ad9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Wr=C3=B3blewski?= Date: Mon, 29 Jul 2024 17:47:59 +0200 Subject: [PATCH] wip --- assets/controllers/persistence.js | 54 ------------------- assets/controllers/state.js | 41 ++++++++++++++ assets/package.json | 4 +- docs/src/docs/features/filtering.md | 4 +- docs/src/docs/features/pagination.md | 19 +++++++ docs/src/docs/features/sorting.md | 19 +++++++ docs/src/docs/installation.md | 2 +- src/Pagination/PaginationUrlGenerator.php | 7 +-- .../PaginationUrlGeneratorInterface.php | 4 +- src/Pagination/PaginationView.php | 2 +- src/Resources/views/themes/base.html.twig | 28 +++++----- src/Twig/DataTableExtension.php | 4 +- src/Type/DataTableType.php | 28 +++------- .../Pagination/PaginationUrlGeneratorTest.php | 21 +++----- 14 files changed, 121 insertions(+), 116 deletions(-) delete mode 100644 assets/controllers/persistence.js create mode 100644 assets/controllers/state.js diff --git a/assets/controllers/persistence.js b/assets/controllers/persistence.js deleted file mode 100644 index 8be048df..00000000 --- a/assets/controllers/persistence.js +++ /dev/null @@ -1,54 +0,0 @@ -import { Controller } from '@hotwired/stimulus' - -export default class extends Controller { - static targets = ['form']; - - static values = { - pagination: Object, - sorting: Object, - } - - connect() { - this.#loadFormsState(); - - if (this.paginationValue.page > 0) { - this.#loadPaginationState(); - } - - if (this.sortingValue.columns) { - this.#loadSortingState(); - } - } - - #loadFormsState() { - const url = new URL(window.location.href); - - for (const form of this.formTargets) { - const formData = new FormData(form); - - for (const [key, value] of formData) { - url.searchParams.set(key, String(value)); - } - } - - window.history.replaceState(null, null, url); - } - - #loadPaginationState() { - const url = new URL(window.location.href); - - url.searchParams.set(this.paginationValue.parameter, this.paginationValue.page); - - window.history.replaceState(null, null, url); - } - - #loadSortingState() { - const url = new URL(window.location.href); - - for (const [name, direction] of Object.entries(this.sortingValue.columns)) { - url.searchParams.set(this.sortingValue.parameter + '[' + name + ']', direction); - } - - window.history.replaceState(null, null, url); - } -} diff --git a/assets/controllers/state.js b/assets/controllers/state.js new file mode 100644 index 00000000..c177a7d3 --- /dev/null +++ b/assets/controllers/state.js @@ -0,0 +1,41 @@ +import { Controller } from '@hotwired/stimulus' + +export default class extends Controller { + static values = { + urlQueryParameters: Object, + } + + connect() { + this.#appendUrlQueryParameters(); + } + + #appendUrlQueryParameters() { + const url = new URL(window.location.href); + + const parameters = this.#flattenParameters(this.urlQueryParametersValue); + + for (const [key, value] of Object.entries(parameters)) { + if (!url.searchParams.has(key)) { + url.searchParams.set(key, String(value)); + } + } + + window.history.replaceState(null, null, url); + } + + #flattenParameters(input, keyName) { + let result = {}; + + for (const key in input) { + const newKey = keyName ? `${keyName}[${key}]` : key; + + if (typeof input[key] === "object" && !Array.isArray(input[key])) { + result = {...result, ...this.#flattenParameters(input[key], newKey)} + } else { + result[newKey] = input[key]; + } + } + + return result; + } +} diff --git a/assets/package.json b/assets/package.json index d0e6baff..36305897 100755 --- a/assets/package.json +++ b/assets/package.json @@ -15,8 +15,8 @@ "fetch": "eager", "enabled": true }, - "persistence": { - "main": "controllers/persistence.js", + "state": { + "main": "controllers/state.js", "fetch": "eager", "enabled": true } diff --git a/docs/src/docs/features/filtering.md b/docs/src/docs/features/filtering.md index 56b91d0d..924533b0 100644 --- a/docs/src/docs/features/filtering.md +++ b/docs/src/docs/features/filtering.md @@ -180,14 +180,14 @@ class ProductController extends AbstractController By default, the filters loaded from the persistence are not visible in the URL. -It is recommended to make sure the **persistence** controller is enabled in your `assets/controllers.json`, +It is recommended to make sure the **state** controller is enabled in your `assets/controllers.json`, which will automatically append the filters to the URL, even if multiple data tables are visible on the same page. ```json { "controllers": { "@kreyu/data-table-bundle": { - "persistence": { + "state": { "enabled": true } } diff --git a/docs/src/docs/features/pagination.md b/docs/src/docs/features/pagination.md index e76368bb..42c89cf0 100644 --- a/docs/src/docs/features/pagination.md +++ b/docs/src/docs/features/pagination.md @@ -176,6 +176,25 @@ class ProductController extends AbstractController ``` ::: +### Adding pagination loaded from persistence to URL + +By default, the pagination loaded from the persistence is not visible in the URL. + +It is recommended to make sure the **state** controller is enabled in your `assets/controllers.json`, +which will automatically append the pagination parameters to the URL, even if multiple data tables are visible on the same page. + +```json +{ + "controllers": { + "@kreyu/data-table-bundle": { + "state": { + "enabled": true + } + } + } +} +``` + ## Default pagination The default pagination data can be overridden using the data table builder's `setDefaultPaginationData()` method: diff --git a/docs/src/docs/features/sorting.md b/docs/src/docs/features/sorting.md index 9473041b..1e934a59 100644 --- a/docs/src/docs/features/sorting.md +++ b/docs/src/docs/features/sorting.md @@ -245,6 +245,25 @@ class ProductController extends AbstractController ``` ::: +### Adding sorting loaded from persistence to URL + +By default, the sorting loaded from the persistence is not visible in the URL. + +It is recommended to make sure the **state** controller is enabled in your `assets/controllers.json`, +which will automatically append the sorting parameters to the URL, even if multiple data tables are visible on the same page. + +```json +{ + "controllers": { + "@kreyu/data-table-bundle": { + "state": { + "enabled": true + } + } + } +} +``` + ## Default sorting The default sorting data can be overridden using the data table builder's `setDefaultSortingData()` method: diff --git a/docs/src/docs/installation.md b/docs/src/docs/installation.md index d4d8603a..e5398f27 100644 --- a/docs/src/docs/installation.md +++ b/docs/src/docs/installation.md @@ -56,7 +56,7 @@ Now, add `@kreyu/data-table-bundle` controllers to your `assets/controllers.json "personalization": { "enabled": true }, - "persistence": { + "state": { "enabled": true }, "batch": { diff --git a/src/Pagination/PaginationUrlGenerator.php b/src/Pagination/PaginationUrlGenerator.php index c1a7b016..3a11b198 100644 --- a/src/Pagination/PaginationUrlGenerator.php +++ b/src/Pagination/PaginationUrlGenerator.php @@ -4,6 +4,7 @@ namespace Kreyu\Bundle\DataTableBundle\Pagination; +use Kreyu\Bundle\DataTableBundle\DataTableView; use Kreyu\Bundle\DataTableBundle\Exception\LogicException; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\RequestStack; @@ -17,7 +18,7 @@ public function __construct( ) { } - public function generate(PaginationView $paginationView, int $page): string + public function generate(DataTableView $dataTableView, int $page): string { $request = $this->getRequest(); @@ -29,9 +30,9 @@ public function generate(PaginationView $paginationView, int $page): string // Recursively replace/merge with the URL query parameters defined in the data table view. // This allows the user to define custom query parameters that should be preserved when changing pages. - $parameters = array_replace_recursive($parameters, $paginationView->parent->vars['url_query_parameters'] ?? []); + $parameters = array_replace_recursive($parameters, $dataTableView->vars['url_query_parameters'] ?? []); - $parameters[$paginationView->parent->vars['page_parameter_name']] = $page; + $parameters[$dataTableView->vars['page_parameter_name']] = $page; return $this->urlGenerator->generate($route, $parameters); } diff --git a/src/Pagination/PaginationUrlGeneratorInterface.php b/src/Pagination/PaginationUrlGeneratorInterface.php index 72fe0371..93205cee 100644 --- a/src/Pagination/PaginationUrlGeneratorInterface.php +++ b/src/Pagination/PaginationUrlGeneratorInterface.php @@ -4,7 +4,9 @@ namespace Kreyu\Bundle\DataTableBundle\Pagination; +use Kreyu\Bundle\DataTableBundle\DataTableView; + interface PaginationUrlGeneratorInterface { - public function generate(PaginationView $paginationView, int $page): string; + public function generate(DataTableView $dataTableView, int $page): string; } diff --git a/src/Pagination/PaginationView.php b/src/Pagination/PaginationView.php index f6ec96dd..d25a0d82 100755 --- a/src/Pagination/PaginationView.php +++ b/src/Pagination/PaginationView.php @@ -15,7 +15,7 @@ public function __construct( PaginationInterface $pagination, ) { $this->vars = [ - 'pagination' => $this, + 'data_table' => $this->parent, 'page_parameter_name' => $this->parent->vars['page_parameter_name'], 'current_page_number' => $pagination->getCurrentPageNumber(), 'current_page_item_count' => $pagination->getCurrentPageItemCount(), diff --git a/src/Resources/views/themes/base.html.twig b/src/Resources/views/themes/base.html.twig index 10cbf5f3..3ad7a915 100755 --- a/src/Resources/views/themes/base.html.twig +++ b/src/Resources/views/themes/base.html.twig @@ -3,15 +3,16 @@ {# Base HTML Theme #} {% block kreyu_data_table %} - {% set stimulus_controllers = ['kreyu--data-table-bundle--persistence'] %} + {% set stimulus_controllers = ['kreyu--data-table-bundle--state'] %} {% if has_batch_actions %} {% set stimulus_controllers = stimulus_controllers|merge(['kreyu--data-table-bundle--batch']) %} {% endif %} - {{ block('action_bar') }} {{ block('table') }} @@ -23,15 +24,16 @@ {% endblock %} {% block kreyu_data_table_form_aware %} - {% set stimulus_controllers = ['kreyu--data-table-bundle--persistence'] %} + {% set stimulus_controllers = ['kreyu--data-table-bundle--state'] %} {% if has_batch_actions %} {% set stimulus_controllers = stimulus_controllers|merge(['kreyu--data-table-bundle--batch']) %} {% endif %} - {{ block('action_bar') }} @@ -149,11 +151,11 @@ {% block pagination_controls %} {%- if has_previous_page -%} - {% with { path: data_table_pagination_url(pagination, 1) } %} + {% with { path: data_table_pagination_url(data_table, 1) } %} {{ block('pagination_first', theme) }} {% endwith %} - {% with { path: data_table_pagination_url(pagination, current_page_number - 1) } %} + {% with { path: data_table_pagination_url(data_table, current_page_number - 1) } %} {{ block('pagination_previous', theme) }} {% endwith %} {%- else -%} @@ -165,18 +167,18 @@ {% if page_number == current_page_number %} {{ block('pagination_page_active', theme) }} {% else %} - {% with { path: data_table_pagination_url(pagination, page_number) } %} + {% with { path: data_table_pagination_url(data_table, page_number) } %} {{ block('pagination_page', theme) }} {% endwith %} {% endif %} {% endfor %} {%- if has_next_page -%} - {% with { path: data_table_pagination_url(pagination, current_page_number + 1) } %} + {% with { path: data_table_pagination_url(data_table, current_page_number + 1) } %} {{ block('pagination_next', theme) }} {% endwith %} - {% with { path: data_table_pagination_url(pagination, page_count) } %} + {% with { path: data_table_pagination_url(data_table, page_count) } %} {{ block('pagination_last', theme) }} {% endwith %} {%- else -%} diff --git a/src/Twig/DataTableExtension.php b/src/Twig/DataTableExtension.php index db9e3ac6..9b62e44b 100755 --- a/src/Twig/DataTableExtension.php +++ b/src/Twig/DataTableExtension.php @@ -328,9 +328,9 @@ public function generateColumnSortUrl(DataTableView $dataTableView, ColumnHeader return $this->columnSortUrlGenerator->generate($dataTableView, ...$columnHeaderViews); } - public function generatePaginationUrl(PaginationView $paginationView, int $page): string + public function generatePaginationUrl(DataTableView $dataTableView, int $page): string { - return $this->paginationUrlGenerator->generate($paginationView, $page); + return $this->paginationUrlGenerator->generate($dataTableView, $page); } /** diff --git a/src/Type/DataTableType.php b/src/Type/DataTableType.php index 6be1492f..7795f002 100755 --- a/src/Type/DataTableType.php +++ b/src/Type/DataTableType.php @@ -122,24 +122,8 @@ public function buildView(DataTableView $view, DataTableInterface $dataTable, ar 'column_count' => count($view->headerRow), ]); - $urlQueryParameters = []; - if ($dataTable->getConfig()->isFiltrationEnabled()) { $view->vars['filtration_form'] = $this->createFiltrationFormView($view, $dataTable); - - foreach ($view->filters as $filterView) { - if (null === $filterView->data || !$filterView->data->hasValue()) { - continue; - } - - $filterParameter = ['value' => $filterView->data?->getValue()]; - - if ($filterView->vars['operator_selectable']) { - $filterParameter['operator'] = $filterView->data?->getOperator()?->value; - } - - $urlQueryParameters[$dataTable->getConfig()->getFiltrationParameterName()][$filterView->vars['name']] = $filterParameter; - } } if ($dataTable->getConfig()->isPersonalizationEnabled()) { @@ -411,7 +395,7 @@ private function createFormView(FormInterface $form, DataTableView $view, DataTa private function getUrlQueryParameters(DataTableView $view, DataTableInterface $dataTable): array { - $urlQueryParameters = []; + $parameters = []; if ($dataTable->getConfig()->isFiltrationEnabled()) { foreach ($view->filters as $filterView) { @@ -425,21 +409,21 @@ private function getUrlQueryParameters(DataTableView $view, DataTableInterface $ $filterParameter['operator'] = $filterView->data->getOperator()?->value; } - $urlQueryParameters[$dataTable->getConfig()->getFiltrationParameterName()][$filterView->vars['name']] = $filterParameter; + $parameters[$dataTable->getConfig()->getFiltrationParameterName()][$filterView->vars['name']] = $filterParameter; } } if ($dataTable->getConfig()->isPaginationEnabled()) { - $urlQueryParameters[$dataTable->getConfig()->getPageParameterName()] = $view->pagination->vars['current_page_number']; - $urlQueryParameters[$dataTable->getConfig()->getPerPageParameterName()] = $view->pagination->vars['item_number_per_page']; + $parameters[$dataTable->getConfig()->getPageParameterName()] = $view->pagination->vars['current_page_number']; + $parameters[$dataTable->getConfig()->getPerPageParameterName()] = $view->pagination->vars['item_number_per_page']; } if ($dataTable->getConfig()->isSortingEnabled()) { foreach ($view->vars['sorting_data']->getColumns() as $sortingColumnData) { - $urlQueryParameters[$dataTable->getConfig()->getSortParameterName()][$sortingColumnData->getName()] = $sortingColumnData->getDirection(); + $parameters[$dataTable->getConfig()->getSortParameterName()][$sortingColumnData->getName()] = $sortingColumnData->getDirection(); } } - return $urlQueryParameters; + return $parameters; } } diff --git a/tests/Unit/Pagination/PaginationUrlGeneratorTest.php b/tests/Unit/Pagination/PaginationUrlGeneratorTest.php index 1d7c28b8..1e6aeb27 100644 --- a/tests/Unit/Pagination/PaginationUrlGeneratorTest.php +++ b/tests/Unit/Pagination/PaginationUrlGeneratorTest.php @@ -6,7 +6,6 @@ use Kreyu\Bundle\DataTableBundle\DataTableView; use Kreyu\Bundle\DataTableBundle\Pagination\PaginationUrlGenerator; -use Kreyu\Bundle\DataTableBundle\Pagination\PaginationView; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; use Symfony\Component\HttpFoundation\InputBag; @@ -69,7 +68,7 @@ public function testItPreservesDataTableUrlQueryParameters() 'foo' => 'bar', ]); - $this->generate($this->createPaginationViewMock(['foo' => 'bar'])); + $this->generate($this->createDataTableViewMock(['foo' => 'bar'])); } public function testItIncludesGivenPage() @@ -80,7 +79,7 @@ public function testItIncludesGivenPage() self::PAGE_PARAMETER_NAME => 5, ]); - $this->generate($this->createPaginationViewMock([self::PAGE_PARAMETER_NAME => 2]), page: 5); + $this->generate($this->createDataTableViewMock([self::PAGE_PARAMETER_NAME => 2]), page: 5); } public function testItMergesEverythingTogether(): void @@ -95,15 +94,15 @@ public function testItMergesEverythingTogether(): void 'foo' => 'bar', ]); - $this->generate($this->createPaginationViewMock(['foo' => 'bar']), page: 5); + $this->generate($this->createDataTableViewMock(['foo' => 'bar']), page: 5); } - private function generate(?PaginationView $paginationView = null, int $page = 1): void + private function generate(?DataTableView $dataTableView = null, int $page = 1): void { - $paginationView ??= $this->createPaginationViewMock(); + $dataTableView ??= $this->createDataTableViewMock(); $paginationUrlGenerator = new PaginationUrlGenerator($this->requestStack, $this->urlGenerator); - $paginationUrlGenerator->generate($paginationView, $page); + $paginationUrlGenerator->generate($dataTableView, $page); } private function createDataTableViewMock(array $urlQueryParameters = []): MockObject&DataTableView @@ -114,12 +113,4 @@ private function createDataTableViewMock(array $urlQueryParameters = []): MockOb return $dataTableView; } - - private function createPaginationViewMock(array $urlQueryParameters = []): MockObject&PaginationView - { - $paginationView = $this->createMock(PaginationView::class); - $paginationView->parent = $this->createDataTableViewMock($urlQueryParameters); - - return $paginationView; - } }