From 9ffa7a92dd5c7e2605ffc3f01c734328863ffd32 Mon Sep 17 00:00:00 2001 From: bidi Date: Mon, 5 Oct 2026 15:28:42 +0300 Subject: [PATCH 1/2] created page - using simple tables Signed-off-by: bidi --- docs/book/v7/how-to/using-simple-tables.md | 184 ++++++++++++++++++ .../create-book-module-via-dot-maker.md | 34 +++- mkdocs.yml | 1 + 3 files changed, 209 insertions(+), 10 deletions(-) create mode 100644 docs/book/v7/how-to/using-simple-tables.md diff --git a/docs/book/v7/how-to/using-simple-tables.md b/docs/book/v7/how-to/using-simple-tables.md new file mode 100644 index 0000000..5cd6c93 --- /dev/null +++ b/docs/book/v7/how-to/using-simple-tables.md @@ -0,0 +1,184 @@ +# Use simple tables + +## Summary + +This page explains how to build a list page as a simple table, with sortable columns, row selection and column visibility that is remembered for each admin. + +## Details + +Simple tables are used by the list pages of Dotkernel Admin, such as the lists of admins, admin logins and users. +Each table is made of these parts: + +- a Twig template that renders the table, using the `sortableColumn` macro from `@partial/macros.html.twig` +- the `table_settings.js` script, which shows or hides columns and enables the edit and delete buttons +- the `Setting` entity, which stores the selected columns for each admin and table +- two routes that read and write the setting: `setting::view-setting` (`GET /{identifier}`) and `setting::store-setting` (`POST /{identifier}`) + +When the page loads, `table_settings.js` requests the saved column selection, applies it, then displays the table. +Whenever the admin toggles a column, the script saves the new selection. +If no selection was saved yet, all columns are displayed. + +The examples below build a list page for books. +For a complete module, see [Creating a book module using DotMaker](../tutorials/create-book-module-via-dot-maker.md). + +## Create a simple table + +Creating a simple table requires four steps: + +- add a setting identifier +- send the identifier to the template +- render the table +- load the scripts + +### Add a setting identifier + +Each table stores its selected columns under its own identifier. +Open `src/Core/src/Setting/src/Enum/SettingIdentifierEnum.php` and add a new case: + +```php +case IdentifierTableBookListSelectedColumns = 'table_book_list_selected_columns'; +``` + +The `identifier` column of the `settings` table is a database `ENUM`, so the new case requires a migration. +Generate it, check that it alters the `settings.identifier` column, then run it: + +```shell +php ./vendor/bin/doctrine-migrations diff +php ./vendor/bin/doctrine-migrations migrate +``` + +See [Create Database Migrations](creating-migrations.md) for more details. + +### Send the identifier to the template + +In the handler that renders the list, send the identifier to the template: + +```php +return new HtmlResponse( + $this->template->render('book::list-book', [ + 'pagination' => $this->bookService->getBooks($request->getQueryParams()), + 'identifier' => SettingIdentifierEnum::IdentifierTableBookListSelectedColumns->value, + ]) +); +``` + +### Render the table + +In the template, import the macro and render the column selector and the table: + +```html +{% from '@partial/macros.html.twig' import sortableColumn %} + + + + + + + + + + + + + {% for book in pagination.items %} + + + + + + {% endfor %} + + +``` + +Things to note: + +- the table is hidden with `style="display: none;"`, the script displays it after applying the saved selection +- the `#column-selector` list is empty, the script fills it with one checkbox for each sortable column +- the first column holds the row checkbox and has no `sortableColumn`, so it cannot be hidden + +### Load the scripts + +At the end of the template, define the three variables that `table_settings.js` requires, then load the script: + +```html +{% block javascript %} +{{ parent() }} + + +{% endblock %} +``` + +If any of the three variables is missing, the script logs an error in the browser console and does nothing. +Any page specific script, such as `book.js`, is loaded the same way, using `type="module"`. +It must be registered in the `entries` object of `vite.config.js` and built, see [Use NPM Commands](npm_commands.md). + +## Name the columns + +The `sortableColumn` macro receives the sort key as its fourth argument. +It adds the `table-column` class to the header link and sets its `data-column` attribute to the sort key, with dots replaced by dashes. +The script hides and shows a column by the class `column-`, so every `` and `` of that column must use the same class: + +| Sort key | `data-column` | Class | +| --- | --- | --- | +| `user.identity` | `user-identity` | `column-user-identity` | +| `detail.firstName` | `detail-firstName` | `column-detail-firstName` | +| `book.release-date` | `book-release-date` | `column-book-release-date` | + +## Edit and delete buttons + +`table_settings.js` also handles row selection: + +- clicking a row (`.table-row`) toggles its checkbox +- the buttons with the ids `btn-edit-resource` and `btn-delete-resource` are enabled only while exactly one `.ui-checkbox` is checked + +The URLs of the selected row are read from the `data-edit-url` and `data-delete-url` attributes of the checked box. +Opening the modals that use them is done by the page specific script, see the `_book.js` file in [Creating a book module using DotMaker](../tutorials/create-book-module-via-dot-maker.md). + +## FAQ + +**Q: Why is a column always displayed, even if I untick it in the column selector?** + +A: The `` or `` class does not match the column's `data-column` value. +Check that every cell of that column uses `column-` followed by the sort key with dots replaced by dashes. + +**Q: Why is the column selector empty?** + +A: The selector is built from the elements with the `table-column` class. +Make sure the headers use the `sortableColumn` macro and that the template contains an empty `
    `. + +**Q: Why is the table not displayed at all?** + +A: The table stays hidden until `table_settings.js` runs. +Check the browser console for errors about `tableId`, `storeSettingsUrl` or `getSettingsUrl`, and make sure the script is loaded with `type="module"`. + +**Q: Why are my column selections not saved?** + +A: The identifier is probably not a case of `SettingIdentifierEnum`, or the migration that adds it to the `settings.identifier` column was not run. +In both cases, the setting routes respond with an error, which you can see in the browser's network tab. + +**Q: Why are all columns displayed again after a reload?** + +A: When the saved selection is empty, all columns are displayed. +This is also the state of an admin that never changed the selection for that table. diff --git a/docs/book/v7/tutorials/create-book-module-via-dot-maker.md b/docs/book/v7/tutorials/create-book-module-via-dot-maker.md index 81f3125..5c47aee 100644 --- a/docs/book/v7/tutorials/create-book-module-via-dot-maker.md +++ b/docs/book/v7/tutorials/create-book-module-via-dot-maker.md @@ -133,15 +133,27 @@ The next step is filling in the required logic for the proposed flow of this mod While `dot-maker` does also include common logic in the relevant files, the tutorial adds custom functionality. As such, the following section will go over the files that require changes. +* `src/Core/src/Setting/src/Enum/SettingIdentifierEnum.php` + +Each table stores its selected columns under its own setting identifier. +Add a new case for the books table, so it does not share its column selection with another table: + +```php +case IdentifierTableBookListSelectedColumns = 'table_book_list_selected_columns'; +``` + +The `identifier` column of the `settings` table is a database `ENUM`, so adding a case also requires a migration, which is generated in the [Migrations](#migrations) section. +For more details about how the column selection works, see [Use simple tables](../how-to/using-simple-tables.md). + * `src/Book/src/Handler/GetListBookHandler.php` -The overall class structure is fully generated, but for this tutorial you will need to send the `indentifier` key to the template, as shown below: +The overall class structure is fully generated, but for this tutorial you will need to send the `identifier` key to the template, as shown below: ```php return new HtmlResponse( - $this->template->render('book::book-list', [ + $this->template->render('book::list-book', [ 'pagination' => $this->bookService->getBooks($request->getQueryParams()), - 'identifier' => SettingIdentifierEnum::IdentifierTableUserListSelectedColumns->value, + 'identifier' => SettingIdentifierEnum::IdentifierTableBookListSelectedColumns->value, ]) ); ``` @@ -529,11 +541,13 @@ $this->add($releaseDateInput); * `src/App/assets/js/components/_book.js` -As the listing pages make use of JavaScript, you will need to manually create your module specific `_book.js` file and register it in `webpack.config.js` for building. +As the listing pages make use of JavaScript, you will need to manually create your module specific `_book.js` file and register it in `vite.config.js` for building. You may copy this sample `_book.js` file to the `src/App/assets/js/components/` directory: ```js +import $ from 'jquery'; + $(document).ready(() => { const request = async(url, options = {}) => { try { @@ -651,12 +665,10 @@ $(document).ready(() => { }); ``` -Next you have to register the file in the `entries` array of `webpack.config.js` by adding the following key: +Next you have to register the file in the `entries` object of `vite.config.js` by adding the following key: ```js -book: [ - './App/assets/js/components/_book.js' -] +book: `${assetsPath}/js/components/_book.js`, ``` To make use of the newly added scripts, make sure to build your assets by running the command: @@ -816,8 +828,8 @@ For this tutorial you may copy the following default page layout in the `list-bo const storeSettingsUrl = '{{ path('setting::store-setting', {identifier: identifier}) }}'; const getSettingsUrl = '{{ path('setting::view-setting', {identifier: identifier}) }}'; - - + + {% endblock %} ``` @@ -945,6 +957,8 @@ php ./vendor/bin/doctrine-migrations diff ``` This will check for differences between your entities and database structure and create migration files if necessary, in `src/Core/src/App/src/Migration`. +Besides creating the `book` table, the generated migration should also alter the `identifier` column of the `settings` table, to include the new `table_book_list_selected_columns` value. +Check the generated file before running it. To execute the migrations, run: diff --git a/mkdocs.yml b/mkdocs.yml index 6f6c70b..a4b80c7 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -34,6 +34,7 @@ nav: - "Use NPM Commands": v7/how-to/npm_commands.md - "Inject Dependencies": v7/how-to/dependency-injection.md - "Set Up CSRF": v7/how-to/csrf.md + - "Use Simple Tables": v7/how-to/using-simple-tables.md - Security: - "Basic Security": v7/security/basic-security.md - "Two-Factor Authentication with Time-based One-Time Password": v7/security/2fa-with-totp.md From 5378b155e52fc84c55d9819f5033d601deed749b Mon Sep 17 00:00:00 2001 From: bidi Date: Mon, 5 Oct 2026 18:39:45 +0300 Subject: [PATCH 2/2] updated pages - create book, simple tables Signed-off-by: bidi --- docs/book/v7/how-to/using-simple-tables.md | 103 +++++++++++------- .../create-book-module-via-dot-maker.md | 32 ++++-- 2 files changed, 83 insertions(+), 52 deletions(-) diff --git a/docs/book/v7/how-to/using-simple-tables.md b/docs/book/v7/how-to/using-simple-tables.md index 5cd6c93..2a87ba2 100644 --- a/docs/book/v7/how-to/using-simple-tables.md +++ b/docs/book/v7/how-to/using-simple-tables.md @@ -64,48 +64,69 @@ return new HtmlResponse( ### Render the table -In the template, import the macro and render the column selector and the table: +In the template, import the macro and render the column selector and the table. +The example below shows only the basic listing. +The full template, with the add, edit and delete buttons and modals, pagination and the remaining columns, is in [Creating a book module using DotMaker](../tutorials/create-book-module-via-dot-maker.md). ```html {% from '@partial/macros.html.twig' import sortableColumn %} -