tobento / service-table
Building HTML tables easily.
Requires
- php: >=8.0
Requires (Dev)
- phpunit/phpunit: ^9.5
- vimeo/psalm: ^4.0
README
The Table Service provides a way for building tables easily.
Table of Contents
Getting started
Add the latest version of the Table service project running this command.
composer require tobento/service-table
Requirements
- PHP 8.0 or greater
Highlights
- Framework-agnostic, will work with any project
- Decoupled design
- Customize rendering
Screenshots
Simple Example
use Tobento\Service\Table\Table; $table = new Table('products'); $table->row([ 'sku' => 'Sku', 'title' => 'Title', 'description' => 'Description', 'price' => 'Price', ])->heading(); $table->row([ 'sku' => 'shirt', 'title' => 'Shirt', 'description' => 'A nice shirt in blue color.', 'price' => 19.99, ]);
Render the table
<?= $table->render() ?> // or just <?= $table ?>
Both tables from above will produce the following output
<div class="table"> <div class="table-row th"> <div class="table-col grow-1">Sku</div> <div class="table-col grow-1">Title</div> <div class="table-col grow-2">Description</div> <div class="table-col grow-1">Price</div> </div> <div class="table-row"> <div class="table-col grow-1">shirt</div> <div class="table-col grow-1">Shirt</div> <div class="table-col grow-2">A nice shirt in blue color.</div> <div class="table-col grow-1">19.99</div> </div> </div>
Documentation
Create Table
use Tobento\Service\Table\Table; use Tobento\Service\Table\TableInterface; use Tobento\Service\Table\Renderer; $table = new Table( name: 'products', columns: ['title', 'description'], // The columns to render only. renderer: new Renderer(), ); var_dump($table instanceof TableInterface); // bool(true) // Get the name of the table: var_dump($table->name()); // string(8) "products"
With Methods
You might change some data or implentation by the following "with methods" returning a new instance.
withColumns
use Tobento\Service\Table\Table; $table = new Table('products'); $newTable = $table->withColumns(['sku', 'title']); var_dump($table === $newTable); // bool(false)
withRenderer
use Tobento\Service\Table\Table; use Tobento\Service\Table\Renderer; $table = new Table('products'); $newTable = $table->withRenderer(new Renderer()); var_dump($table === $newTable); // bool(false)
Create Rows
Creating rows from array items
use Tobento\Service\Table\Table; $table = new Table('products'); $table->rows([ [ 'sku' => 'shirt', 'title' => 'Shirt', 'description' => 'A nice shirt in blue color.', 'price' => 19.99, ], [ 'sku' => 'cap', 'title' => 'Cap', 'description' => 'A nice cap.', 'price' => 11.99, ], ]);
Using a callback for creating rows
You may use a callback for creating rows if you provide an array of objects for instance or want to create only specific row columns.
use Tobento\Service\Table\Table; use Tobento\Service\Table\Row; class Product { public function __construct( protected string $title, protected float $price, ) {} public function title(): string { return $this->title; } public function price(): float { return $this->price; } } $table = new Table('products'); $table->rows([ new Product('Shirt', 19.99), new Product('Cap', 11.99), ], function(Row $row, Product $product): void { $row->column(key: 'title', text: $product->title()); $row->column(key: 'price', text: $product->price()); });
Create only specific row columns from an array of items:
$table = new Table('products'); $table->rows([ [ 'sku' => 'shirt', 'title' => 'Shirt', 'description' => 'A nice shirt in blue color.', 'price' => 19.99, ], [ 'sku' => 'cap', 'title' => 'Cap', 'description' => 'A nice cap.', 'price' => 11.99, ], ], function(Row $row, array $item): void { $row->column(key: 'title', text: $item['title']); $row->column(key: 'price', text: $item['price']); });
Create Row
Creating a row from an array
use Tobento\Service\Table\Table; $table = new Table('products'); $table->row([ 'sku' => 'shirt', 'title' => 'Shirt', 'description' => 'A nice shirt in blue color.', 'price' => 19.99, ]);
Using a callback for creating row columns
You may use a callback for creating row columns if you provide an object.
use Tobento\Service\Table\Table; use Tobento\Service\Table\Row; class Product { public function __construct( protected string $title, protected float $price, ) {} public function title(): string { return $this->title; } public function price(): float { return $this->price; } } $table = new Table('products'); $table->row( new Product('Shirt', 19.99), function(Row $row, Product $product): void { $row->column(key: 'title', text: $product->title()) ->column(key: 'price', text: $product->price()); } );
Row Methods
Heading
You may mark the row as a heading.
use Tobento\Service\Table\Table; $table = new Table('products'); $table->row([ 'title' => 'Title', 'description' => 'Description', ])->heading();
Id
You may give the row an id to fetch it later.
use Tobento\Service\Table\Table; $table = new Table('products'); $table->row([ 'title' => 'Title', 'description' => 'Description', ])->id('header'); $headerRow = $table->getRow('header');
Each
You may use the each method to create the row columns.
use Tobento\Service\Table\Table; use Tobento\Service\Table\Row; $item = [ 'sku' => 'shirt', 'title' => 'Shirt', 'description' => 'A nice shirt in blue color.', 'price' => 19.99, ]; $table = new Table('products'); $table->row() ->each($item, function(Row $row, $value, $key): void { $row->column($key, $value); });
When
The when method will execute the given callback when the first argument given to the method evaluates to true.
use Tobento\Service\Table\Table; use Tobento\Service\Table\Row; $table = new Table('products'); $table->row() ->when(true, function(Row $row): void { $row->column('actions', 'Actions'); });
Html
You may mark columns as html, indicating that no escaping is done.
use Tobento\Service\Table\Table; $table = new Table('products'); $table->row([ 'sku' => 'shirt', 'title' => 'Shirt', 'description' => '<p>A nice shirt in blue color.</p>', 'price' => 19.99, ])->html('description');
PependHtml / AppendHtml
You may prepend and append html to the row.
use Tobento\Service\Table\Table; $table = new Table('products'); $table->row([ 'sku' => '<input type="text" id="sku" name="sku">', 'title' => '<input type="text" id="title" name="title">', ])->html('sku') ->html('title') ->prependHtml('<form>') ->appendHtml('</form>');
Custom Row
You may use the addRow method to pass a custom row.
use Tobento\Service\Table\Table; use Tobento\Service\Table\RowInterface; $table = new Table('products'); // must implement RowInterface $customRow = new CustomRow(); $table->addRow($customRow);
Render Table
There are different ways of rendering the table depending on your needs.
Default Renderer
The default renderer generates the table with DIV tags and calculates the columns size automatically.
use Tobento\Service\Table\Table; use Tobento\Service\Table\Renderer; $table = new Table( name: 'products', renderer: new Renderer(), ); $table->row([ 'sku' => 'Sku', 'title' => 'Title', 'description' => 'Description', 'price' => 'Price', ])->heading(); $table->row([ 'sku' => 'shirt', 'title' => 'Shirt', 'description' => 'A nice shirt in blue color.', 'price' => 19.99, ]); echo $table;
Outputs:
<div class="table"> <div class="table-row th"> <div class="table-col grow-1">Sku</div> <div class="table-col grow-1">Title</div> <div class="table-col grow-2">Description</div> <div class="table-col grow-1">Price</div> </div> <div class="table-row"> <div class="table-col grow-1">shirt</div> <div class="table-col grow-1">Shirt</div> <div class="table-col grow-2">A nice shirt in blue color.</div> <div class="table-col grow-1">19.99</div> </div> </div>
Custom Renderer
You might write your own renderer fitting your application.
use Tobento\Service\Table\Table; use Tobento\Service\Table\TableInterface; use Tobento\Service\Table\RendererInterface; use Tobento\Service\Table\Str; class CustomRenderer implements RendererInterface { public function render(TableInterface $table): string { if (empty($table->getRows())) { return ''; } $html = '<table>'; foreach($table->getRows() as $row) { if (empty($row->getColumns())) { continue; } $html .= '<tr>'; if ($row->prependedHtml()) { $html .= $row->prependedHtml(); } foreach($row->getColumns() as $column) { $text = $row->isHtml($column->key()) ? $column->text() : Str::esc($column->text()); if ($row->isHeading()) { $html .= '<th>'.$text.'</th>'; } else { $html .= '<td>'.$text.'</td>'; } } if ($row->appendedHtml()) { $html .= $row->appendedHtml(); } $html .= '</tr>'; } $html .= '</table>'; return $html; } } $table = new Table( name: 'products', renderer: new CustomRenderer(), );