Action Columns
UX DataTables now builds row actions from an Actions collection. The recommended entry point is
AbstractDataTable::configureActions().
Recommended Usage with AbstractDataTable
use App\Entity\User;
use Pentiminax\UX\DataTables\Attribute\AsDataTable;
use Pentiminax\UX\DataTables\Column\NumberColumn;
use Pentiminax\UX\DataTables\Column\TextColumn;
use Pentiminax\UX\DataTables\Model\AbstractDataTable;
use Pentiminax\UX\DataTables\Model\Action;
use Pentiminax\UX\DataTables\Model\Actions;
#[AsDataTable(User::class)]
final class UsersDataTable extends AbstractDataTable
{
public function configureColumns(): iterable
{
yield NumberColumn::new('id', 'ID');
yield TextColumn::new('email', 'Email');
yield TextColumn::new('status', 'Status');
}
public function configureActions(Actions $actions): Actions
{
return $actions
->setColumnLabel('Actions')
->add(
Action::edit('Edit')
->setClassName('btn btn-sm btn-warning')
->setIcon('bi bi-pencil')
)
->add(
Action::delete('Delete')
->setClassName('btn btn-sm btn-danger')
->setIcon('bi bi-trash')
->askConfirmation('Delete this user?')
->displayIf('status', 'draft')
);
}
protected function mapRow(mixed $item): array
{
/** @var User $item */
return [
'id' => $item->getId(),
'email' => $item->getEmail(),
'status' => $item->getStatus(),
];
}
}
When configureActions() returns at least one action, AbstractDataTable automatically appends an
ActionColumn named actions.
Action API
Factory Methods
| Method | Description |
|---|---|
| `Action::delete($label = 'Delete', $className = 'btn btn-danger')` | Create the built-in delete action |
| `Action::detail($label = 'Detail', $className = 'btn btn-primary')` | Create a link action to a detail page |
| `Action::edit($label = 'Edit', $className = 'btn btn-warning')` | Create an inline edit action (opens a Bootstrap 5 modal) |
Fluent Configuration
| Method | Description |
|---|---|
| `setLabel(string $label)` | Override the button label |
| `setClassName(string $className)` | Customize button classes |
| `setIcon(string $icon)` | Render an icon before the label |
| `askConfirmation(string $message)` | Display a browser confirmation prompt before the action |
| `displayIf(string $field, mixed $value)` | Show the action only when `row[field] === value` |
| `setIdField(string $idField)` | Use another row key instead of `id` |
| `setEntityClass(string $entityClass)` | Set the Doctrine entity class explicitly |
| `linkToUrl(string\|callable $url)` | Set the URL for detail actions (static string or per-row callable) |
Actions API
| Method | Description |
|---|---|
| `add(Action $action)` | Register an action for the column |
| `remove(ActionType $type)` | Remove a previously registered action |
| `setColumnLabel(string $label)` | Customize the action column header |
Actions stores one action per ActionType. The public enum values are:
use Pentiminax\UX\DataTables\Enum\ActionType;
ActionType::Delete; // value: DELETE
ActionType::Detail; // value: DETAIL
ActionType::Edit; // value: EDIT
Manual Usage with DataTableBuilderInterface
If you build tables manually, create an Actions collection and wrap it with
ActionColumn::fromActions():
use App\Entity\User;
use Pentiminax\UX\DataTables\Builder\DataTableBuilderInterface;
use Pentiminax\UX\DataTables\Column\ActionColumn;
use Pentiminax\UX\DataTables\Column\TextColumn;
use Pentiminax\UX\DataTables\Model\Action;
use Pentiminax\UX\DataTables\Model\Actions;
$actions = (new Actions())
->setColumnLabel('Operations')
->add(
Action::delete()
->setEntityClass(User::class)
->setIdField('uuid')
->askConfirmation('Delete this user?')
);
$table = $builder
->createDataTable('users')
->columns([
TextColumn::new('email', 'Email'),
ActionColumn::fromActions('actions', 'Operations', $actions),
])
->data([
['uuid' => '8f9f0c31', 'email' => 'john@example.com'],
]);
Manual tables must provide the entity class themselves because there is no #[AsDataTable]
attribute to infer it from.
Inline Edit (Modal)
Action::edit() opens a Bootstrap 5 modal containing an auto-generated Symfony Form. The form
fields are built from the table’s column configuration.
Requirements
symfony/formmust be installed (composer require symfony/form)- Bootstrap 5 must be loaded on the page (the modal uses
bootstrap.Modal)
How It Works
- User clicks the Edit button on a row
- The Stimulus controller fetches a pre-filled form from
GET /datatables/ajax/edit-form - A Bootstrap 5 modal opens with the rendered form
- On submit, a
POST /datatables/ajax/edit-formvalidates and persists the changes - The table reloads automatically (with Mercure broadcast if enabled)
Column-to-Form Mapping
The form builder maps each column to a Symfony Form type:
| Column Pattern | Form Type |
|---|---|
| `BooleanColumn` (`renderAsSwitch`) | `CheckboxType` |
| `ChoiceColumn` (has `choices`) | `ChoiceType` |
| `DateColumn` (has `dateFormat`) | `DateType` (single_text widget) |
| `NumberColumn` (`num`, `num-fmt`, …) | `NumberType` |
| `TextColumn` (`string`, `string-utf8`) | `TextType` |
| `html` type | `TextareaType` |
| `ActionColumn`, `TemplateColumn`, `UrlColumn` | Skipped automatically |
Primary Key Fields
Columns matching the entity’s Doctrine identifier are rendered as disabled fields in the form. They are visible for reference but cannot be modified.
Excluding Columns from the Form
Use hideWhenUpdating() on any column to exclude it from the edit modal entirely:
public function configureColumns(): iterable
{
yield NumberColumn::new('id', 'ID');
yield TextColumn::new('name', 'Name');
yield DateColumn::new('createdAt', 'Created')
->hideWhenUpdating(); // Excluded from the edit form
}
Full Example
use App\Entity\Product;
use Pentiminax\UX\DataTables\Attribute\AsDataTable;
use Pentiminax\UX\DataTables\Column\BooleanColumn;
use Pentiminax\UX\DataTables\Column\NumberColumn;
use Pentiminax\UX\DataTables\Column\TextColumn;
use Pentiminax\UX\DataTables\Model\AbstractDataTable;
use Pentiminax\UX\DataTables\Model\Action;
use Pentiminax\UX\DataTables\Model\Actions;
#[AsDataTable(Product::class)]
final class ProductsDataTable extends AbstractDataTable
{
public function configureColumns(): iterable
{
yield NumberColumn::new('id', 'ID');
yield TextColumn::new('name', 'Name');
yield NumberColumn::new('price', 'Price');
yield BooleanColumn::new('active', 'Active');
}
public function configureActions(Actions $actions): Actions
{
return $actions->add(
Action::edit('Edit')
->setIcon('bi bi-pencil')
);
}
protected function mapRow(mixed $item): array
{
/** @var Product $item */
return [
'id' => $item->getId(),
'name' => $item->getName(),
'price' => $item->getPrice(),
'active' => $item->isActive(),
];
}
}
Ajax Endpoints
All action endpoints are handled by built-in Stimulus controllers. Import the bundle routes:
// config/routes/ux_datatables.php
use Symfony\Component\Routing\Loader\Configurator\RoutingConfigurator;
return static function (RoutingConfigurator $routes): void {
$routes->import('@DataTablesBundle/config/routes.php');
};
Delete Endpoint
DELETE /datatables/ajax/delete — payload: entity, id, topics (optional Mercure topics).
Edit Form Endpoints
GET /datatables/ajax/edit-form— returns the rendered form HTMLPOST /datatables/ajax/edit-form— validates and persists the form data
These endpoints are only available when symfony/form is installed.