elao/html-action-bundle
Symfony bundle adding HTML CRUD and list actions for ElaoAdminBundle. Configure html_list/create/read/update/delete to generate admin routes and forms with optional security rules, providing ready-to-use backend pages for your entities.
Installation
Add the bundle to your composer.json:
composer require elao/html-action-bundle
Register the bundle in config/bundles.php:
Elao\HtmlActionBundle\ElaoHtmlActionBundle::class => ['all' => true],
First Use Case
Extend an existing admin entity (e.g., Product) with a custom HTML action:
use Elao\HtmlActionBundle\Action\HtmlAction;
use Elao\HtmlActionBundle\Action\HtmlActionManager;
// In your Admin class (e.g., ProductAdmin)
public function configureActions(HtmlActionManager $manager)
{
$manager->add('custom_html_action', new HtmlAction(
'Custom Action',
'fa fa-rocket', // Font Awesome icon
'product_custom_action',
function ($entity) {
return $this->generateUrl('product_custom_route', ['id' => $entity->getId()]);
},
function ($entity) {
return '<button class="btn btn-primary">Click Me</button>';
}
));
}
Template Integration Override the admin template to include the action buttons:
{% extends '@ElaoAdmin/CRUD/base.html.twig' %}
{% block actions %}
{{ parent() }}
{{ admin.actions.render('custom_html_action', entity) }}
{% endblock %}
Dynamic Action Rendering Use closures to dynamically generate HTML based on entity state:
$manager->add('dynamic_action', new HtmlAction(
'Dynamic Action',
'fa fa-info',
'product_info',
function ($entity) {
return $this->generateUrl('product_show', ['id' => $entity->getId()]);
},
function ($entity) {
$html = '<div class="dropdown">';
$html .= '<button class="btn btn-default dropdown-toggle" data-toggle="dropdown">';
$html .= 'Details <span class="caret"></span></button>';
$html .= '<ul class="dropdown-menu">';
$html .= '<li><a href="' . $this->generateUrl('product_show', ['id' => $entity->getId()]) . '">View</a></li>';
$html .= '</ul></div>';
return $html;
}
));
Conditional Actions Hide/show actions based on entity properties or permissions:
$manager->add('conditional_action', new HtmlAction(
'Approve',
'fa fa-check',
'product_approve',
function ($entity) {
return $this->generateUrl('product_approve', ['id' => $entity->getId()]);
},
function ($entity) use ($security) {
if ($entity->isApproved() || !$security->isGranted('ROLE_ADMIN')) {
return '';
}
return '<button class="btn btn-success">Approve</button>';
}
));
Bulk Actions Extend the mass action dropdown in the list view:
$manager->add('bulk_export', new HtmlAction(
'Export',
'fa fa-download',
'product_bulk_export',
function () {
return $this->generateUrl('product_bulk_export');
},
function () {
return '<button class="btn btn-default">Export Selected</button>';
}
));
Override the mass action template to include the new button.
Reusing Actions Across Admins Create a base admin class to share common actions:
abstract class BaseProductAdmin extends AbstractAdmin
{
protected function configureSharedActions(HtmlActionManager $manager)
{
$manager->add('share_action', new HtmlAction(
'Share',
'fa fa-share-alt',
'product_share',
function ($entity) {
return $this->generateUrl('product_share', ['id' => $entity->getId()]);
},
function ($entity) {
return '<button class="btn btn-link">Share</button>';
}
));
}
}
Styling
Use the existing btn-* classes for consistency. For custom styles, target:
.action-button {
margin-right: 5px;
}
JavaScript Enhancement Attach events to dynamically generated buttons:
{{ admin.actions.render('custom_html_action', entity, {
'data-toggle': 'tooltip',
'title': 'Custom tooltip'
}) }}
Routing
Ensure your routes are named (e.g., product_custom_route) for URL generation:
# config/routes.yaml
product_custom_route:
path: /product/{id}/custom
controller: App\Controller\ProductController::customAction
Localization Wrap action labels in translation functions:
$manager->add('translate_action', new HtmlAction(
$this->trans('action.label'), // e.g., 'action.custom_label'
'fa fa-globe',
'product_translate',
...
));
Template Override Conflicts
actions in base.html.twig).{{ admin.actions.render() }}.Closure Scope Issues
HtmlAction lose scope. Explicitly use dependencies:
function ($entity) use ($router, $translator) { ... }
Font Awesome Dependency
{% block stylesheets %}
{{ parent() }}
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
{% endblock %}
Caching Headaches
php bin/console cache:clear
CSRF Protection
Action Not Showing?
configureActions() method is called (e.g., in configureListFields()).Blank HTML Output
trim():
function ($entity) { return trim($html); }
Routing Errors
dump($this->generateUrl(...)) to debug URL generation.Custom Action Types
Extend HtmlAction to add validation or pre-processing:
class ValidatedHtmlAction extends HtmlAction
{
public function __construct($label, $icon, $route, $urlGenerator, $htmlGenerator, $validator)
{
parent::__construct($label, $icon, $route, $urlGenerator, $htmlGenerator);
$this->validator = $validator;
}
public function isValid($entity)
{
return $this->validator($entity);
}
}
Event Listeners Hook into admin events to dynamically modify actions:
// src/EventListener/AdminActionListener.php
public function onAdminPreRender(AdminEvent $event)
{
$admin = $event->getAdmin();
if ($admin instanceof ProductAdmin) {
$admin->getActionManager()->add('dynamic_action', ...);
}
}
Asset Management Override the bundle’s assets for custom icons or styles:
# config/packages/elao_html_action.yaml
elao_html_action:
assets:
stylesheets: ['css/custom-actions.css']
Permission Integration Integrate with Symfony’s security system to gate actions:
$manager->add('secure_action', new HtmlAction(
'Secure Action',
'fa fa-lock',
'product_secure',
function ($entity) use ($security) {
if (!$security->isGranted('ROLE_SUPER_ADMIN')) {
return '';
}
return $this->generateUrl('product_secure', ['id' => $entity->getId()]);
},
function () {
return '<button class="btn btn-warning">Secure</button>';
}
));
$manager->add('grouped_action', new HtmlAction(
'Group',
'',
'',
function () {},
function () {
return '
<div class="panel panel-default">
<div class="panel-heading">Actions</div>
<div class="panel-body">
How can I help you explore Laravel packages today?