Table
Here you can find all for tables
namespace Patrikjak\Utils\Table;
Simple table
Simple table without pagination
Name | Link | Location | Created at | Role | |
---|---|---|---|---|---|
John Doe | john.doe@example.com |
New York
USA | 2021-01-01 12:00:00 | Admin | |
Jane Doe | jane.doe@example.com |
Los Angeles
USA | 2021-01-02 12:00:00 | User | |
John Smith | john.smith@example.com |
London
UK | 2021-01-03 12:00:00 | User | |
Jane Smith | jane.smith@example.com |
Paris
France | 2021-01-04 12:00:00 | User |
You will need to create table provider and pass the data to table
TablesController.php
use App\Services\SimpleTableProvider;use Illuminate\View\View; class PageController{ public function index(SimpleTableProvider $simpleTableProvider): View { return view('index', [ 'simpleTable' => $simpleTableProvider->getTable(), ]); }}
index.blade.php
<x-pjutils.table::table :table="$simpleTable" />
SimpleTableProvider.php
<?php namespace App\Services; use Patrikjak\Utils\Common\Enums\Type;use Patrikjak\Utils\Table\Services\BaseTableProvider;use Patrikjak\Utils\Table\Factories\Cells\CellFactory;use Patrikjak\Utils\Table\Services\TableProviderInterface; final class SimpleTableProvider extends BaseTableProvider implements TableProviderInterface{ /** * @inheritDoc */ public function getHeader(): ?array { return [ 'name' => 'Name', 'email' => 'E-mail', 'link' => 'Link', 'location' => 'Location', 'created_at' => 'Created at', 'role' => 'Role', ]; } /** * @inheritDoc */ public function getData(): array { return [ [ 'id' => 1, 'name' => CellFactory::simple('John Doe'), 'email' => CellFactory::simple('john.doe@example.com'), 'link' => CellFactory::link('Google', 'https://google.com'), 'location' => CellFactory::double('New York', 'USA'), 'created_at' => CellFactory::simple('2021-01-01 12:00:00'), 'role' => CellFactory::chip('Admin', Type::SUCCESS), ], [ 'id' => 2, 'name' => CellFactory::simple('Jane Doe'), 'email' => CellFactory::simple('jane.doe@example.com'), 'link' => CellFactory::link('Google', 'https://google.com'), 'location' => CellFactory::double('Los Angeles', 'USA'), 'created_at' => CellFactory::simple('2021-01-02 12:00:00'), 'role' => CellFactory::chip('User', Type::DANGER), ], [ 'id' => 3, 'name' => CellFactory::simple('John Smith'), 'email' => CellFactory::simple('john.smith@example.com'), 'link' => CellFactory::link('Google', 'https://google.com'), 'location' => CellFactory::double('London', 'UK'), 'created_at' => CellFactory::simple('2021-01-03 12:00:00'), 'role' => CellFactory::chip('User', Type::INFO), ], [ 'id' => 4, 'name' => CellFactory::simple('Jane Smith'), 'email' => CellFactory::simple('jane.smith@example.com'), 'link' => CellFactory::link('Google', 'https://google.com'), 'location' => CellFactory::double('Paris', 'France'), 'created_at' => CellFactory::simple('2021-01-04 12:00:00'), 'role' => CellFactory::chip('User', Type::WARNING), ], ]; }}
Paginated table
# | Name | Location | Role | ||
---|---|---|---|---|---|
1 | Esta Heathcote | mherzog@example.org |
North Katrina
Bosnia and Herzegovina | User |
|
2 | Karlie Shanahan | crohan@example.net |
Myronstad
Algeria | User |
|
3 | Jaycee Hahn | murray.zella@example.com |
East Shanelfort
Denmark | User |
|
4 | Kevon Goodwin | raphaelle84@example.net |
Jadashire
Paraguay | User |
|
5 | Mr. Clovis Olson | lupe06@example.org |
New Moisesland
Bosnia and Herzegovina | User |
|
6 | Stefanie Christiansen | lottie33@example.com |
South Rossfurt
Slovakia (Slovak Republic) | User |
|
7 | Sofia Upton III | johns.vance@example.net |
South Rossfurt
Slovakia (Slovak Republic) | User |
|
8 | Lacey Emard II | ruth.okon@example.com |
Lake Effieside
Cayman Islands | User |
|
9 | Loyce Hoeger | aupton@example.com |
East Dagmar
Dominica | User |
|
10 | Hermina Rosenbaum | shintz@example.net |
East Dudleymouth
Vanuatu | User |
|
Same as simple table, but you need to pass Parameters to table provider
TablesController.php
use App\Services\PaginatedTableProvider;use Illuminate\View\View;use Patrikjak\Utils\Table\Http\Requests\TableParametersRequest; class TablesController{ public function index(PaginatedTableProvider $paginatedTableProvider, TableParametersRequest $request): View { return view('index', [ 'paginatedTable' => $paginatedTableProvider->getTable( $request->getTableParameters($paginatedTableProvider->getTableId()), ), ]); }}
api.php
You need to add route for table parts - ajax request for table body and pagination
Middlewares are required for cookies
use App\Http\Controllers\Api\PageController;use Illuminate\Support\Facades\Route; Route::prefix('api') ->middleware(['api', AddQueuedCookiesToResponse::class, EncryptCookies::class]) ->name('api.') ->group(static function () { Route::get('/table-parts', [PageController::class, 'tableParts'])->name('table-parts'); });
Api/TablesController.php
You need to create controller for table parts
use App\Http\Controllers\Controller;use App\Services\PaginatedTableProvider;use Illuminate\Http\JsonResponse;use Patrikjak\Utils\Table\Http\Requests\TableParametersRequest; class TablesController extends Controller{ public function tableParts( PaginatedTableProvider $paginatedTableProvider, TableParametersRequest $request, ): JsonResponse { return new JsonResponse($paginatedTableProvider->getHtmlParts( $request->getTableParameters($paginatedTableProvider->getTableId())), ); }}
index.blade.php
<x-pjutils.table::table :table="$paginatedTable" />
SimpleTableProvider.php
When you are using LengthAwarePaginator you need to set path for links in paginator - take a look in getPaginator method
<?php namespace App\Services; use App\Models\User;use Patrikjak\Utils\Common\Enums\Icon;use Patrikjak\Utils\Common\Enums\Type;use Patrikjak\Utils\Table\Dto\Cells\Actions\Item;use Patrikjak\Utils\Table\Dto\Pagination\Paginator;use Patrikjak\Utils\Table\Factories\Cells\CellFactory;use Patrikjak\Utils\Table\Factories\Pagination\PaginatorFactory;use Patrikjak\Utils\Table\Services\BasePaginatedTableProvider;use Patrikjak\Utils\Table\Services\TableProviderInterface; class PaginatedTableProvider extends BasePaginatedTableProvider implements TableProviderInterface{ public function getTableId(): string { return 'paginated-table'; } /** * @inheritDoc */ public function getHeader(): ?array { return [ 'name' => 'Name', 'email' => 'E-mail', 'location' => 'Location', 'created_at' => 'Created at', 'role' => 'Role', ]; } /** * @inheritDoc */ public function getData(): array { return $this->getPageData()->map(static function (User $user) { [$city, $country] = explode(',', $user->location); return [ 'id' => $user->id, 'name' => CellFactory::simple($user->name), 'email' => CellFactory::simple($user->email), 'location' => CellFactory::double($city, $country), 'created_at' => CellFactory::simple($user->created_at->format('d/m/Y')), 'role' => CellFactory::chip(ucfirst($user->role), match ($user->role) { 'admin' => Type::SUCCESS, 'sub-admin' => Type::WARNING, default => Type::INFO, }), ]; })->toArray(); } public function getPaginator(): Paginator { return PaginatorFactory::createFromLengthAwarePaginator( User::paginate($this->getPageSize(), page: $this->getCurrentPage()) ->withPath(route('api.table-parts')), ); } /** * Here you can define the columns that will be displayed in the table. * By default, all columns from the header are displayed. * @inheritDoc */ public function getColumns(): array { return [ 'name', 'email', 'location', 'role', ]; } /** * You can define if row order should be displayed. */ public function showOrder(): bool { return true; } /** * @inheritDoc */ public function getActions(): array { return [ new Item('Edit', 'edit', Icon::EDIT, visible: function (array $row): bool { return $row['id'] !== 1; }), new Item('Delete', 'delete', Icon::TRASH, type: Type::DANGER, visible: function (array $row): bool { return !in_array($row['id'], [1, 2], true); }), new Item('Link', 'link', href: 'https://google.com'), new Item('Dynamic link', 'dynamic-link', href: function (array $row): string { return 'some-link/' . $row['id']; }), new Item( 'With different method', 'different-method', href: route('api.dummy-action-with-different-method'), method: 'POST', ), ]; }}
main.js
You need to bind table functions with pagination in your main.js file. All dropdowns and all tables will be bound after calling these 2 functions
import {bindTableFunctions} from "./vendor/pjutils/table/table";import {bindDropdowns} from "./vendor/pjutils/utils/dropdown"; bindTableFunctions();bindDropdowns();
tables.ts
Here you can find out how to set callback for actions
import {doAction} from "./vendor/pjutils/table/actions"; const table: HTMLElement = document.querySelector('.pj-table-wrapper#paginated-table'); if (table) { doAction(table, 'edit', (rowId: string) => { console.log('Editing row:', rowId); });}
Table with bulk actions
You need to just define the bulk actions and show checkboxes and the table will be ready to use. You can define the bulk actions in the table provider.
Data you will receive from bulk actions are table rows ids.
|
# | Name | Location | Role | ||
---|---|---|---|---|---|---|
|
1 | Esta Heathcote | mherzog@example.org |
North Katrina
Bosnia and Herzegovina | User |
|
|
2 | Karlie Shanahan | crohan@example.net |
Myronstad
Algeria | User |
|
|
3 | Jaycee Hahn | murray.zella@example.com |
East Shanelfort
Denmark | User |
|
|
4 | Kevon Goodwin | raphaelle84@example.net |
Jadashire
Paraguay | User |
|
|
5 | Mr. Clovis Olson | lupe06@example.org |
New Moisesland
Bosnia and Herzegovina | User |
|
|
6 | Stefanie Christiansen | lottie33@example.com |
South Rossfurt
Slovakia (Slovak Republic) | User |
|
|
7 | Sofia Upton III | johns.vance@example.net |
South Rossfurt
Slovakia (Slovak Republic) | User |
|
|
8 | Lacey Emard II | ruth.okon@example.com |
Lake Effieside
Cayman Islands | User |
|
|
9 | Loyce Hoeger | aupton@example.com |
East Dagmar
Dominica | User |
|
|
10 | Hermina Rosenbaum | shintz@example.net |
East Dudleymouth
Vanuatu | User |
|
We will use same data but we need to show checkboxes to the table
BulkActionsTableProvider.php
<?php namespace App\Services; use App\Models\User;use Patrikjak\Utils\Common\Enums\Icon;use Patrikjak\Utils\Common\Enums\Type;use Patrikjak\Utils\Table\Dto\BulkActions\Item;use Patrikjak\Utils\Table\Dto\Pagination\Paginator;use Patrikjak\Utils\Table\Factories\Pagination\PaginatorFactory; final class BulkActionsTableProvider extends PaginatedTableProvider{ public function getTableId(): string { return 'bulk-actions-table'; } public function getPaginator(): Paginator { return PaginatorFactory::createFromLengthAwarePaginator( User::paginate($this->getPageSize(), page: $this->getCurrentPage()) ->withPath(route('api.bulk-actions-table-parts')), ); } public function showCheckboxes(): bool { return true; } public function getBulkActions(): array { return [ new Item('Export', route('api.dummy-action')), new Item('Change role', route('api.dummy-action'), icon: Icon::EDIT), new Item('Delete', route('api.dummy-action'), icon: Icon::TRASH, type: Type::DANGER), ]; }}
There is prepared trait to get the selected rows ids. You can use it in your request class.
<?php namespace App\Http\Requests; use Illuminate\Foundation\Http\FormRequest;use Patrikjak\Utils\Table\Http\Requests\Traits\HandlesBulkActionsIds; class TestRequest extends FormRequest{ use HandlesBulkActionsIds;}
After that you can use the getBulkActionsIds method to get all selected ids
public function testAction(DummyRequest $request): JsonResponse{ return new JsonResponse(['message' => sprintf( 'Action performed successfully with ids: %s', $request->getBulkActionsIds()->implode(', '), )]);}
Sort and filter table
# | Name | Location | Company | Created at |
---|---|---|---|---|
1 | Esta Heathcote |
North Katrina
Bosnia and Herzegovina | Kuhlman-Tromp | 01/12/2024 |
2 | Sofia Upton III |
South Rossfurt
Slovakia (Slovak Republic) | Kuhlman-Tromp | 02/12/2024 |
3 | Lacey Emard II |
Lake Effieside
Cayman Islands | Kuhlman-Tromp | 02/12/2024 |
4 | Loyce Hoeger |
East Dagmar
Dominica | Kuhlman-Tromp | 02/12/2024 |
5 | Hermina Rosenbaum |
East Dudleymouth
Vanuatu | Kuhlman-Tromp | 02/12/2024 |
6 | Dr. Donato Grady DVM |
West Kira
Yemen | Kuhlman-Tromp | 02/12/2024 |
7 | Bryana Waelchi |
West Nya
Costa Rica | Kuhlman-Tromp | 02/12/2024 |
8 | Prof. Aglae Jenkins I |
Brekkeville
French Guiana | Kuhlman-Tromp | 02/12/2024 |
9 | Gregory O'Keefe |
New Jazmin
Thailand | Kuhlman-Tromp | 02/12/2024 |
10 | Frederique Bogan |
New Rupert
Sudan | Kuhlman-Tromp | 02/12/2024 |
Sort
To enable sorting functionality in your table, you need to define the getSortableColumns method. This method specifies which columns can be used for sorting.
Defining Sortable Columns
The getSortableColumns method should return an array of Patrikjak\Utils\Table\Dto\Sort\SortableColumn definitions
The parameters for each column definition are:
- label - The label displayed to users.
- column - The actual column name used for sorting.
It is possible to include columns that are not directly visible in the table but are still sortable.
Retrieving Sort Criteria
To retrieve the sorting definition, use the getSortCriteria method, which is defined in BaseTableProvider.
These definitions can be used in methods responsible for fetching table data.
Applying Sorting to Queries
You can apply sorting to your queries using the applySort method from Patrikjak\Utils\Common\Services\QueryBuilder\SortService
Example Usage:
$this->sortService->applySort($query, $this->getSortCriteria(), $this->getColumnsMask());
In this example, the getColumnsMask method is also used. This method returns an array where:
- The values are the masked column values.
- The keys are the actual table columns that will be used for filtering.
By using applySort, you can dynamically add sorting to your queries, ensuring flexible and user-friendly table sorting functionality.
Filter
Filtering functionality is also available in the table. To enable filtering, you need to define the getFilterableColumns method.
Defining Filterable Columns
The getFilterableColumns method should return an array of Patrikjak\Utils\Table\Dto\Filter\Definitions\FilterableColumn definitions
The parameters for each column definition are:
- label - The label displayed to users.
- column - The actual column name used for filtering.
- type - The type of filter. Available types are:
- text - A simple text filter. You can use the text filter to filter data by text (contains, not contains, equals, not equals, starts with, ends with).
- select - A select filter. You can use the select filter with the select options filter. Data are loaded from custom endpoint.
- date - A date filter. You can use the date filter with the date range filter.
- number - A number filter. You can use the number filter with the number range filter.
To create type for filter definition, use Patrikjak\Utils\Table\Factories\Filter\FilterableFactory
FilterableFactory::text(); // text filter - without any additional parametersFilterableFactory::select($dataEndpoint); // select filter - you need to pass the data endpointFilterableFactory::date(); // date filter - you can pass from and to valueFilterableFactory::number(); // number filter - you can pass the min and max value
Note: min and max values are optional and their usage is to set the min and max attribute for the input fields. If you need restrictions, you need to handle them in the backend.
Select data endpoint
To load data for the select filter, you need to create a custom endpoint that returns JsonResponse with data of Patrikjak\Utils\Table\Dto\Filter\Definitions\Select\SelectFilterOptions
You can use Patrikjak\Utils\Table\Factories\Filter\SelectFilterOptionsFactory factory to create it from array
public function companyFilterOptions(): JsonResponse{ $data = Company::all('name')->flatMap(function (Company $company) { return [$company->name => $company->name]; })->toArray(); return new JsonResponse(SelectFilterOptionsFactory::createFromArray($data));}
Retrieving Filter Criteria
To retrieve the filter definition, use the getFilterCriteria method, which is defined in BaseTableProvider.
Applying Filtering to Queries
You can apply filtering to your queries using the applyFilter method from Patrikjak\Utils\Common\Services\QueryBuilder\FilterService
Example usage:
$this->filterService->applyFilter($query, $this->getFilterCriteria(), $this->getColumnsMask());
By using applyFilter, you can dynamically add filtering to your queries, ensuring flexible and user-friendly table filtering functionality.
You can see TableProvider with sort and filter here:
<?php namespace App\Services; use App\Models\User;use Illuminate\Support\Carbon;use Patrikjak\Utils\Common\Services\QueryBuilder\FilterService;use Patrikjak\Utils\Common\Services\QueryBuilder\PaginatorService;use Patrikjak\Utils\Common\Services\QueryBuilder\SortService;use Patrikjak\Utils\Table\Dto\Filter\Definitions\FilterableColumn;use Patrikjak\Utils\Table\Factories\Filter\FilterableFactory;use Patrikjak\Utils\Table\Dto\Pagination\Paginator;use Patrikjak\Utils\Table\Dto\Sort\SortableColumn;use Patrikjak\Utils\Table\Factories\Cells\CellFactory;use Patrikjak\Utils\Table\Factories\Pagination\PaginatorFactory;use Patrikjak\Utils\Table\Services\BasePaginatedTableProvider;use Patrikjak\Utils\Table\Services\Sortable;use Patrikjak\Utils\Table\Services\TableProviderInterface; class SortAndFilterTableProvider extends BasePaginatedTableProvider implements TableProviderInterface, Sortable{ public function __construct( private readonly FilterService $filterService, private readonly SortService $sortService, private readonly PaginatorService $paginatorService, ) { } public function getTableId(): string { return 'sort-and-filter-table'; } /** * @inheritDoc */ public function getHeader(): ?array { return [ 'name' => 'Name', 'location' => 'Location', 'company' => 'Company', 'created_at' => 'Created at', ]; } /** * @inheritDoc */ public function getData(): array { return $this->getPageData()->map(static function (User $user) { [$city, $country] = explode(',', $user->location); return [ 'id' => $user->id, 'name' => CellFactory::simple($user->name), 'location' => CellFactory::double($city, $country), 'company' => CellFactory::simple($user->company_name), 'created_at' => CellFactory::simple($user->created_at->format('d/m/Y')), ]; })->toArray(); } public function getPaginator(): Paginator { $query = User::select('users.*', 'c.name AS company_name') ->join('companies AS c', 'users.company_id', '=', 'c.id'); $this->filterService->applyFilter($query, $this->getFilterCriteria(), $this->getColumnsMask()); $this->sortService->applySort($query, $this->getSortCriteria(), $this->getColumnsMask()); $paginatedData = $this->paginatorService->paginate( $query, $this->getCurrentPage(), $this->getPageSize(), route('api.sort-and-filter-table-parts'), ); return PaginatorFactory::createFromLengthAwarePaginator($paginatedData); } public function showOrder(): bool { return true; } /** * Here you can define columns that can be sorted. * @inheritDoc */ public function getSortableColumns(): array { $maskedColumns = $this->getColumnsMask(); return [ new SortableColumn('Name', $maskedColumns['users.name']), new SortableColumn('Location', $maskedColumns['users.location']), new SortableColumn('Company', $maskedColumns['c.name']), new SortableColumn('Created at', $maskedColumns['users.created_at']), new SortableColumn('Company email', $maskedColumns['c.email']), ]; } /** * Here you can define columns that can be filtered. * @inheritDoc */ public function getFilterableColumns(): array { $maskedColumns = $this->getColumnsMask(); return [ new FilterableColumn('Name', $maskedColumns['users.name'], FilterableFactory::text()), new FilterableColumn('Location', $maskedColumns['users.location'], FilterableFactory::text()), new FilterableColumn( 'Company', $maskedColumns['c.name'], FilterableFactory::select(route('api.company-filter-options')), ), new FilterableColumn( 'Created at', $maskedColumns['users.created_at'], FilterableFactory::date(Carbon::make('2024-12-01')), ), new FilterableColumn('Company email', $maskedColumns['c.email'], FilterableFactory::text()), new FilterableColumn( 'Random number', $maskedColumns['random_number'], FilterableFactory::number(max: 100), ), ]; } /** * @return array<string, string> */ private function getColumnsMask(): array { return [ 'users.name' => 'name', 'users.location' => 'location', 'c.name' => 'company', 'users.created_at' => 'created_at', 'c.email' => 'email', 'random_number' => 'number', ]; }}
How does it work?
The BaseTableProvider class is an abstract class that provides the base functionality for table providers. It defines methods for retrieving table data, headers, columns, and other table-related properties.
Base Table Provider
getHeader()
Returns the header of the table as an array. This method must be implemented by subclasses.
abstract public function getHeader(): ?array;
getData()
Returns the data of the table as an array. This method must be implemented by subclasses.
abstract public function getData(): array;
If you want to set custom class for row you need to set rowClass key to data array
public function getData(): array{ return [ [ 'id' => 1, 'name' => CellFactory::simple('John Doe'), 'email' => CellFactory::simple('john.doe@example.com'), 'rowClass' => 'custom-class', ], ];}
getTable()
Returns a Table object based on the provided parameters. It sets the internal parameters and constructs the table with various properties.
public function getTable(?Parameters $parameters = null): Table;
getColumns()
Returns the columns of the table as an array of column names. It derives the columns from the header.
public function getColumns(): array;
getTableId()
Returns the ID of the table. The default implementation returns table.
public function getTableId(): string;
getRowId()
Returns the ID of the row. You need to pass key of provided data. The default implementation returns id.
public function getRowId(): string;
showOrder()
Indicates whether the table should show order. The default implementation returns false.
public function showOrder(): bool;
showCheckboxes()
Indicates whether the table should show checkboxes. The default implementation returns false.
public function showCheckboxes(): bool;
getActions()
Returns the actions for the table. The default implementation returns an empty array.
public function getActions(): array;
You can set actions to be visible only for certain rows. You can pass callable or bool to show/hide action for row. In callback you will get array of page data - from getData() method
new Item('Hidden for some rows', 'dynamic', visible: static function (array $row): bool { $rowId = $row['id']; assert($rowId instanceof Simple); return $rowId->value !== '1';}),
There is option to set href and method. You can pass closure as href and you will get row data. When method is not set, it will redirect you to passed href. When method is set, it will do request for you to given href. After request, table is refreshed.
When request is done, notification is showed, you can pass message, title, level in response to set notification content and level.
return new JsonResponse([ 'title' => 'Success', 'message' => 'Action performed successfully', 'level' => 'success',]);
Paginated Table Provider
The BasePaginatedTableProvider class extends the BaseTableProvider class and implements the TableProviderInterface and SupportsPagination interfaces. It provides additional functionality for handling paginated tables.
Methods
getPageSize
Returns the page size for the table. The default implementation from request returns 10
protected function getPageSize(): int;
getTable()
You need to pass Parameters for BasePaginatedTableProvider
public function getTable(?Parameters $parameters = null): Table;
getHtmlParts()
Returns an array of HTML parts for the table, including the body and pagination. It sets the internal table property and generates the HTML for the body and pagination.
public function getHtmlParts(Parameters $parameters): array;
getPaginationSettings()
Returns the pagination settings for the table. It constructs a Settings object with the current pagination parameters.
public function getPaginationSettings(): Settings;
getPageSizeOptions()
Returns an array of page size options for the table.
protected function getPageSizeOptions(): array;// default implementation// return [10 => 10, 20 => 20, 50 => 50, 100 => 100];
getPageData()
Returns the data for the current page. It initializes the paginator and retrieves the paginated data.
protected function getPageData(): Collection;
getBodyHTML()
Returns the HTML for the table body. It uses Blade to render the Body component with the current table.
protected function getBodyHTML(): string;
getPaginationHTML()
Returns the HTML for the table pagination. It uses Blade to render the Paginator component with the current pagination settings.
protected function getPaginationHTML(): string;