Skip to content

Upgrade Laravel from 10 to 13 #853

@edwh

Description

@edwh

Laravel 10 to 13 Upgrade Plan for Restarters.net

Document Date: May 19, 2026
Current State: Laravel 10.48.29 (PHP 8.2)
Target State: Laravel 13.x (PHP 8.3+ minimum, PHP 8.4 recommended)


Executive Summary

Upgrading from Laravel 10 to Laravel 13 is a multi-step sequential process. While Laravel 13 was released March 17, 2026 and is not an LTS version (no LTS since v6), it will receive bugfixes until Q3 2027 and security updates until Q1 2028. The upgrade path is: 10 → 11 → 12 → 13. A direct jump is not supported.

Key Challenge: laravelcollective/html is an abandoned package that will not work with Laravel 11+, requiring migration to an alternative before upgrading.


Current Application State

Framework & Runtime

  • Laravel Version: 10.48.29
  • PHP Version: 8.2-fpm (Dockerfile) / ^8.1 (composer.json)
  • Platform: Fly.io with PHP 8.2-fpm Docker image
  • Database: MySQL (Laravel 13.3+ pulls in Symfony 8 which strongly recommends PHP 8.4)

Key Dependencies

Package Current Version Status
laravel/framework ^10.48 Active, will be EOL Feb 2025 for bugs, need to move
intervention/image ^2.7 Currently OK, but v3 has major breaking changes
owen-it/laravel-auditing ^13.1 Needs upgrade to ^14.0 for Laravel 11+
laravelcollective/html ^6.4 CRITICAL: Abandoned, incompatible with Laravel 11+
spatie/laravel-validation-rules ^3.4 Compatible through Laravel 13
spatie/calendar-links ^1.6 Compatible through Laravel 13
spinen/laravel-discourse-sso ^2.8 Need to verify compatibility per version
laravel/tinker ^2.8 Needs verification
barryvdh/laravel-debugbar ^3.8 (dev) May need updates
phpunit/phpunit ^9.5.10 (dev) May need updates for PHP 8.3+ compatibility

Frontend Stack

  • Vue.js: 2.7.14 (Vue 2 - not affected by Laravel upgrade)
  • Vite: Used for asset bundling
  • Build System: npm/Vite-based (not dependent on Laravel version)

Code Using Affected Packages

  • intervention/image: Heavy usage in app/Helpers/FixometerFile.php (image processing, thumbnails, orientation)
  • owen-it/laravel-auditing: 18+ uses across models (Group, Alert, UserGroups, User, Device, Party, etc.)
  • laravelcollective/html: No direct Html:: facade usage detected, but package is declared as dependency
  • spatie packages: Limited use (validation rules, calendar links in PartyController and GroupController)

Test Suite

  • PHPUnit: Version 9.5.10, configured in phpunit.xml
  • Coverage: Feature and Unit tests in tests/ directory
  • Database: Uses test database configuration for tests

Target State: Laravel 13

Framework & Runtime

  • Laravel Version: 13.x (released March 17, 2026)
  • PHP Version: 8.3 minimum, 8.4+ recommended (8.5 supported)
    • Critical: Laravel 13.3+ pulls in Symfony 8 which effectively requires PHP 8.4
  • Platform: Update Dockerfile.fly and Dockerfile from PHP 8.2 to PHP 8.4
  • Database: MySQL compatibility unchanged

Support Timeline

  • Bug Fixes: Until Q3 2027 (September 2027)
  • Security Updates: Until Q1 2028 (March 2028)
  • Note: Laravel 13 is not an LTS version (no LTS releases since Laravel 6)

New Features (Information Only)

  • Laravel AI SDK for text generation, tool-calling agents, embeddings, audio, images, vector stores
  • Configuration attribute improvements for models, jobs, commands
  • Continued quality-of-life improvements

Upgrade Path: 10 → 11 → 12 → 13

Why Sequential Upgrades?

Laravel upgrades must be performed sequentially due to:

  • Each version introduces breaking changes that depend on previous versions being installed
  • Dependency conflicts occur when jumping multiple versions
  • Package ecosystem needs time to publish compatible versions for each release

Overall Upgrade Sequence

Phase 1: Preparation (Pre-upgrade)
├─ Resolve laravelcollective/html before ANY upgrade
├─ Verify intervention/image upgrade strategy
├─ Upgrade PHP runtime to 8.3 minimum (8.4 recommended)
└─ Ensure full test suite passes at current state

Phase 2: Laravel 10 → 11
├─ Create feature branch
├─ Update composer dependencies
├─ Run automated changes via Laravel Shift (optional)
├─ Code review and manual fixes
└─ Full test suite validation

Phase 3: Laravel 11 → 12
├─ Update composer dependencies
├─ Handle Carbon 3.x requirement
├─ Code review
└─ Test suite validation

Phase 4: Laravel 12 → 13
├─ Update composer dependencies
├─ Update PHP to 8.4 if not already done
├─ Handle new configuration attributes
├─ Test suite validation

Phase 5: Post-upgrade
├─ Performance testing and optimization
├─ Security audit (Sentry integration verify)
├─ Deploy to staging → production
└─ Monitor for edge cases

Step 1: Preparation (Before Any Upgrade)

1.1 Address laravelcollective/html (CRITICAL BLOCKER)

Status: Package is abandoned and incompatible with Laravel 11+

Options:

Option A: Migrate to spatie/laravel-html (Recommended for new code)

  • Effort: Medium (3-8 hours depending on usage)
  • Risk: High if heavily used, Low if minimal usage
  • Steps:
    1. Audit current usage: grep -r "Html::\|collective/html" app/ and resources/views/
    2. Install spatie/laravel-html via composer
    3. Update view files to use Spatie syntax
    4. Spatie HTML has same methods but different syntax/argument order
    5. Laravel Shift can automate this conversion if you use it (saves hours)
    6. Test extensively as syntax differs significantly

Current Status: Grep shows no direct Html:: usage in PHP code. Check Blade templates for {{ Html:: or Blade form/html directives.

Option B: Use LaravelLux/Html (Drop-in replacement)

  • Effort: Very Low (30 minutes)
  • Risk: Low
  • Steps:
    1. Replace laravelcollective/html with laravellux/html in composer.json
    2. Replace namespace Collective\Html with LaravelLux\Html across app
    3. No other changes necessary for most applications
    4. Maintains same API, easier migration path

Recommendation: Start with Option B (LaravelLux) due to minimal code changes and drop-in compatibility. If issues arise, migrate to Spatie later.

Timeline: Must complete before Laravel 11 upgrade


1.2 Verify Blade Templates for Html Facade Usage

grep -r "Html::\|@form\|@select\|@text\|@textarea\|@checkbox" resources/views/ --include="*.blade.php"

If significant usage found, budget 4-8 additional hours for refactoring.


1.3 Upgrade PHP Runtime to 8.3+ (Required for Laravel 13)

Current State: Docker images use PHP 8.2-fpm

Actions Required:

  1. Update Dockerfile:
    FROM php:8.4-fpm  # Recommended, or 8.3-fpm as minimum
  2. Update Dockerfile.fly:
    FROM php:8.4-cli AS builder
    FROM php:8.4-fpm
  3. Update composer.json PHP requirement from ^8.1 to ^8.3
  4. Test locally with new PHP version before upgrading Laravel
  5. Verify all PHP extensions available in new image (gd, curl, zip, bcmath, etc.)

Effort: 1-2 hours
Risk: Low to Medium (depends on extension availability in PHP 8.4 image)

Timeline: Can do this now or during Laravel 11 upgrade. Doing it first allows testing with current Laravel 10 on PHP 8.3.


1.4 Run Full Test Suite at Current State

task docker:test:phpunit
npm test  # Playwright e2e tests
npm run jest  # JS unit tests

Ensure all tests pass before beginning any upgrades. Document baseline:

  • Total test count
  • Coverage percentage (if tracked)
  • Execution time

Effort: 30 minutes to 2 hours (depends on test duration)
Risk: None (verification only)


Step 2: Laravel 10 → 11 Upgrade

2.1 Key Breaking Changes for Laravel 11

Category Change Impact App Impact
PHP Minimum PHP 8.2.0 required (was 8.1) Medium App already on 8.2, will move to 8.3+
HTTP Client Requires curl 7.34.0+ Low Unlikely to affect
Packages Must publish migrations for Cashier, Passport, Sanctum, Spark, Telescope Medium App doesn't use these; others may use customs
Rate Limiting Constructor now accepts seconds (was minutes) Low Check if custom rate limiting middleware exists
spatie/once Removed, Laravel provides native once() Low Verify app doesn't depend on external package
MariaDB Driver New dedicated MariaDB driver available Low Optional, can use mysql driver
owen-it/laravel-auditing Must upgrade to v14.0+ CRITICAL App uses extensively

2.2 owen-it/laravel-auditing v13 → v14 Migration

Current Version: ^13.1
Required Version: ^14.0 for Laravel 11+

Changes in v14:

  • Requires PHP >= 8.2, supports Laravel 11, 12, 13
  • Breaking change in config file: rename 'resolver' to 'resolvers', move user resolver to 'user' key
  • Must publish and update migrations if customized
  • Models already implement Auditable interface correctly (18 models in app)

Steps:

  1. Update composer.json: "owen-it/laravel-auditing": "^14.0"
  2. Review config/audit.php - check if 'resolver' key exists:
    • If exists, rename to 'resolvers' and nest user resolver properly
    • If already using 'resolvers' structure, no change needed
  3. Publish updated config if needed: php artisan vendor:publish --provider="OwenIt\\Auditing\\AuditingServiceProvider"
  4. Run migrations: php artisan migrate
  5. Test audit functionality for all affected models

Timeline: 1-2 hours
Risk: Medium (breaking config change, but Laravel Shift can automate)


2.3 intervention/image Strategy Decision

Current Version: ^2.7
Available Versions: v2.x (stable for 10), v3.x (modern, with breaking changes)

Decision Point:

  • Keep v2.x: Works with Laravel 10-13 long term, but deprecated

    • Pros: Minimal code changes, stable, works through all versions
    • Cons: No new features, will eventually be unsupported
    • Effort: None, upgrade in step 4 when ready
  • Upgrade to v3.x: Required for future-proofing, modern architecture

    • Pros: Modern, maintained, better performance
    • Cons: Major breaking changes, significant refactoring required
    • Required changes:
      • Remove Image:: static facade usage
      • All calls become $manager->read() then method chaining
      • Driver config changes (string "gd" → Intervention\Image\Drivers\Gd\Driver)
      • Color values can't be arrays anymore
      • Can't create images from URI directly (must fetch first)
    • Effort: 6-12 hours (FixometerFile.php and image-related code needs refactoring)
    • Risk: High (major architectural change in image handling)

Recommendation for Laravel 11 Upgrade:

  • Keep v2.x for now (Laravel 11 supports it fine)
  • Schedule v3.x migration for a separate story/PR
  • This keeps upgrade focused and reduces risk
  • Can add to post-upgrade optimization phase

Decision: Use v2.x through Laravel 13 upgrade. Plan v3.x migration separately.


2.4 Update composer.json for Laravel 11

{
  "require": {
    "php": "^8.3",
    "laravel/framework": "^11.0",
    "laravel/tinker": "^2.9",
    "laravel/ui": "^4.5",
    "owen-it/laravel-auditing": "^14.0",
    // Other packages stay same unless indicated below
    "barryvdh/laravel-translation-manager": "^0.6.2",
    // ... rest of dependencies
  },
  "require-dev": {
    "laravel/dusk": "^7.12",
    "barryvdh/laravel-debugbar": "^3.11",
    "phpunit/phpunit": "^10.5",
    "nunomaduro/collision": "^7.4",
    "spatie/laravel-ignition": "^2.8",
    // ... rest of dev dependencies
  }
}

Package Updates for L11:

  • laravel/framework: ^11.0 (from ^10.48)
  • laravel/tinker: ^2.9 (from ^2.8)
  • laravel/ui: ^4.5 (from ^4.2)
  • owen-it/laravel-auditing: ^14.0 (from ^13.1)
  • laravel/dusk: ^7.12 (from ^7.4)
  • barryvdh/laravel-debugbar: ^3.11 (from ^3.8)
  • phpunit/phpunit: ^10.5 (from ^9.5.10) - PHP 8.3 requires PHPUnit 10+
  • nunomaduro/collision: ^7.4 (from ^6.3)
  • spatie/laravel-ignition: ^2.8 (from ^2.0)
  • Symfony packages: ^7.0 (from ^6.2)

2.5 Use Laravel Shift (Optional but Recommended)

What Shift Automates:

  • Updates composer.json dependencies
  • Updates core Laravel configuration files
  • Converts model $casts property to method if applicable
  • Stubs new interface methods
  • Detects and flags deprecated code patterns
  • Updates namespace imports
  • Handles config file merges
  • Time Savings: ~2 hours

What Shift Does NOT Do:

  • Migrate laravelcollective/html to alternative
  • Refactor custom business logic
  • Update application-specific code patterns
  • Fix tests

Cost: ~$99 for a single shift per repo (laravelshift.com)

Decision: Recommended if budget allows. Can also do manually by following official upgrade guide.


2.6 Manual Code Changes Required for Laravel 11

Even with Laravel Shift, manual review needed for:

  1. Check config/audit.php (owen-it/laravel-auditing):

    // OLD (if exists)
    'resolver' => [...],
    
    // NEW (if not already)
    'resolvers' => [
        'user' => [...],
    ],
  2. Remove spatie/once if used:

    grep -r "spatie/once\|use function Once" app/

    Replace with Laravel's native once() function.

  3. Rate limiting config (if custom):

    • Check if app defines rate limiting middleware with minute-based limits
    • Can now use per-second granularity, but old code still works
    • No change required unless optimizing
  4. Service Provider review:

    • Check AppServiceProvider::register() and boot()
    • Verify any deferred services still work with new container bindings
    • Check for any explicit Illuminate\* version-specific code
  5. Middleware stack in app/Http/Kernel.php:

    • Verify all middleware classes still exist
    • Check for deprecated middleware that moved in Laravel 11
  6. Database migrations:

    • Run php artisan migrate and verify all migrations complete
    • If custom migrations exist, check for deprecated schema methods

2.7 Testing Strategy for Laravel 11

# 1. Run full test suite
task docker:test:phpunit
npm test

# 2. Verify audit functionality
php artisan tinker
# In tinker: test audit trails on a model
$user = App\User::first();
$user->update(['name' => 'Test']);
App\OwenIt\Auditing\Models\Audit::latest()->first(); # Verify audit recorded

# 3. Test image processing
php artisan tinker
# Test image operations in FixometerFile::upload()

# 4. Smoke tests
# - Login as admin (jane@bloggs.net / passw0rd)
# - Create/edit a party
# - Upload images
# - Create a group
# - View reports/stats

2.8 Laravel 10 → 11 Summary

Aspect Effort Risk Notes
composer.json updates 1 hour Low With Shift: 15 min
owen-it/laravel-auditing v14 1-2 hours Medium Config file changes
Code review & fixes 2-4 hours Medium Depends on findings
PHP 8.3 testing 1-2 hours Low Already have 8.3 plan
Test suite validation 2-4 hours Medium Must pass all tests
Total Estimated Effort 7-13 hours Medium Can use Shift to save 2+ hours

Step 3: Laravel 11 → 12 Upgrade

3.1 Key Breaking Changes for Laravel 12

Category Change Impact App Impact
Carbon 3.x v2.x no longer supported, must use v3.x Medium May already be on v3.x if upgraded with Shift
Database Classes Blueprint, Grammar constructors require Connection Low Only affects apps manually instantiating these
Schema Methods getTables(), getViews(), getTypes() include all schemas by default Low Only if app uses these methods
Quality Focus Minimal breaking changes, focus on QoL improvements Low Most apps upgrade with no code changes

3.2 Carbon 2.x → 3.x (If Not Already Done)

Check Current Version:

composer show | grep carbon

If on Carbon 2.x:

composer require "nesbot/carbon:^3.0"

Changes Needed:

  • Most code is compatible, but some formatting methods changed
  • Check config/app.php for timezone settings
  • Run tests to ensure datetime handling works

Timeline: 30 minutes to 1 hour
Risk: Low (Carbon maintains good backward compatibility in most scenarios)


3.3 Database Schema Method Review

Check if app uses these methods:

grep -r "Schema::getTables\|Schema::getViews\|Schema::getTypes" app/ database/

Current Behavior Change (Laravel 12):

  • These methods now include results from ALL schemas by default
  • Old behavior: only primary schema
  • Fix: Pass schema argument if need specific schema:
    Schema::getTables(schema: 'public')  // Laravel 12

Effort: 30 minutes if found, 0 if not used


3.4 composer.json Updates for Laravel 12

{
  "require": {
    "php": "^8.3",
    "laravel/framework": "^12.0",
    "nesbot/carbon": "^3.0",
    "owen-it/laravel-auditing": "^14.0",
    // Check all packages for ^12.0 support
  },
  "require-dev": {
    "laravel/dusk": "^8.0",
    "barryvdh/laravel-debugbar": "^3.14",
    "phpunit/phpunit": "^10.5",
    "spatie/laravel-ignition": "^2.10",
    // ... etc
  }
}

Package Version Bumps:

  • laravel/framework: ^12.0 (from ^11.0)
  • laravel/dusk: ^8.0 (from ^7.12)
  • barryvdh/laravel-debugbar: ^3.14 (from ^3.11)
  • spatie/laravel-ignition: ^2.10 (from ^2.8)

3.5 Laravel 11 → 12 Summary

Aspect Effort Risk Notes
composer.json updates 30 min Low Straightforward version bumps
Carbon 3.x verification 30 min Low Usually automatic with Shift
Code review 1-2 hours Low Few breaking changes
Test suite validation 2-3 hours Low Most tests should pass as-is
Total Estimated Effort 4-6 hours Low Smoothest upgrade of the three

Step 4: Laravel 12 → 13 Upgrade

4.1 Key Changes for Laravel 13

Category Change Impact App Impact
PHP Version Requires PHP 8.3 minimum, 8.4+ recommended Medium Must upgrade PHP if not done
Symfony 8 Laravel 13.3+ pulls Symfony 8 (requires PHP 8.4) High Recommend PHP 8.4
Breaking Changes Zero documented breaking changes Low Config/attribute improvements only
Configuration Attributes Models, Jobs, Commands use attributes Optional Modernization, not required
Laravel AI SDK New first-party AI integration Optional Not used in current app

4.2 PHP 8.4 Upgrade (Strongly Recommended)

If not already on PHP 8.4:

# In Dockerfile and Dockerfile.fly
FROM php:8.4-fpm
FROM php:8.4-cli AS builder

Why PHP 8.4?

  • Required for Laravel 13.3+ due to Symfony 8 dependencies
  • Better performance, JIT compiler improvements
  • Recommended by Laravel team for 2026+

Risk: Low (PHP 8.4 is stable and well-tested)
Effort: 30 minutes to test in Docker


4.3 Configuration Attribute Improvements (Optional Modernization)

Laravel 13 introduces configuration attributes for:

  • Models: #[Attribute] for fillable, hidden, casts metadata
  • Jobs: Queue configuration via attributes
  • Commands: Command registration and options via attributes

Current State: App likely uses traditional config properties
Recommendation: Not required for upgrade - can modernize incrementally after upgrade

Example (Optional):

// OLD (still supported)
class Device extends Model implements Auditable {
    protected $fillable = ['name', 'description'];
    protected $casts = ['created_at' => 'datetime'];
}

// NEW (optional in Laravel 13)
#[Fillable(['name', 'description'])]
#[Cast('created_at', 'datetime')]
class Device extends Model implements Auditable {
    // ...
}

4.4 composer.json Updates for Laravel 13

{
  "require": {
    "php": "^8.3",  // Or ^8.4 for PHP 8.4 minimum
    "laravel/framework": "^13.0",
    "owen-it/laravel-auditing": "^14.0",
    // Verify all packages support Laravel 13
    "spatie/laravel-validation-rules": "^4.0",  // May need bump for L13
    "spatie/calendar-links": "^1.10",
    // ... etc
  },
  "require-dev": {
    "laravel/dusk": "^8.0+",
    "barryvdh/laravel-debugbar": "^3.15+",
    "phpunit/phpunit": "^10.5+",
    "spatie/laravel-ignition": "^2.14",
  }
}

4.5 Compatibility Verification Before Laravel 13

Check Each Package for L13 Support:

# For each major package:
composer show <package-name> --latest

# Or visit Packagist:
# https://packagist.org/packages/<vendor>/<package>

Critical Packages to Check:

  • spatie/laravel-validation-rules - may need v4.0 or higher
  • spatie/calendar-links - check for Laravel 13 support
  • spinen/laravel-discourse-sso - verify compatibility
  • Any custom forked packages (e.g., wouternl/laravel-drip from custom repo)

Timeline: 1-2 hours for research and testing


4.6 Laravel 12 → 13 Summary

Aspect Effort Risk Notes
PHP 8.4 Docker update 30 min Low Straightforward image change
Verify package compatibility 1-2 hours Medium Some packages may lag
composer.json updates 30 min Low Version bump exercise
Code review (zero breaking changes) 1 hour Low Attribute migration is optional
Test suite validation 2-3 hours Low Should pass with zero changes
Total Estimated Effort 5-7 hours Low Easiest upgrade step

Special Considerations: Discourse SSO Integration

Package: spinen/laravel-discourse-sso ^2.8

Laravel Compatibility Check:

composer show spinen/laravel-discourse-sso

Research Required Before Each Step:

  1. Check GitHub releases for Laravel 11, 12, 13 compatibility
  2. Verify SSO still works after each upgrade
  3. Test Discourse account sync during upgrade testing

Risk: Medium (external service integration)
Testing Required: Login via Discourse on each version


Special Considerations: Fly.io Deployment

Dockerfile Updates Required

Current (PHP 8.2):

FROM php:8.2-cli AS builder
FROM php:8.2-fpm

For Laravel 13 (PHP 8.4):

FROM php:8.4-cli AS builder
FROM php:8.4-fpm

Database Migrations on Fly

Current Process (from fly.toml):

# Migrations run in startup.sh instead of release_command because
# release_command machines don't have 6PN network access to the DB.

Verify Still Works:

  • Ensure startup.sh runs migrations correctly
  • Test on staging Fly environment before production
  • May need to adjust Symfony version compatibility in migrations if needed

Environment Variables

fly.toml PHP environment variables should still work:

  • No Laravel 13-specific new env vars required
  • Monitor SENTRY integration on production after upgrade

Package-Specific Deep Dives

intervention/image: Keep v2.x or Upgrade to v3.x?

Recommendation: Keep v2.x through Laravel 13, plan v3.x separately

Why:

  • v2.7 continues to work with Laravel 10-13
  • v3 migration is a separate large effort (6-12 hours)
  • Separating concerns reduces risk per PR/story
  • Can schedule v3 upgrade as follow-up improvement

If Future v3.x Upgrade Needed:

Files That Need Changes:

  • app/Helpers/FixometerFile.php - heavy Image:: usage (thumbnails, orientation, cropping)
  • Any controllers uploading/processing images
  • Search for Intervention\Image\ImageManagerStatic as Image

v3.x Breaking Changes Summary:

// v2.x pattern (currently used)
Image::make($path)->orientate()->save($path);

// v3.x pattern (required change)
$manager = new ImageManager(new GdDriver()); // or ImagickDriver
$image = $manager->read($path);
$image->rotate(/* ... */)->save();  // orientate() may not exist, need alternate

// v3 Driver config (not string anymore)
'driver' => Intervention\Image\Drivers\Gd\Driver::class,

Recommendation: Schedule as Q2-Q3 2026 improvement story, not part of upgrade.


laravelcollective/html: Migration Path Confirmation

Decision: Use LaravelLux/Html (Option B)

Migration Steps (Simple):

  1. In composer.json:
    "require": {
      "laravellux/html": "^2.0"
    }
   Remove: `"laravelcollective/html": "^6.4"`

2. Search and replace in app code:
   ```bash
   grep -r "Collective\\Html" app/ config/ --include="*.php"
   grep -r "collective/html" . --include="*.php" --include="*.blade.php"
  1. Replace namespace:

    // OLD
    use Collective\Html\HtmlBuilder;
    
    // NEW
    use LaravelLux\Html\HtmlBuilder;
  2. In config/app.php providers:

    // OLD
    Collective\Html\HtmlServiceProvider::class,
    
    // NEW
    LaravelLux\Html\HtmlServiceProvider::class,
  3. Verify Blade directives still work:

    {{-- These should work as-is with LaravelLux --}}
    {!! Form::open(['url' => 'test']) !!}
    {!! Form::text('name') !!}
    {!! Form::close() !!}

Timeline: 1-2 hours
Risk: Low (drop-in replacement, same API)

When: Complete before Laravel 11 upgrade (blocking issue)


owen-it/laravel-auditing v14.x Configuration

Config File Location: config/audit.php

v13 → v14 Breaking Change:

// v13 (OLD)
'resolvers' => [
    'resolver' => [
        'user' => [
            'model' => App\User::class,
            'morph_prefix' => 'user',
        ],
    ],
],

// v14 (NEW)
'resolvers' => [
    'user' => [
        'model' => App\User::class,
        'morph_prefix' => 'user',
    ],
],

Changes:

  • Remove outer 'resolver' wrapper
  • Move user resolver directly under 'resolvers'
  • Publishing config: php artisan vendor:publish --provider="OwenIt\\Auditing\\AuditingServiceProvider" --tag="config"

Models Using Auditable (18 instances):

  • app/Group.php
  • app/Alert.php
  • app/UserGroups.php
  • app/User.php
  • app/Device.php
  • app/Party.php (and others)

Testing:

# After upgrade, verify audits are recorded
php artisan tinker
>>> $user = App\User::find(1);
>>> $user->update(['name' => 'Test Name']);
>>> App\OwenIt\Auditing\Models\Audit::where('auditable_type', 'App\\User')->latest()->first();

Should see audit trail of the update.


Spatie Packages Status

Current Usage:

  • spatie/laravel-validation-rules ^3.4 - for Delimited rule
  • spatie/calendar-links ^1.6 - for event calendar
  • spatie/laravel-ignition ^2.0 - debug error pages (dev)

Compatibility:

  • All maintain Laravel 11, 12, 13 support
  • May need version bumps (v3.x → v4.x for validation-rules)
  • Ignition tracks Laravel closely

No Breaking Changes Expected - these are well-maintained by Spatie.


Laravel Shift: Tool Overview

What Laravel Shift Automates

Dependency Management:

  • Rewrites composer.json with new versions
  • Resolves transitive dependencies correctly
  • Handles --with-all-dependencies flag automatically

Framework Updates:

  • Updates Laravel configuration files (app.php, database.php, etc.)
  • Merges new config keys with existing custom keys
  • Updates service provider registrations

Code Detection & Stubs:

  • Detects deprecated code patterns
  • Converts model $casts to method signatures (if desired)
  • Generates interface method stubs for compatibility
  • Finds and flags suspicious patterns

Dependency-Specific Updates:

  • Handles owen-it/laravel-auditing config changes
  • Updates spatie package configurations
  • Manages database migration transitions

VCS Integration:

  • Creates atomic commits (one change per commit)
  • Opens pull request for review
  • Provides detailed descriptions of each commit

What Shift Does NOT Do

  • laravelcollective/html migration - You must choose alternative and migrate manually
  • intervention/image v2→v3 migration - Too complex, requires developer judgment
  • Custom business logic refactoring - Only framework updates
  • Test updates - Tests must be reviewed and fixed manually
  • Architecture decisions - Doesn't change app design patterns

Cost-Benefit Analysis

Cost: ~$99 per shift per repository
Time Saved: ~2 hours per major upgrade (total 6 hours for 10→13)
Total Savings: ~$297 for all 3 shifts, saves ~6 hours of developer time
ROI: Positive if developer time > $16/hour equivalent opportunity cost

Recommendation: Use Shift for at least Laravel 11 upgrade. Can do 12→13 manually as breaking changes are minimal.

How to Use Shift

  1. Create a fresh feature branch from develop
  2. Push to GitHub
  3. Visit laravelshift.com
  4. Authenticate with GitHub
  5. Select repo and Laravel version target
  6. Let Shift run (takes 5-10 minutes)
  7. Review PR created by Shift
  8. Merge or request changes
  9. Continue with manual testing

Testing Strategy Across All Upgrades

Unit Test Phase

# After each Laravel upgrade
task docker:test:phpunit

# Specific test suites
./vendor/bin/phpunit tests/Feature/Events/
./vendor/bin/phpunit tests/Feature/Admin/
./vendor/bin/phpunit tests/Unit/

Integration Test Phase

# After test suite passes
npm test  # Playwright e2e tests
npm run jest  # JS unit tests

Manual Smoke Tests

Admin Login:

  1. Start app: task docker:up-debug
  2. Navigate to: http://localhost:8001
  3. Login as jane@bloggs.net / passw0rd
  4. Verify dashboard loads

Party Creation:

  1. Create new party (Events → New)
  2. Verify form renders correctly
  3. Submit and verify save

Image Processing:

  1. Upload image to party/group
  2. Verify image processing (thumbnails, orientation)
  3. Check file storage (S3 on prod, local on dev)

Discourse Integration:

  1. Verify Discourse SSO link works
  2. Create test user account
  3. Sync with Discourse

Database Audit Trail:

  1. Modify a record via admin
  2. Check audit trail is recorded
  3. Verify User model in audit is correct

Risk Assessment & Mitigation

High Risk Areas

  1. laravelcollective/html Dependency

    • Risk: Blocks Laravel 11+ upgrade if not addressed
    • Mitigation: Replace with LaravelLux before any upgrade
    • Contingency: Have both packages installed during transition period
  2. owen-it/laravel-auditing Config Changes

    • Risk: Audit trails may break if config wrong
    • Mitigation: Test audit recording in tinker after upgrade
    • Contingency: Revert to v13 if v14 fails, migrate later
  3. intervention/image Usage

    • Risk: Heavy usage in image processing (FixometerFile.php)
    • Mitigation: Keep v2.x through all upgrades, schedule v3 separately
    • Contingency: Fall back to GD/Imagick built-ins if needed
  4. Discourse SSO Integration

    • Risk: External service, version-dependent
    • Mitigation: Test SSO after each step
    • Contingency: Have fallback local auth during issues
  5. PHP 8.4 Docker Image

    • Risk: New PHP version may have extension issues
    • Mitigation: Test Docker image locally before deploying
    • Contingency: Can stay on 8.3 temporarily if 8.4 problematic

Medium Risk Areas

  1. Database migrations - Test thoroughly
  2. Configuration file merges - Review all changes
  3. Package transitive dependencies - Use --with-all-dependencies flag
  4. Fly.io deployment - Use staging first
  5. Sentry integration - Monitor error reporting after deploy

Low Risk Areas

  1. Vue 2 frontend - Unaffected by Laravel upgrade
  2. Spatie packages - Well-maintained, backward compatible
  3. Test suite - Should largely pass as-is
  4. Vite asset bundling - No framework-specific changes

Estimated Total Effort & Timeline

Per-Version Effort Breakdown

Upgrade Estimation Risk Notes
Preparation (PHP, laravelcollective/html) 6-10 hours Medium Blocking issue resolution
Laravel 10 → 11 7-13 hours Medium Major changes, use Shift recommended
Laravel 11 → 12 4-6 hours Low Smoothest upgrade
Laravel 12 → 13 5-7 hours Low Zero breaking changes
TOTAL (All Steps) 22-36 hours Medium 3-4 weeks part-time

Recommended Timeline

Week 1: Preparation
├─ Days 1-2: Resolve laravelcollective/html (2-3 hours)
├─ Days 1-3: Upgrade PHP 8.2 → 8.4 Docker images (1-2 hours)
├─ Days 1-5: Test current state, document baselines (2-4 hours)
└─ Days 3-5: Review dependencies, coordinate (1-2 hours)

Week 2-3: Laravel 10 → 11
├─ Days 1-2: Purchase/run Laravel Shift (1 hour + review)
├─ Days 2-5: Manual code review and fixes (4-6 hours)
├─ Days 5+: Full test suite, smoke tests (2-4 hours)
└─ Result: Staging deploy, verify 48 hours

Week 4: Laravel 11 → 12
├─ Days 1-2: Manual upgrade, Shift optional (2-3 hours)
├─ Days 2-5: Code review and testing (2-3 hours)
└─ Result: Staging deploy, verify 48 hours

Week 5: Laravel 12 → 13
├─ Days 1-2: Manual upgrade (2-3 hours)
├─ Days 2-5: Code review and testing (2-3 hours)
└─ Result: Staging deploy, verify 48 hours

Week 6: Production Deploy & Monitoring
├─ Days 1: Deploy to production Fly.io
├─ Days 1-5: Monitor Sentry, logs, performance
├─ Days 5+: Declare upgrade complete
└─ Schedule post-upgrade optimizations (intervention/image v3)

Total Duration: 5-6 weeks with part-time effort (10-15 hours/week)


Post-Upgrade Optimizations (Future Work)

intervention/image v2 → v3 Migration

Effort: 6-12 hours
Timeline: Q2-Q3 2026
Impact: Modern image library, performance improvements
Files: FixometerFile.php, image controllers

Model Attribute Modernization

Effort: 4-8 hours
Timeline: Q3 2026+
Impact: Cleaner code, modern PHP, compile-time checks
Files: All Eloquent models

PHP 8.4 Feature Adoption

Effort: 4-8 hours
Timeline: Q3 2026+
Impact: Performance, null-safe operators, property hooks
Files: Throughout codebase


Rollback & Contingency Plan

If Laravel 11 Upgrade Fails

  1. At any point during upgrade:

    git reset --hard develop
  2. Revert Docker image:

    git checkout HEAD -- Dockerfile Dockerfile.fly
  3. Revert composer.json:

    composer install
  4. Restart containers:

    task docker:down-all
    task docker:up-core

If Deployed to Staging & Fails

  1. Keep current production stable - Fly.io already running Laravel 10
  2. Test thoroughly on staging - Separate Fly instance
  3. Document issues - Create tickets for each problem
  4. Iterate - Fix on feature branch, re-test on staging
  5. Only deploy to production when staging verified for 48+ hours

If Deployed to Production & Issues Arise

  1. Quick rollback:

    # On Fly.io
    fly releases  # list recent releases
    fly releases rollback <version>
  2. OR redeploy from previous production branch:

    git checkout production
    fly deploy
  3. Communicate - Alert team via Sentry/monitoring that issue detected

  4. Root cause analysis - Fix in feature branch

  5. Re-test on staging before redeploying


Success Criteria

Upgrade is Successful When:

  1. All tests pass:

    task docker:test:phpunit  # 100% pass rate
    npm test                  # 100% pass rate
    npm run jest             # 100% pass rate
  2. Laravel version correct:

    php artisan --version  # Shows Laravel 13.x.x
  3. Manual smoke tests pass:

    • Admin login works
    • Party creation works
    • Image upload processes correctly
    • Discourse SSO works
    • Audit trails record correctly
  4. Staging deployment stable:

    • Running on Fly.io Laravel 13 for 48+ hours
    • No error spikes in Sentry
    • Performance metrics normal
    • Database migrations completed
  5. Production deployment successful:

    • Zero downtime deployment (if using Fly deploy)
    • All users can access app
    • No Sentry errors related to upgrade
    • Feature functionality verified by team

References & Resources

Official Laravel Documentation

Package-Specific Documentation

Community Resources

Related Planning Docs

  • /home/edward/restarters.net/CLAUDE.md - Project setup and task conventions
  • /home/edward/restarters.net/docs/local-development.md - Local dev environment

Sign-Off & Approval

Prepared By: Claude Code Agent
Date: May 19, 2026
Status: Ready for Review

Next Steps:

  1. Review this plan with team/stakeholders
  2. Approve timeline and resource allocation
  3. Create GitHub milestone for Laravel 13 upgrade
  4. Schedule sprint/weeks for implementation
  5. Create individual stories for each phase
  6. Begin with preparation phase (laravelcollective/html, PHP 8.4)

Appendix: Quick Reference Commands

Docker Management

# Start development environment
task docker:up-core          # Core only
task docker:up-debug         # With debug tools
task docker:down-all         # Stop all containers

# Run commands
task docker:run:bash -- "npm run dev"
task docker:run:artisan -- migrate
task docker:shell            # Open bash in container

Composer Commands

# Update dependencies
composer update
composer update --with-all-dependencies

# Check package versions
composer show <package>
composer show --latest

# Require new versions
composer require "laravel/framework:^11.0"

Artisan Commands

# Version info
php artisan --version

# Database
php artisan migrate
php artisan migrate:refresh --seed

# Configuration
php artisan config:cache
php artisan cache:clear

# Tinker (REPL)
php artisan tinker

Testing

# PHPUnit
./vendor/bin/phpunit
./vendor/bin/phpunit --filter testName
./vendor/bin/phpunit tests/Feature/Events/

# Jest
npm run jest

# Playwright
npm test

Git Workflow

# Create feature branch
git checkout develop
git pull
git checkout -b feature/laravel-11-upgrade

# Commit changes
git add .
git commit -m "Upgrade Laravel from 10 to 11"

# Push and create PR
git push origin feature/laravel-11-upgrade
# Then create PR via GitHub UI

# After approval and merge
git checkout develop
git pull

END OF DOCUMENT

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions