# Upgrade Log: Laravel 11 → 12 + Full Ecosystem ## Starting State | Package | Before | Target | |---|---|---| | laravel/framework | 11.48.0 | ^12.0 | | filament/filament | 3.3.48 | ^5.0 | | livewire/livewire | 3.7.10 | ^4.0 | | tailwindcss | 3.4.19 | ^4.0 | | pestphp/pest | 3.8.5 | ^4.0 | | barryvdh/laravel-debugbar | 3.16.5 | ^4.0 | | jeffgreco13/filament-breezy | 2.6.4 | ^3.0 | | ariaieboy/filament-currency | 1.13.0 | ^3.0 | | amidesfahani/filament-tinyeditor | 3.0.0 | ^4.0 | | mollie/laravel-mollie | 3.1.0 | ^4.0 | **Baseline tests:** 14 passed (41 assertions) **Baseline build:** Success (client + SSR) --- ## Phase 1: Pre-Flight - [x] Branch created: `upgrade/laravel-12-full` - [x] Baseline tests: 14 passed - [x] Baseline build: success ## Phase 2: Composer — Laravel 12 - [x] Replaced `codezero/laravel-localized-routes` with `opgginc/codezero-laravel-localized-routes:^5.0` (drop-in fork) - [x] Removed `codezero/laravel-localizer` (single-locale nl project) - [x] Set `codezero/composer-preload-files` to `false` in allow-plugins (transitive dep still present) - [x] Updated constraints: laravel/framework ^12, symfony/http-client ^8.0, symfony/mailgun-mailer ^8.0, barryvdh/laravel-debugbar ^4.0, mollie/laravel-mollie ^4.0 - [x] `composer update -W` — Laravel v12.50.0 installed - [x] Tests: 14 passed ## Phase 3: Filament v3 → v4 → v5 - [x] Removed `awcodes/filament-table-repeater` (no Filament v4/v5 version exists) - [x] Replaced TableRepeater with standard `Filament\Forms\Components\Repeater` in ChildrenRelationManager - [x] Ran `filament-v4` rector on 197 files - [x] Removed `filament/spatie-laravel-translatable-plugin` (no v4 release), skipped to v5 - [x] Ran `filament-v5` rector on 197 files - [x] Installed all Filament v5 packages + `lara-zeus/spatie-translatable:^2.0` - [x] Fixed ~20 files: `Filament\Pages\Actions\*` → `Filament\Actions\*` - [x] Fixed `Filament\Forms\Set` → `Filament\Schemas\Components\Utilities\Set` (ProductResource, ChildrenRelationManager) - [x] Filament v5.2.1, Livewire v4.1.4 installed - [x] Tests: 14 passed ## Phase 4: Tailwind v3 → v4 - [x] Installed tailwindcss@^4, @tailwindcss/postcss, @tailwindcss/cli - [x] Removed autoprefixer, @tailwindcss/forms, @tailwindcss/typography, @tailwindcss/aspect-ratio, eslint-plugin-tailwindcss - [x] Updated postcss.config.js to use @tailwindcss/postcss - [x] Migrated tailwind.config.js to CSS-first config in app.css (@theme, @source, @variant directives) - [x] Converted utility.css from @layer to @utility directives - [x] Deleted tailwind.config.js - [x] Removed tailwindcss settings from eslint.config.mjs - [x] Installed vite@^7, @vitejs/plugin-vue@^6, laravel-vite-plugin@^2 - [x] Build: success, Tests: 14 passed ## Phase 5: Pest v3 → v4 - [x] Upgraded pestphp/pest:^4.0 + pestphp/pest-plugin-laravel:^4.0 - [x] Pest v4.3.2, PHPUnit 12.5.8 installed - [x] Tests: 14 passed ## Phase 6: NPM Updates - [x] Removed eslint-plugin-tailwindcss (re-added by quality:update, incompatible with TW v4) - [x] Fixed eslint.config.mjs (removed tailwindcss settings block re-added by quality:update) - [x] `npm update` for minor/patch bumps - [x] Major updates: @vuepic/vue-datepicker ^12.1.0, @vueuse/core ^14.2.1, swiper ^12.1.0, vite-imagetools ^9.0.2, imagetools-core ^9.1.0, vite-plugin-vue-devtools ^8.0.6, vue-flatpickr-component ^12.0.0, globals ^17.3.0 - [x] Fixed `VueDatePicker` import: default → named export (v12 breaking change) - [x] ESLint stays at v9 (intentionally skipped) - [x] Build: success ## Phase 7: Final Verification - [x] Tests: 14 passed (41 assertions) - [x] Build: success (client + SSR) - [x] Pint: clean (fixed 2 lang files) - [x] `composer outdated --direct`: only predis/predis v2→v3 (out of scope) - [x] `npm outdated`: only eslint v9→v10, @eslint/js v9→v10 (intentionally skipped) - [x] `php artisan filament:upgrade`: assets published successfully - [x] `php artisan about`: Laravel 12.50.0 confirmed --- ## Final State | Package | Version | |---|---| | laravel/framework | 12.50.0 | | filament/filament | 5.2.1 | | livewire/livewire | 4.1.4 | | tailwindcss | 4.x | | pestphp/pest | 4.3.2 | | phpunit/phpunit | 12.5.8 | | vite | 7.3.1 | | @vitejs/plugin-vue | 6.x | | laravel-vite-plugin | 2.x | --- ## Exceptions & Issues Log 1. **codezero/composer-preload-files plugin error**: After removing codezero packages, `composer dump-autoload` failed. Fix: set `allow-plugins.codezero/composer-preload-files` to `false`. 2. **awcodes/filament-table-repeater blocks Filament v4**: No compatible version. Fix: removed package, replaced with standard Repeater. 3. **lara-zeus/spatie-translatable requires Filament v5**: Rector changed imports to LaraZeus namespace during v4 step, but package wasn't installable until v5. Caused transient FatalError. Fix: skip translatable during v4, install with v5. 4. **filament-v5 rector MissingInputException**: Running without input fails. Fix: pipe `echo "app"` to rector. 5. **Vite 7 peer dependency conflicts**: laravel-vite-plugin@^2 requires Vite 7. Fix: install vite@^7 with `--force`. 6. **@import must precede @source in TW v4**: CSS ordering constraint. Fix: move @import before @source directives. 7. **eslint-plugin-tailwindcss re-added by quality:update**: `strixi/laravel-quality` re-publishes eslint.config.mjs on `composer update`. Fix: remove the package and tailwindcss settings again after composer operations. 8. **@vuepic/vue-datepicker v12 default export removed**: Changed to named export. Fix: `import { VueDatePicker }` instead of default import. 9. **Add basic browser smoke tests after upgrade**: After completing the upgrade, set up `pestphp/pest-plugin-browser` + Playwright and create browser tests that seed pages (via `PageSeeder`) and visit key public routes (`/`, `/over-ons`, `/contact`). Use `assertNoSmoke()` to catch JS errors and console logs. This catches runtime issues (broken Inertia renders, missing assets, JS import errors) that unit/feature tests won't find. 10. **@vuepic/vue-datepicker v12 locale + format props changed**: The `locale` prop now requires a date-fns `Locale` object instead of a string. The `format` prop was renamed to `formats` with sub-properties (e.g. `formats.input`). Fix: `import {nl} from "date-fns/locale"`, change `:locale="'nl'"` to `:locale="nl"`, change `format="dd-MM-yyyy"` to `:formats="{ input: 'dd-MM-yyyy' }"`. 11. **Filament v5 evaluates column labels during initialization**: `$livewire->tableFilters` is null before filters are populated, causing "Trying to access array offset on null" errors. Fix: use null-safe access with `?? null` or `?? 'default'` defaults when accessing filter values in column labels/formatters.