Installation
composer require badpixxel/widgets-bundle
Enable the bundle in config/bundles.php:
return [
// ...
BadPixxel\WidgetsBundle\WidgetsBundle::class => ['all' => true],
];
First Widget
Create a basic widget class in src/Widgets/:
namespace App\Widgets;
use BadPixxel\WidgetsBundle\Widget\AbstractWidget;
class MyFirstWidget extends AbstractWidget
{
public function getTitle(): string
{
return 'My Custom Widget';
}
public function getContent(): string
{
return '<p>Hello, Dashboard!</p>';
}
}
Register Widget
Add the widget to your dashboard configuration in config/packages/badpixxel_widgets.yaml:
widgets:
dashboard:
- App\Widgets\MyFirstWidget
Display Widgets Render widgets in a Twig template:
{% for widget in widgets %}
{{ widget.render() }}
{% endfor %}
Use the built-in BootstrapWidget for rapid UI integration:
use BadPixxel\WidgetsBundle\Widget\BootstrapWidget;
class StatsWidget extends BootstrapWidget
{
public function getTitle(): string
{
return 'System Stats';
}
public function getContent(): string
{
return $this->renderStatsCard();
}
private function renderStatsCard(): string
{
return $this->renderCard([
'title' => 'Active Users',
'value' => 42,
'icon' => 'users',
]);
}
}
Initialization
Override initialize() to fetch data early:
public function initialize()
{
$this->data = $this->entityManager->getRepository(User::class)->getActiveUsers();
}
Caching
Use setCacheLifetime() to cache widget output:
public function __construct()
{
$this->setCacheLifetime(3600); // Cache for 1 hour
}
Dependency Injection Inject services via constructor:
public function __construct(
private UserRepository $userRepo,
private ClockInterface $clock
) {}
Dynamic Widget Loading Load widgets dynamically via a service:
# config/packages/badpixxel_widgets.yaml
widgets:
dashboard:
- '@widget.service_id' # Service-based widget
Widget Groups Organize widgets into tabs/sections:
// In a custom dashboard controller
$widgets = $this->widgetManager->getWidgets('dashboard');
$widgets->groupBy(['tab1', 'tab2']);
Conditional Rendering Skip widgets based on user roles:
public function isVisible(): bool
{
return $this->security->isGranted('ROLE_ADMIN');
}
Widget Events
Listen to widget events (e.g., WidgetRenderEvent):
$eventDispatcher->addListener(
WidgetEvents::PRE_RENDER,
[$this, 'onPreRender']
);
Widget Templates Override Twig templates:
{# templates/widgets/my_widget.html.twig #}
<div class="custom-widget">
{{- include '@Widgets/default.html.twig' -}}
</div>
Widget Configuration
Use configure() to accept runtime options:
public function configure(array $options)
{
$this->options = $options;
}
Caching Issues
php bin/console cache:clear
Dependency Conflicts
sonata-project/block-bundle is installed if using Sonata integration:
composer require sonata-project/block-bundle
Twig Template Overrides
templates/widgets/ to avoid overwrites during updates.Widget Registration
Resources/config/services.yaml.Log Widget Output
Enable debug mode in config/packages/badpixxel_widgets.yaml:
debug: true
Check Widget Events Use Symfony’s event dispatcher to log widget lifecycle:
$eventDispatcher->addListener(
WidgetEvents::POST_RENDER,
function (WidgetRenderEvent $event) {
$this->logger->debug('Widget rendered:', ['widget' => $event->getWidget()->getClass()]);
}
);
Inspect Widget Data Dump widget data in Twig:
{{ dump(widget.getData()) }}
Custom Widget Types
Extend AbstractWidget for reusable logic:
abstract class ChartWidget extends AbstractWidget
{
protected function renderChart(): string { /* ... */ }
}
Widget Storage Save widget configurations to the database:
use BadPixxel\WidgetsBundle\Widget\WidgetStorageInterface;
public function __construct(private WidgetStorageInterface $storage) {}
Widget Permissions Integrate with Symfony’s security system:
public function isVisible(): bool
{
return $this->security->isGranted('VIEW_DASHBOARD');
}
YAML vs. PHP Config Prefer PHP config for complex widget arrays:
// config/packages/badpixxel_widgets.php
return [
'widgets' => [
'dashboard' => [
'App\Widgets\MyWidget',
['@widget.service', ['param' => 'value']],
],
],
];
Widget Prioritization
Order matters in the widgets array—earlier entries render first.
Bootstrap Version
Ensure your Bootstrap CSS/JS matches the bundle’s defaults (v4/v5). Override in assets/ if needed.
How can I help you explore Laravel packages today?