It Started With a Bug
When I was building VMMS — a voucher management system
for government offices — everything worked fine locally.
MySQL. Clean queries. Fast results.
Then I deployed to a server running MariaDB.
Half my charts broke.
The Problem
I had written date queries like this all over the codebase:
// This breaks on MariaDB
DB::table('voucher_transactions')
->selectRaw('MONTHNAME(created_at) as month, COUNT(*) as total')
->groupByRaw('MONTH(created_at), MONTHNAME(created_at)')
->get();
MONTH() and MONTHNAME() are MySQL functions. They work
fine on MySQL but behave differently on MariaDB — especially
when combined with GROUP BY.
The result? Months showing up in the wrong order.
Duplicate entries. Missing data.
Why This Happens
MySQL and MariaDB have diverged over the years. They share
a lot of syntax but handle certain functions differently —
especially around date grouping and ordering.
The safe rule is: if you want your app to work on both,
don't rely on database-specific date functions.
The Fix — Pull the Data and Use Carbon
Instead of doing date calculations in SQL, I pulled the
raw data and used Carbon to handle everything in PHP:
// DB-agnostic approach
$rows = VoucherTransaction::where('user_id', $userId)
->whereYear('created_at', $year)
->whereNull('deleted_at')
->get(['created_at', 'status']);
$byMonth = [];
foreach ($rows as $row) {
$m = (int) $row->created_at->format('n');
$status = strtolower($row->status);
$byMonth[$m] = $byMonth[$m] ?? ['accomplished' => 0, 'rejected' => 0];
if ($status === 'accomplished') $byMonth[$m]['accomplished']++;
if ($status === 'rejected') $byMonth[$m]['rejected']++;
}
ksort($byMonth);
Then to get the month name I used Carbon instead of
MONTHNAME():
$monthName = Carbon::create($year, $m, 1)->format('F');
// Returns "January", "February", etc.
// Works the same on MySQL and MariaDB
Building the Full Year Chart
For charts that need all 12 months — even empty ones —
I use range(1, 12) and fill in zeros for months with
no data:
return collect(range(1, 12))->map(function ($m) use ($byMonth, $year) {
$d = $byMonth[$m] ?? ['accomplished' => 0, 'rejected' => 0];
return [
'month' => Carbon::create($year, $m, 1)->format('F'),
'accomplished' => $d['accomplished'],
'rejected' => $d['rejected'],
];
});
This guarantees all 12 months always appear in the chart
— even if there's no data for some months. Clean and
predictable.
What About Performance?
You might be thinking — isn't pulling all rows and
processing in PHP slower than doing it in SQL?
For most applications — yes, SQL aggregation is faster.
But in practice for this use case:
- The data is filtered by user and year first
- The result set is small (max 365 rows per year per user)
- The PHP processing is negligible
If you're dealing with millions of rows this approach
needs rethinking. But for typical business applications
it's fast enough and the portability is worth it.
Other Carbon Tricks I Use in VMMS
Comparing dates without time:
// Use DATE() in SQL to avoid time comparison issues
->whereRaw('DATE(deadline) >= ?', [$now->toDateString()])
->whereRaw('DATE(deadline) <= ?', [$now->copy()->addDays(7)->toDateString()])
Calculating days left:
$deadline = Carbon::parse($t->deadline)->startOfDay();
$daysLeft = (int) $now->copy()->startOfDay()->diffInDays($deadline, false);
The false parameter makes diffInDays return negative
numbers for past dates — useful for overdue detection.
Calculating processing time:
$minutes = Carbon::parse($row->process_initiate)
->diffInMinutes(Carbon::parse($row->process_accomplished));
The Lesson
Don't assume your local MySQL behavior will match
production. If your app might run on MariaDB, PostgreSQL,
or any other database — keep your date logic in PHP
with Carbon and use only standard SQL for filtering.
It's a small habit that saves a lot of debugging time.
About VMMS
This and many other lessons came from building VMMS —
a complete voucher management system for government
offices, companies, and educational institutions.
🔴 Live demo: https://vmms-app-production.up.railway.app/login
Available on Gumroad:
👉 https://getvmms.gumroad.com/l/zeroqz
Happy to answer any questions in the comments! 🚀
Top comments (0)