Table

Here you can find all for tables

        
namespace Patrikjak\Utils\Table;

Simple table

Simple table without pagination

Name E-mail Link Location Created at Role
John Doe New York
USA
2021-01-01 12:00:00 Admin
Jane Doe Los Angeles
USA
2021-01-02 12:00:00 User
John Smith London
UK
2021-01-03 12:00:00 User
Jane Smith 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

Edit

Delete

With different method

# Name E-mail Location Role
1 Esta Heathcote North Katrina
Bosnia and Herzegovina
User
2 Karlie Shanahan Myronstad
Algeria
User
3 Jaycee Hahn East Shanelfort
Denmark
User
4 Kevon Goodwin Jadashire
Paraguay
User
5 Mr. Clovis Olson New Moisesland
Bosnia and Herzegovina
User
6 Stefanie Christiansen South Rossfurt
Slovakia (Slovak Republic)
User
7 Sofia Upton III South Rossfurt
Slovakia (Slovak Republic)
User
8 Lacey Emard II Lake Effieside
Cayman Islands
User
9 Loyce Hoeger East Dagmar
Dominica
User
10 Hermina Rosenbaum 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.

Edit

Delete

With different method

# Name E-mail Location Role
1 Esta Heathcote North Katrina
Bosnia and Herzegovina
User
2 Karlie Shanahan Myronstad
Algeria
User
3 Jaycee Hahn East Shanelfort
Denmark
User
4 Kevon Goodwin Jadashire
Paraguay
User
5 Mr. Clovis Olson New Moisesland
Bosnia and Herzegovina
User
6 Stefanie Christiansen South Rossfurt
Slovakia (Slovak Republic)
User
7 Sofia Upton III South Rossfurt
Slovakia (Slovak Republic)
User
8 Lacey Emard II Lake Effieside
Cayman Islands
User
9 Loyce Hoeger East Dagmar
Dominica
User
10 Hermina Rosenbaum 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

Sort
Sort by
Name
Location
Company
Created at
Company email
Filter
Filter by
Name
Location
Company
Created at
Company email
Random number
# 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:

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:

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:

To create type for filter definition, use Patrikjak\Utils\Table\Factories\Filter\FilterableFactory

    
FilterableFactory::text(); // text filter - without any additional parameters
FilterableFactory::select($dataEndpoint); // select filter - you need to pass the data endpoint
FilterableFactory::date(); // date filter - you can pass from and to value
FilterableFactory::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;