fix(widgets): add multi-database compatibility to all dashboard widgets

Replace SQLite-specific functions with database-agnostic expressions to support
  MySQL, SQLite, PostgreSQL, and SQL Server across all Filament dashboard widgets.

  - Fix strftime() date formatting in SubscriptionMetrics, RevenueMetrics, and TrialPerformance
  - Fix CAST AS REAL syntax in ChurnAnalysis widget
  - Add getDateFormatExpression() method for date function compatibility
  - Add getCastExpression() method for CAST syntax compatibility
  - Support MySQL/MariaDB, SQLite, PostgreSQL, and SQL Server drivers
  - Maintain identical functionality across all database types

  Fixes multiple SQLSTATE[42000] syntax errors when using MySQL/MariaDB databases.
This commit is contained in:
idevakk
2025-12-02 07:09:30 -08:00
parent 659325c01d
commit e60973c391
4 changed files with 67 additions and 5 deletions

View File

@@ -112,12 +112,14 @@ class ChurnAnalysis extends ChartWidget
private function getChurnByProvider(): array
{
$castExpression = $this->getCastExpression();
return Subscription::query()
->select(
'provider',
DB::raw('COUNT(CASE WHEN status = "cancelled" THEN 1 END) as cancelled'),
DB::raw('COUNT(*) as total'),
DB::raw('(CAST(COUNT(CASE WHEN status = "cancelled" THEN 1 END) AS REAL) * 100.0 / COUNT(*)) as churn_rate')
DB::raw("({$castExpression} * 100.0 / COUNT(*)) as churn_rate")
)
->groupBy('provider')
->orderBy('churn_rate', 'desc')
@@ -130,12 +132,14 @@ class ChurnAnalysis extends ChartWidget
private function getChurnByPlan(): array
{
$castExpression = $this->getCastExpression();
return Subscription::query()
->select(
'plans.name as plan_name',
DB::raw('COUNT(CASE WHEN subscriptions.status = "cancelled" THEN 1 END) as cancelled'),
DB::raw('COUNT(*) as total'),
DB::raw('(CAST(COUNT(CASE WHEN subscriptions.status = "cancelled" THEN 1 END) AS REAL) * 100.0 / COUNT(*)) as churn_rate')
DB::raw("({$castExpression} * 100.0 / COUNT(*)) as churn_rate")
)
->join('plans', 'subscriptions.plan_id', '=', 'plans.id')
->groupBy('plans.id', 'plans.name')
@@ -147,4 +151,17 @@ class ChurnAnalysis extends ChartWidget
})
->toArray();
}
private function getCastExpression(): string
{
$connection = DB::connection()->getDriverName();
return match ($connection) {
'sqlite' => 'CAST(COUNT(CASE WHEN status = "cancelled" THEN 1 END) AS REAL)',
'mysql', 'mariadb' => 'CAST(COUNT(CASE WHEN status = "cancelled" THEN 1 END) AS DECIMAL(10,2))',
'pgsql' => 'CAST(COUNT(CASE WHEN status = "cancelled" THEN 1 END) AS NUMERIC)',
'sqlsrv' => 'CAST(COUNT(CASE WHEN status = "cancelled" THEN 1 END) AS FLOAT)',
default => 'CAST(COUNT(CASE WHEN status = "cancelled" THEN 1 END) AS DECIMAL(10,2))', // fallback to MySQL format
};
}
}