245 lines
13 KiB
Markdown
245 lines
13 KiB
Markdown
# Claude's plan: the next Laravel upgrade (11 → 12, then 13)
|
|
|
|
**Author: Claude Code** · Written 2026-10-15 · Status: proposal, not started
|
|
|
|
Written to compare with Mistral Vibe's plan (`LARAVEL_UPGRADE_PLAN_VIBE.md`). Every fact about this codebase
|
|
below was checked on 2026-10-15 against branch `laravel-11` (8e01ebc4) and Packagist. How: see
|
|
[Appendix](#appendix-how-the-facts-were-checked).
|
|
|
|
---
|
|
|
|
## 1. Recommendation in short
|
|
|
|
1. **Go to Laravel 12 first, and plan Laravel 13 as a second project right after it.** Laravel 12 can keep
|
|
our heavily customised WireChat 0.2. Laravel 13 needs WireChat 0.6, the largest single risk in either
|
|
upgrade. Doing them separately keeps each step small and each rollback simple.
|
|
2. **Do the risky preparation on Laravel 11 first**, as normal small deploys: Carbon 3, removing two unused
|
|
packages, and taking the abandoned flatpickr package into our own code. The Laravel 12 upgrade itself is
|
|
then a small change.
|
|
3. **Reuse what worked for Laravel 11**: `scripts/upgrade.sh`, a local rehearsal on a copy of the database, a
|
|
rehearsal on a fictitious second platform, and a staged rollout from a separate branch.
|
|
4. **Timing matters.** Laravel 11 has had no security fixes since March 2026. Laravel 12 gets security fixes
|
|
only until 2027-02-24, so Laravel 13 has to follow soon after.
|
|
|
|
---
|
|
|
|
## 2. Where we stand
|
|
|
|
| | |
|
|
|---|---|
|
|
| Production | Laravel 11.57.0 since 2026-10-15, PHP 8.3.35, Livewire 3.8 |
|
|
| Other instances | to be listed (PHP version per server matters for Laravel 13) |
|
|
| Newest Laravel 12 | 12.70.0, needs PHP 8.2+, Carbon 3 |
|
|
| Newest Laravel 13 | 13.36.0, needs **PHP 8.3+**, Carbon 3, Symfony 7.4/8 |
|
|
| Support (verify on laravel.com/docs/master/releases) | 11: security fixes ended March 2026 · 12: security fixes until 2027-02-24 · 13: about a year longer than 12 |
|
|
|
|
---
|
|
|
|
## 3. What blocks the upgrade, in this codebase
|
|
|
|
### 3.1 Packages
|
|
|
|
Most packages already have a release for Laravel 12 and 13 **in the major version we use now** (Livewire 3,
|
|
Jetstream 5, Fortify, Sanctum, Scout 10, spatie permission 6, medialibrary 11, Reverb, WireUI 2,
|
|
laravel-localization 2, dompdf, excel 3, and others). The exceptions:
|
|
|
|
| Package | Use here | Laravel 12 | Laravel 13 | What to do |
|
|
|---|---|---|---|---|
|
|
| `asdh/laravel-flatpickr` | `<x-flatpickr>` in 21 views; we already override its component class | **no release** (stops at Laravel 11) | **no release** | Move it into the app (one class, 2 views, CSS/JS), drop the package |
|
|
| `nesbot/carbon` | pinned at `^2.72` | needs **Carbon 3** | Carbon 3 | Upgrade on Laravel 11 first (Laravel 11 supports Carbon 3) |
|
|
| `psr/simple-cache` | pinned at `^1.0` in our composer.json (left over from the skeleton) | Laravel 12.29+ needs `^3.0` | `^3.0` | Remove the pin or set `^3.0` |
|
|
| `robsontenorio/mary` | **not used** (only `config/mary.php`) | needs 2.x | needs 2.x | Remove the package and its config |
|
|
| `laradumps/laradumps` (dev) | **no `ds()` calls** | needs 5.x | needs 5.x | Remove |
|
|
| `cviebrock/eloquent-sluggable`, `-taggable` | 24 files | 12.x | 13.x | Version bump (these majors only follow Laravel) |
|
|
| `nunomaduro/collision` (dev) | test runner | 8.6+ (we have 8.5) | 8.x | Update within 8.x |
|
|
| `staudenmeir/*` (3 packages) | relations | newer minor | newer minor | Update within the current line |
|
|
| `namu/wirechat` | chat; **45 override and published view files** | 0.2.11 works | **0.6 required** | Laravel 13 only: a project of its own |
|
|
| `laravel/tinker`, `barryvdh/laravel-debugbar` (dev) | | current line works | new major | Laravel 13 only |
|
|
|
|
### 3.2 Carbon 3: the main code risk
|
|
|
|
Carbon 3 changed `diffIn*()`: the results are now **signed** (negative when the argument is in the past) and
|
|
**floats**. Laravel 11 accepts Carbon 3, so this can be fixed and deployed before Laravel 12.
|
|
|
|
- 28 `diffIn*()` calls in 20 files need review.
|
|
- Example that would break: `now()->diffInSeconds(Carbon::parse($lastLoginAt))` in
|
|
`InactiveProfileWarning2Mail` and `InactiveProfileWarningFinalMail` becomes negative.
|
|
- Calls that already pass `false` (signed) or cast to `(int)` need a check for the float, not the sign.
|
|
- 172 `Carbon::parse` and 45 `Carbon::now` calls: mostly unaffected, but run the full suite and look at
|
|
date displays (transaction lists, statements, inactivity mails, call expiry).
|
|
|
|
### 3.3 Laravel 12 upgrade-guide items checked against the code
|
|
|
|
| Change in Laravel 12 | Here |
|
|
|---|---|
|
|
| Carbon 3 required | yes, see 3.2 |
|
|
| `image` validation rule excludes SVG | not affected: the one upload rule uses `mimes:jpg,jpeg,png,svg` |
|
|
| `local` disk default root moves to `storage/app/private` | not affected: `config/filesystems.php` sets `root => storage_path('app')` |
|
|
| `HasUuids` generates UUIDv7 | not used |
|
|
| `mergeIfMissing` with nested arrays | not used |
|
|
| `Concurrency` result keys | not used |
|
|
| `Schema::getTables()` and similar, multi-schema | not used |
|
|
| `DatabaseTokenRepository` expiry in seconds | no custom repository |
|
|
| Route precedence for duplicate names, container default values, `Str::is` with newlines | low; covered by the test suite and smoke tests |
|
|
|
|
To do in Phase 2: read the full official guide once more, and diff the `laravel/laravel` skeleton 11 → 12
|
|
(config files) against ours.
|
|
|
|
### 3.4 Tests
|
|
|
|
- 466 test methods, 656 tests including data sets, green on Laravel 11.
|
|
- PHPUnit 10.5 can stay for Laravel 12.
|
|
- 20 test files still use `@test` docblock annotations. They work on PHPUnit 10 and 11, but PHPUnit 12
|
|
ignores them, so converting them to `#[Test]` attributes is preparation for later.
|
|
|
|
---
|
|
|
|
## 4. Phases
|
|
|
|
### Phase 0: Before starting (no code changes)
|
|
|
|
1. Finish the Laravel 11 follow-ups: a quiet prod log, `main` fast-forwarded to `laravel-11`, snapshot removed
|
|
(see the post-upgrade reminder in shared memory).
|
|
2. List every instance and its PHP version. Laravel 13 needs PHP 8.3 everywhere.
|
|
3. Decide: Laravel 12 then 13 (recommended), or straight to 13 (see [section 6](#6-alternatives-considered)).
|
|
|
|
### Phase 1: Preparation on Laravel 11 (small changes, normal deploys)
|
|
|
|
Each step is its own commit on `main`, tested with the full suite, and deployed with the normal
|
|
`deploy.sh`, so production gets smaller changes one at a time instead of one big jump.
|
|
|
|
1. **Carbon 3.**
|
|
- `nesbot/carbon` to `^3.8`.
|
|
- Review all 28 `diffIn*()` calls: keep the meaning ("how long ago" stays positive, whole numbers where
|
|
shown to people), with `abs()` and `(int)` or `floor()` where needed.
|
|
- Add tests for the inactivity mails and call expiry, which compute durations.
|
|
2. **Composer clean-up.**
|
|
- Remove the `psr/simple-cache` pin.
|
|
- Remove `robsontenorio/mary` (and `config/mary.php`) and `laradumps/laradumps`.
|
|
3. **Flatpickr into the app.**
|
|
- Move the component (our override in `app/Overrides/Asdh`), its two views and its CSS/JS into the app.
|
|
- Register it under the same name `flatpickr`, so the 21 views keep `<x-flatpickr>` unchanged.
|
|
- Remove the package, the override and its classmap entry.
|
|
- Test every date picker: transactions table, select period, profile form, posts, calls.
|
|
4. **Updates within the current lines.**
|
|
- Collision to 8.6+, the `staudenmeir` packages, and the newest Livewire 3.8.x.
|
|
- `composer update` within the constraints, then the full suite.
|
|
|
|
### Phase 2: Laravel 12 (branch `laravel-12`, staged rollout)
|
|
|
|
1. **Branch** `laravel-12` from `main` once Phase 1 is on `main`.
|
|
2. **Composer:**
|
|
- `laravel/framework ^12.0`, `cviebrock/eloquent-sluggable ^12.0`, `cviebrock/eloquent-taggable ^12.0`.
|
|
- `composer update -W`. Write down every major version that changed in the lock file.
|
|
3. **Upgrade guide and skeleton:** apply section 3.3, and diff the Laravel 11 and 12 skeleton config files
|
|
against ours.
|
|
4. **Our overrides.** For every package with an override or published views (Jetstream `HasProfilePhoto`,
|
|
Matchish, WireChat views, WireUI), diff the vendor original between the old and new version and port the
|
|
changes, as was done for WireChat 0.2.11.
|
|
5. **Tests:**
|
|
- Full suite green.
|
|
- Render the main Livewire pages.
|
|
- Smoke test by hand: log in, switch profile, payment, chat with an image, poll, search, mailing preview,
|
|
PDF statement, date pickers.
|
|
- All 4 themes.
|
|
6. **Rehearsals with `scripts/upgrade.sh`:**
|
|
- a clone of `main` with a copy of the database (success path, rollback before and after going live)
|
|
- the fictitious Uuro platform (`TIMEBANK_CONFIG=uuro`, own config, own theme)
|
|
- a routine deploy afterwards
|
|
|
|
The script is version-neutral. It reads the PHP requirement from the target lock file, but check its
|
|
texts ("Laravel 10 -> 11") and the version check in step 6.
|
|
7. **Docs:**
|
|
- `references/UPGRADE_LARAVEL_11_TO_12.md` (the 10 → 11 guide with the changes)
|
|
- versions in `AGENTS.md`
|
|
- the post-upgrade checklist
|
|
8. **Rollout:**
|
|
- the development server first, then production, then other instances: `--target origin/laravel-12`
|
|
- fast-forward `main` once all instances are upgraded or ready
|
|
|
|
### Phase 3: Laravel 13 (separate project, start right after Laravel 12 is live)
|
|
|
|
1. **PHP 8.3 on every instance** (production already has 8.3.35).
|
|
2. **WireChat 0.2 → 0.6**, the biggest item:
|
|
- Read the changelogs of 0.3 to 0.6.
|
|
- Re-port our 45 override and view files.
|
|
- Check our `_finishUpload` override (Livewire 3.8 signed uploads), private attachments and the listeners
|
|
that work across profile switches.
|
|
- Extend the chat tests first, so regressions show.
|
|
- This can be prepared on Laravel 12 if WireChat 0.6 also supports 12 (it does: `^11|^12|^13`).
|
|
3. **Package majors:** tinker 3, debugbar 4 (dev), sluggable and taggable 13; check Symfony 7.4/8,
|
|
Monolog 3.10 and Guzzle.
|
|
4. **Read the official 12 → 13 upgrade guide.** I do not list its items from memory here; they have to be
|
|
checked against the code, as in 3.3.
|
|
5. **Same method** as Phase 2: branch `laravel-13`, rehearsals, staged rollout.
|
|
6. **Finish before 2027-02-24**, when Laravel 12 gets its last security fixes.
|
|
|
|
---
|
|
|
|
## 5. Risks and how to limit them
|
|
|
|
| Risk | Impact | Limit it by |
|
|
|---|---|---|
|
|
| Carbon 3 changes durations silently (negative or fractional) | Wrong texts in mails ("-3 days ago"), wrong expiry decisions | Phase 1 on Laravel 11 with tests per `diffIn*()` call; check mails per theme |
|
|
| Flatpickr removal breaks date pickers | Forms where a date cannot be entered | Same tag name; test every page with a picker |
|
|
| A package's new major changes behaviour (sluggable, taggable) | Wrong slugs or tags | Read their changelogs; tests on posts, tags, calls |
|
|
| Override drift (vendor views and classes) | Chat or profile photo regressions | Diff vendor originals old to new, as for WireChat 0.2.11 |
|
|
| WireChat 0.6 (Laravel 13) | Chat broken | Separate phase, tests first, prepare on Laravel 12 |
|
|
| A server with older PHP | Laravel 13 cannot run there | List PHP versions in Phase 0; `upgrade.sh` refuses too old PHP |
|
|
| A long-running branch drifts from `main` | Rebase conflicts during the rollout | Short phases; do not rebase after a server upgraded from the branch |
|
|
| Two AI agents in one folder | Conflicting edits during the upgrade | Worklog entries; one agent per phase step; the user dispatches |
|
|
|
|
---
|
|
|
|
## 6. Alternatives considered
|
|
|
|
- **Straight to Laravel 13 in one project.** One rollout instead of two, and the longest support. But it
|
|
combines Carbon 3, all package majors, the Laravel 12 and 13 framework changes and WireChat 0.6 in one
|
|
step, so a failure is harder to pin down and a rollback undoes more. Reasonable only if Phase 1 is done
|
|
and WireChat 0.6 has been tried separately first, so in practice it is the same work in a different order.
|
|
- **Laravel 12 only, and stay there.** Its security support ends 2027-02-24. Not an end state.
|
|
- **Doing Phase 1 inside the Laravel 12 branch.** Possible, but then production gets Carbon 3, flatpickr
|
|
and the framework at once. Phase 1 on Laravel 11 gives smaller, easier-to-check production changes.
|
|
|
|
---
|
|
|
|
## 7. Effort (working time, rough)
|
|
|
|
| Phase | Effort |
|
|
|---|---|
|
|
| 0 | an hour (an inventory and a decision) |
|
|
| 1 | 1 to 2 days (Carbon review is most of it), plus normal deploys |
|
|
| 2 | 2 to 3 days, including rehearsals, plus a rollout evening per instance |
|
|
| 3 | 3 to 5 days, mainly WireChat 0.6, plus rehearsals and rollout |
|
|
|
|
---
|
|
|
|
## 8. Open questions for the user
|
|
|
|
1. Which instances exist besides timebank.cc, and on which PHP version?
|
|
2. Is there still a development server to upgrade first?
|
|
3. Agree with Laravel 12 first and 13 straight after, or prefer straight to 13?
|
|
4. Is Mary perhaps planned for future UI work? (It is not used now.)
|
|
|
|
---
|
|
|
|
## Appendix: how the facts were checked
|
|
|
|
- **Package support:**
|
|
- The Packagist metadata of every package in `composer.json` was read with
|
|
`.agents/archive/laravel-compat-check.py` (output: `.agents/archive/laravel-compat-2026-10-15.txt`).
|
|
- For each package it checks the newest version supporting Laravel 12 and 13, overall and within the
|
|
major version in use.
|
|
- **Dependency solving:** `composer update --dry-run -W` in a copy of `composer.json`/`composer.lock` with
|
|
`laravel/framework ^12.0` and `nesbot/carbon ^3.8`. It stopped at `cviebrock/eloquent-sluggable` (needs
|
|
12.x) and `psr/simple-cache` (`^1.0` pin versus Laravel 12.29+).
|
|
- **Code impact:** `grep` counts in `app/`, `resources/views/`, `routes/`, `database/` and `tests/`:
|
|
- `<x-flatpickr>`: 21 files
|
|
- `diffIn*()`: 28 calls in 20 files
|
|
- `Mary`: no use
|
|
- `ds()`: none
|
|
- WireChat overrides: 45 files
|
|
- `@test` annotations: 20 files
|
|
- **Not verified here:** Laravel 13's exact upgrade-guide items and support end date (to be read from the
|
|
official documentation in Phase 3).
|