TableBuilder Class Documentation
The TableBuilder class provides a fluent, field-first interface for creating and managing dynamic tables. It uses a modern chaining pattern where you configure each field individually, making your code more readable and maintainable.
System Overview
TableBuilder acts as a wrapper that combines:
- ModelList: Database connection and query management
- ListStructure: Column structure and configuration
- PageInfo: Pagination and display information
It provides a single, chainable API for building tables with less code and better readability.
Method Reference Summary
| Category | Method | Description |
|---|---|---|
| Query Building | select(array|string $columns) |
Select specific columns |
where(string $condition, array $params = [], string $operator = 'AND') |
Add WHERE condition | |
whereIn(string $field, array $values) |
WHERE IN clause | |
whereLike(string $field, string $value, string $position = 'both') |
LIKE search condition | |
whereBetween(string $field, $min, $max) |
BETWEEN condition | |
join(string $table, string $condition, string $type = 'INNER') |
JOIN tables | |
leftJoin(string $table, string $condition) |
LEFT JOIN tables | |
rightJoin(string $table, string $condition) |
RIGHT JOIN tables | |
groupBy(string $field) |
GROUP BY clause | |
having(string $condition, array $params = []) |
HAVING clause | |
orderBy(string $field, string $direction = 'ASC') |
Set ordering | |
limit(int $limit) |
Set row limit | |
queryCustomCallback(callable $callback) |
Custom query logic | |
| Field Configuration (Field-First) |
field(string $key) |
Start configuring a field |
label(string $label) |
Set field label | |
type(string $type) |
Set field type | |
options(array $options) |
Set select options | |
fn(callable $fn) |
Custom formatter function | |
link(string $link, array $options = []) |
Convert to clickable link | |
file(array $options = []) |
Display as file download links | |
image(array $options = []) |
Display as image thumbnails | |
truncate(int $length, string $suffix = '...') |
Truncate text with suffix | |
hide() |
Hide field | |
noSort() |
Disable sorting | |
sortBy(string $realField) |
Map to real database field | |
class(string $classes) |
Set CSS classes (see Styling docs) | |
moveBefore(string $fieldName) |
Move current field before another field | |
| Column Management | reorderColumns(array $order) |
Reorder all columns by array of column names |
hideColumns(array $keys) |
Hide multiple columns at once | |
resetFields() |
Hide all existing columns from the model | |
| Actions | setPage(string $page) |
Set page name for action links |
setDefaultActions(array $customActions = []) |
Auto-create Edit/Delete actions | |
addAction(string $key, array $config) |
Add a single row action | |
setActions(array $actions) |
Configure custom row actions | |
setBulkActions(array $actions) |
Configure bulk actions | |
| Output | getData() |
Get complete data array |
getTable() |
Get HTML table | |
getResponse() |
Get HTML + additional data (handles JSON automatically) | |
render() |
Get HTML table directly | |
getFunctionsResults() |
Get action callback results |
Basic Usage
DO NOT wrap the table output in a div with the table ID. The TableBuilder already includes a wrapper with the correct ID. Adding another div will create duplicate IDs and break filters!
<div id="posts_table">
<?php echo $table_html; ?>
</div>
<?php echo $table_html; ?>
Why? The TableBuilder's getResponse()['html'] already contains the full table HTML with the proper wrapper and ID. Just echo it directly!
Constructor and Factory Method
$table = \Builders\TableBuilder::create($model, 'table_id');
Simple Table Creation
// CONTROLLER
$model = new \Models\PostModel();
$tableBuilder = \Builders\TableBuilder::create($model, 'posts_table')
->limit(20)
->orderBy('created_at', 'desc')
->setDefaultActions()
// Field-first pattern: configure title field
->field('title')
->link('?page='.$this->page.'&action=edit&id=%id%');
$response = $tableBuilder->getResponse();
Response::render('view.php', $response);
// VIEW (view.php)
// <?php echo $table_html; ?> ← Just this! Don't wrap it!
Query Building Methods
TableBuilder supports all standard query operations. Here are the most common:
Default Pagination Limit
You can set a global default for the number of rows per page by defining $conf['page_info_limit'] in your milkadmin_local/config.php file:
$conf['page_info_limit'] = 50; // Default rows per page
This will apply to all tables unless overridden with the limit() method. See Milkadmin Local Configuration for more details.
$table = \Builders\TableBuilder::create($model, 'posts_table')
// Basic operations
->select(['id', 'title', 'status', 'created_at'])
->where('status = ?', ['published'])
->orderBy('created_at', 'desc')
->limit(50)
// Advanced operations
->whereIn('status', ['active', 'pending'])
->whereLike('title', 'search_term', 'both') // LIKE '%search_term%'
->whereBetween('created_at', '2024-01-01', '2024-12-31')
->leftJoin('categories', 'posts.category_id = categories.id')
->getTable();
📘 For comprehensive query documentation: See Abstract Model - Query Methods
Field-First Pattern
The field-first pattern provides a clean, readable way to configure each table column. Instead of calling methods with the field name as a parameter, you first select the field with field() and then chain configuration methods.
Why Field-First?
- Better Readability: All configurations for a field are grouped together
- Clearer Intent: Easy to see what each field does at a glance
- IDE Support: Better autocomplete and method suggestions
- Consistent: Matches modern fluent API patterns
Basic Field Configuration
$table = \Builders\TableBuilder::create($model, 'posts_table')
// Configure each field with method chaining
->field('id')
->label('ID')
->hide() // Hide this field
->field('title')
->label('Article Title')
->link('?page=posts&action=edit&id=%id%')
->truncate(80)
->field('status')
->label('Status')
->type('select')
->options([
'draft' => 'Draft',
'published' => 'Published',
'archived' => 'Archived'
])
->field('created_at')
->label('Publication Date')
->type('datetime')
->getTable();
Available Field Methods
| Method | Description | Example |
|---|---|---|
label(string $label) |
Set display label for the field | ->field('created_at')->label('Published') |
type(string $type) |
Set field type (text, select, date, html, etc.) | ->field('status')->type('select') |
options(array $options) |
Set options for select fields | ->field('status')->options(['active' => 'Active']) |
fn(callable $fn) |
Custom formatter function | ->field('name')->fn(fn($row) => strtoupper($row['name'])) |
hide() |
Hide field from display | ->field('password')->hide() |
noSort() |
Disable sorting for this field | ->field('actions')->noSort() |
sortBy(string $real_field) |
Map virtual field to database field for sorting | ->field('doctor_name')->sortBy('doctor.name') |
Display Formatting Methods
Links - link() method
The link() method converts a field to a clickable link with placeholder support.
$table = \Builders\TableBuilder::create($model, 'posts_table')
// Basic link
->field('title')
->link('?page=posts&action=edit&id=%id%')
// Link with options
->field('author')
->link('/profile/%author_id%', [
'target' => '_blank',
'class' => 'text-primary fw-bold'
])
// Multiple placeholders
->field('full_name')
->link('/user/%id%?tab=profile&ref=%category%')
// Link with fetch (AJAX loading)
->field('lessons')
->link('?page=courses&action=lessons&entity_id=%id%', [
'data-fetch' => 'post'
])
->getTable();
// Supported placeholders:
// %id% - Primary key or 'id' field
// %field_name% - Any column value (e.g., %title%, %status%, %created_at%)
// Available options:
// 'target' => '_blank' | '_self' | '_parent' | '_top'
// 'class' => 'css-classes-here'
// 'data-fetch' => 'post' | 'get' - Enable AJAX loading
Files - file() method
The file() method converts array fields containing file data into download links. Automatically applied when model has type=array and form-type=file.
$table = \Builders\TableBuilder::create($model, 'documents_table')
// Basic usage
->field('attachments')
->file()
// With custom options
->field('files')
->file([
'class' => 'btn btn-link text-primary',
'target' => '_blank'
])
->getTable();
// Expected data format:
// $row->attachments = [
// ['url' => 'media/file1.pdf', 'name' => 'Document.pdf'],
// ['url' => 'media/file2.docx', 'name' => 'Report.docx']
// ];
// Available options:
// 'class' => 'custom-css-class' // Default: 'js-file-download'
// 'target' => '_blank' | '_self' // Default: '_blank'
Images - image() method
The image() method displays image thumbnails. Automatically applied when model has type=array and form-type=image.
$table = \Builders\TableBuilder::create($model, 'products_table')
// Basic usage
->field('photos')
->image()
// With custom options
->field('gallery')
->image([
'size' => 80, // Thumbnail size in pixels
'class' => 'rounded', // CSS classes
'lightbox' => true, // Clickable links
'max_images' => 3 // Limit displayed images
])
->getTable();
// Expected data format:
// $row->photos = [
// ['url' => 'media/photo1.jpg', 'name' => 'Product Image 1'],
// ['url' => 'media/photo2.jpg', 'name' => 'Product Image 2']
// ];
// Available options:
// 'size' => 50 // Width/height in pixels
// 'class' => '' // CSS classes
// 'lightbox' => false // Enable clickable links
// 'max_images' => null // Show "+N" for remaining
Text Truncation - truncate() method
The truncate() method limits text length, adding a suffix when exceeded.
$table = \Builders\TableBuilder::create($model, 'posts_table')
// Basic truncation
->field('description')
->truncate(100)
// Custom suffix
->field('title')
->truncate(50, '…')
->field('content')
->truncate(200, ' [read more]')
->getTable();
// Features:
// - UTF-8 safe (uses mb_substr and mb_strlen)
// - Applied after all formatting
// - Only truncates strings exceeding the specified length
// - Can be combined with other methods
Complete Example
$table = \Builders\TableBuilder::create($model, 'posts_table')
// Query configuration
->where('status != ?', ['deleted'])
->orderBy('created_at', 'DESC')
->limit(20)
// Field configurations with field-first pattern
->field('id')
->label('ID')
->hide()
->field('title')
->label('Article Title')
->link('?page=posts&action=edit&id=%id%')
->truncate(80)
->field('author_name')
->label('Author')
->fn(function($row) {
return $row['first_name'] . ' ' . $row['last_name'];
})
->field('category')
->label('Category')
->type('select')
->options([
'tech' => 'Technology',
'news' => 'News',
'blog' => 'Blog'
])
->field('status')
->label('Status')
->field('photos')
->label('Images')
->image(['size' => 60, 'max_images' => 2])
->field('created_at')
->label('Publication Date')
->type('datetime')
// Actions
->setPage('posts')
->setDefaultActions()
->getTable();
Row Actions
Row actions are buttons that appear for each table row, allowing users to perform operations like Edit, Delete, View, or custom actions.
Quick Example
$table = \Builders\TableBuilder::create($model, 'posts_table')
->setActions([
'edit' => [
'label' => 'Edit',
'link' => '?page=posts&action=edit&id=%id%'
],
'delete' => [
'label' => 'Delete',
'action' => [$this, 'actionDelete'],
'confirm' => 'Are you sure?',
'class' => 'btn-danger'
]
]);
Default Actions Helper
Use setDefaultActions() to automatically generate Edit and Delete actions:
$table = \Builders\TableBuilder::create($model, 'posts_table')
->setDefaultActions(); // Auto-generates Edit and Delete actions
See: Row Actions Documentation
Bulk Actions
For operations on multiple rows at once, use bulk actions.
See: Bulk Actions Documentation
Table Styling
TableBuilder provides comprehensive styling options including field-specific styling with conditional classes.
See: Table Styling Documentation
Output Methods
Understanding render() vs getResponse()
When to use each method
render() or getTable() - Use when you have NO row actions or bulk actions with callbacks:
// Simple table - no actions or only link actions
$html = TableBuilder::create($model, 'table_id')
->field('title')->link('?page=posts&action=edit&id=%id%')
->render();
Response::render($view, ['html' => $html]);
getResponse() - REQUIRED when you have row actions or bulk actions with callback functions:
// Table with action callbacks - MUST use getResponse()
$response = TableBuilder::create($model, 'table_id')
->setActions([
'delete' => [
'label' => 'Delete',
'action' => [$this, 'actionDelete'], // Callback function
'confirm' => 'Are you sure?'
]
])
->getResponse(); // Returns ['html' => '...', ...callback results]
// Pass entire $response array to Response::render()
Response::render($view, $response);
AJAX Handling
Response::render() automatically handles AJAX requests. When the table makes AJAX calls for sorting, pagination, or filtering, Response::render() detects this and returns JSON automatically. No manual checks needed!
Common Mistake
// ❌ WRONG - Don't concatenate with render() when you have callbacks
$response['html'] = $titleHtml . $tableBuilder->render();
Response::render($view, $response);
// ✅ CORRECT - Use getResponse() and add other data separately
$response = $tableBuilder->getResponse();
$response['title_html'] = $titleHtml;
Response::render($view, $response);
Why? getResponse() returns ['html' => '...', ...action_results]. The action results are needed for AJAX/JSON handling and table reloading after callbacks execute.
All Available Output Methods
$table = \Builders\TableBuilder::create($model, 'posts_table')
->limit(20)
->orderBy('created_at', 'desc');
// 1. Get complete data array (advanced usage)
$data = $table->getData();
// Returns: ['rows' => [...], 'info' => ListStructure, 'page_info' => PageInfo]
// 2. Get HTML table only (no actions with callbacks)
$html = $table->render();
echo $html;
// 3. Alternative syntax for render()
$html = $table->getTable();
echo $html;
// 4. Get response array (required for action callbacks)
$response = $table->getResponse();
// Returns: ['html' => '...', ...additional data from actions]
// 5. Get only function results from actions
$results = $table->getFunctionsResults();
Key Features Summary
- Field-First Pattern: Configure each field with clear, grouped method chains
- Fluent Interface: Method chaining for readable code
- Action Functions: Execute custom logic on selected rows with return values for theme integration
- Advanced Queries: Support for JOINs, WHERE conditions, and custom callbacks
- Flexible Display: Links, images, files, truncation, custom formatters
- Styling Options: Comprehensive table and field-specific styling with conditional classes