Installation Add the package via Composer:
composer require draw/dashboard-bundle
Publish the bundle’s assets and configuration:
php artisan vendor:publish --provider="Draw\DashboardBundle\DashboardBundle" --tag=config
php artisan vendor:publish --provider="Draw\DashboardBundle\DashboardBundle" --tag=assets
Basic Setup
Register the bundle in config/app.php under providers:
Draw\DashboardBundle\DashboardBundle::class,
First Dashboard Widget
Create a simple widget by extending AbstractWidget:
namespace App\Dashboard\Widgets;
use Draw\DashboardBundle\Widgets\AbstractWidget;
class ExampleWidget extends AbstractWidget
{
public function getTitle(): string
{
return 'Example Widget';
}
public function getContent(): string
{
return '<div>Hello, Dashboard!</div>';
}
}
Register the Widget
Add it to your dashboard configuration (config/dashboard.php):
'widgets' => [
'example' => [
'class' => \App\Dashboard\Widgets\ExampleWidget::class,
'position' => 1,
],
],
Display the Dashboard
Add a route in routes/web.php:
use Draw\DashboardBundle\Controller\DashboardController;
Route::get('/dashboard', [DashboardController::class, 'index']);
Extending AbstractWidget
Override getTitle(), getContent(), and optionally getIcon() for customization.
Example:
class StatsWidget extends AbstractWidget
{
public function getContent(): string
{
return view('dashboard.widgets.stats')->render();
}
}
Dynamic Data Fetching Use dependency injection to fetch data (e.g., repositories, services):
class UserStatsWidget extends AbstractWidget
{
public function __construct(private UserRepository $userRepo) {}
public function getContent(): string
{
$stats = $this->userRepo->getStats();
return view('dashboard.widgets.user_stats', compact('stats'));
}
}
Blade Views
Store widget views in resources/views/dashboard/widgets/.
Example: resources/views/dashboard/widgets/example.blade.php
<div class="card">
<h5 class="card-header">{{ $widget->getTitle() }}</h5>
<div class="card-body">
{!! $widget->getContent() !!}
</div>
</div>
Override Default Template
Publish the bundle’s views and extend dashboard.blade.php:
php artisan vendor:publish --provider="Draw\DashboardBundle\DashboardBundle" --tag=views
Customize the layout in resources/views/vendor/dashboard/dashboard.blade.php.
Widget Positioning
Configure widget order in config/dashboard.php:
'widgets' => [
'stats' => ['class' => StatsWidget::class, 'position' => 1],
'users' => ['class' => UserStatsWidget::class, 'position' => 2],
],
routes/web.php:
Route::middleware(['auth'])->get('/dashboard', [DashboardController::class, 'index']);
getContent() or use Laravel’s Gate:
public function getContent(): string
{
if (!auth()->user()->can('view_admin_dashboard')) {
abort(403);
}
return view('...');
}
resources/assets/dashboard/
Compile them alongside your main assets.Widget Registration Order
Widgets are rendered in ascending order of their position value. Ensure unique positions to avoid overlaps.
Missing Dependencies If a widget fails silently, check:
Caching Issues Clear Laravel’s cache after publishing assets or config:
php artisan cache:clear
php artisan view:clear
Blade vs. Raw HTML
Use {!! !!} for raw HTML in getContent() to avoid auto-escaping:
return '<div>' . htmlspecialchars($dynamicData) . '</div>';
Log Widget Output
Temporarily log getContent() output to debug rendering:
\Log::debug('Widget content:', ['content' => $this->getContent()]);
Check Published Assets
Verify assets are published to public/vendor/dashboard/:
php artisan vendor:publish --tag=assets --force
Inspect Configuration Dump the loaded config to confirm widget registration:
dd(config('dashboard'));
Custom Dashboard Controller
Extend DashboardController to add logic (e.g., analytics):
namespace App\Http\Controllers;
use Draw\DashboardBundle\Controller\DashboardController as BaseDashboardController;
class DashboardController extends BaseDashboardController
{
public function index()
{
// Custom logic (e.g., track visits)
return parent::index();
}
}
Dynamic Widget Loading Load widgets dynamically via a service provider:
public function register()
{
$this->app->bind(
\Draw\DashboardBundle\Contracts\WidgetInterface::class,
function () {
return new DynamicWidget();
}
);
}
Multi-Tenant Dashboards Override widget configuration per tenant by extending the config loader:
config(['dashboard.widgets' => $this->getTenantWidgets()]);
getContent() if the widget doesn’t change frequently:
return Cache::remember("widget_{$this->getTitle()}", now()->addHours(1), function () {
return view('...')->render();
});
How can I help you explore Laravel packages today?