Skip to content

Laravel 13 compatibility#162

Draft
thekevinm wants to merge 1 commit intomasterfrom
shift/laravel-13
Draft

Laravel 13 compatibility#162
thekevinm wants to merge 1 commit intomasterfrom
shift/laravel-13

Conversation

@thekevinm
Copy link
Copy Markdown
Contributor

Summary

Wave 0 of the 53-package Laravel 11 → 13 upgrade campaign. This PR
makes df-core compose, autoload, and runtime-load cleanly under
Laravel 13.7 while keeping the package framework-agnostic in its
production require (host application pins the framework version).

Tracks main-app L13 Shift PR dreamfactory/dreamfactory#578.
Companion to df-commercial gold release 7.4.5.

Changes (8 files)

composer.json

Dep Before After Why
php ^8.0 ^8.3 L13 minimum; matches main app
doctrine/dbal ^3.1.4 removed Zero src/ usage
symfony/yaml ^6.0 ^6.0|^7.0 L13 ships Symfony 7
laravel/helpers none ^1.8 (require) Polyfills the 346 array_get/camel_case/array_except sites still in df-core; production-required so consumers don't need to add it
laravel/framework none ^13.7 (require-dev) Test-only — keeps df-core framework-agnostic in require
phpunit/phpunit @stable ^11.5.3 L13-compat
mockery/mockery none ^1.6 Test stack
nunomaduro/collision none ^8.6 Test stack
orchestra/testbench none ^11.0 Note: ^10 pins to Laravel 12; ^11 is the L13-compat track

src/Http/Controllers/Controller.php

Removed DispatchesJobs trait. The trait was removed from Laravel core in Laravel 11 (upgrade guide). Verified zero $this->dispatch() callers anywhere in df-core. Sibling-package df-script uses the trait directly — that's its own future fix, not blocked by this change.

src/LaravelServiceProvider.php

Three changes:

  1. Replaced include __DIR__.'/../routes/routes.php' with $this->loadRoutesFrom() — handles route-cache check internally, idiomatic for L11+.
  2. Gated Route::prependMiddlewareToGroup('web', FirstUserCheck::class) with Route::hasMiddlewareGroup('web'). L13's API-only application stack does not register a web group; the un-gated call would throw InvalidArgumentException.
  3. Gated MongoDBServiceProvider and CorsServiceProvider registrations with class_exists() for graceful degradation when the optional package isn't installed.

src/Providers/CorsServiceProvider.php

Dropped the unused Kernel $kernel parameter from boot() (and its use Illuminate\Contracts\Http\Kernel;).

Important verification: Laravel 13.7's Illuminate\Http\Middleware\HandleCors still typehints Fruitcake\Cors\CorsService in its constructor (Laravel did NOT migrate to a first-party Illuminate\Http\CorsService — see vendor/laravel/framework/src/Illuminate/Http/Middleware/HandleCors.php). The existing singleton(Fruitcake\Cors\CorsService::class, ...) binding is therefore correct and unchanged. Same for DfCorsService extends Fruitcake\Cors\CorsService.

src/Models/NoDbModel.php

Hardened the getDates() / getDateFormat() overrides instead of removing them. Removing them broke runtime — verified by smoke test that without the override, attributesToArray()addDateAttributesToArray()getDates() (trait fallback) → usesTimestamps() → fatal Call to undefined method, because NoDbModel doesn't extend Model and doesn't use HasTimestamps. The new stubs short-circuit safely whether $dates / $dateFormat are set on the subclass or not.

src/Database/Connectors/SQLiteConnector.php

Added : \PDO covariant return type to connect(). Parent has no return type; widening is allowed.

src/Testing/TestCase.php

  1. Renamed setupBeforeClasssetUpBeforeClass (PHPUnit 11 is case-sensitive; PHPUnit 9 also was, so this was a latent bug).
  2. Replaced the broken require './bootstrap/app.php' (relative to the runner's cwd, never reliable) with a resolveBootstrapPath() helper that:
    • Honors DF_TEST_BOOTSTRAP env override.
    • Falls back to df-core's own bootstrap/app.php if present.
    • Walks up from the file location to find a host-app root with both bootstrap/app.php and artisan.

src/helpers.php

Replaced the four internal array_get() calls inside df-core's own helpers with Illuminate\Support\Arr::get(). df-core's helpers no longer depend on the laravel/helpers polyfill being installed at runtime. (The 346 array_get sites elsewhere in models/services/components stay polyfilled — out of scope for this PR; follow-up will migrate them to Arr::/Str::.)

Test plan

Stage 1 — package-isolated

  • composer validate --strict: passes
  • composer install (with path-repo aliases for the pre-existing structural df-system ↔ df-core circular require — this is not new in L13): resolves laravel/framework v13.7.0, phpunit/phpunit 11.5.55, orchestra/testbench v11.1.0, tymon/jwt-auth 2.3.0, symfony/yaml v7.4.8
  • php -l across all src/ + tests/ files: zero parse errors
  • Reflection load of all 10 critical public classes succeeds
  • Functional smoke: NoDbModel::toArray() and BaseModel::toArray() run without fatal; DfCorsService::__construct() works

Stage 2 — integration with Shift'd main app (shift-173254)

  • Worktree of shift-173254 + path-repo override pointing at this branch
  • composer update dreamfactory/df-core: succeeds
  • All 10 critical df-core classes load via reflection inside the host app's autoloader
  • php artisan testNOT RUN: blocked by sibling packages (df-file, df-aws, df-mongo-logs, etc.) not yet upgraded. df-mongo-logs requires jenssegers/mongodb which pins illuminate/support ^10.0|^11, blocking L13 resolution. Tolerated per Wave 0 plan; sibling upgrades are Wave 1+.

Stage 3 — review

Self-review against the diff caught one critical spec error before commit (the spec said "remove getDates() from NoDbModel — dead code"; runtime smoke test proved it's load-bearing for the trait composition). Restored with hardening.

Reviewer notes / key risks

  1. NoDbModel::getDates() is load-bearing. Do not remove. The HasAttributes trait calls it during attributesToArray(), and NoDbModel can't satisfy the trait's default implementation because it doesn't extend Model. This was the spec's most important false-positive.
  2. Laravel 13.7 still uses Fruitcake\Cors\CorsService. There is no Illuminate\Http\CorsService in L13.7. Verified directly against vendor/laravel/framework/src/Illuminate/Http/Middleware/HandleCors.php.
  3. orchestra/testbench constraint: must be ^11.0, not ^10.0. v10 pins L12.
  4. Pre-existing circular require: df-core requires df-system ~0.6.2, and df-system requires df-core ~1.0. df-core's dev-master doesn't satisfy ~1.0, so standalone composer install has never worked on master and still doesn't. Not introduced by this PR. Stage 1 used path-repo aliases as a workaround.
  5. df-script uses DispatchesJobs directly (not via df-core's Controller). It will need its own L13 fix (the trait was removed in L11). Out of scope for this PR; flagged for Wave 1+.

Generalizable insights for sibling-package waves

  • Spec-vs-reality drift on Cors: Don't trust unverified upgrade-guide claims about Laravel-internal Cors changes. Always grep vendor/laravel/framework/.../HandleCors.php for the actual constructor typehint.
  • Trait-method overrides that look dead may not be: Eloquent traits call $this->method() internally. If a child class uses the trait directly (not via Model), removing the override can blow up the trait chain. Always smoke-test toArray() / jsonSerialize().
  • orchestra/testbench major-version tracks Laravel: v10 → L12, v11 → L13. Don't carry over the spec's ^10.
  • web middleware group is API-only-stack-optional in L13. Always gate prependMiddlewareToGroup('web', ...).
  • DispatchesJobs was removed in L11, not L13. Sibling packages with direct use Illuminate\Foundation\Bus\DispatchesJobs; will fatal at autoload time once they're upgraded.
  • PHPUnit 11 is case-sensitive on setUpBeforeClass. Audit all setupBeforeClass typos in sibling packages — this was latent on master.

Wave 0 of the 53-package Laravel 11 -> 13 upgrade campaign. Updates
df-core to compose, autoload, and runtime-load cleanly under Laravel
13.7 while preserving the package's framework-agnostic require list and
its public API for downstream consumers (df-system, df-user, df-aws,
and ~50 other packages).

Composer:
  - Bump php to ^8.3 (matches Laravel 13 minimum + main app PR #578).
  - Drop unused doctrine/dbal (zero src/ usage).
  - Add laravel/helpers ^1.8 (production polyfill for the 346 sites of
    array_get/camel_case/array_except still in df-core).
  - Widen symfony/yaml to ^6.0|^7.0.
  - Add Laravel 13 dev-stack: laravel/framework ^13.7 (require-dev to
    keep df-core framework-agnostic), phpunit/phpunit ^11.5.3,
    mockery/mockery ^1.6, nunomaduro/collision ^8.6, orchestra/testbench
    ^11.0 (^11 is the L13-compatible track; ^10 pins L12).

Source:
  - Http/Controllers/Controller: drop DispatchesJobs trait (removed in
    Laravel 11). Verified zero $this->dispatch() callers in df-core.
  - LaravelServiceProvider: replace include __DIR__/../routes/routes.php
    with $this->loadRoutesFrom() (idiomatic + handles route-cache
    internally); gate Route::prependMiddlewareToGroup('web', ...) with
    Route::hasMiddlewareGroup('web') (L13's API-only stack may not
    register the web group); gate MongoDB and CORS provider
    registrations with class_exists() so missing-package degrades
    gracefully.
  - Providers/CorsServiceProvider: drop unused Kernel $kernel parameter
    from boot() (and its `use Illuminate\Contracts\Http\Kernel;`).
    Confirmed Laravel 13.7's HandleCors middleware still typehints
    Fruitcake\Cors\CorsService, so the singleton binding remains
    correct; CorsService class is unchanged.
  - Models/NoDbModel: keep getDates()/getDateFormat() overrides but
    harden them. The HasAttributes trait calls $this->getDates()
    during attributesToArray()->addDateAttributesToArray(), and the
    trait's own implementation depends on usesTimestamps() which
    NoDbModel cannot satisfy (it doesn't extend Model and doesn't use
    HasTimestamps). Without the override the call chain reaches
    usesTimestamps() and fatal-errors. Stub implementations now defend
    against $dates / $dateFormat being unset.
  - Database/Connectors/SQLiteConnector: add `: \PDO` covariant return
    type to connect() (parent has no return type; widening allowed).
  - Testing/TestCase: rename setupBeforeClass -> setUpBeforeClass
    (PHPUnit 11 is case-sensitive). Replace the broken cwd-relative
    require './bootstrap/app.php' with a path resolver that walks up
    from this file looking for bootstrap/app.php + artisan, and
    supports DF_TEST_BOOTSTRAP env override.
  - helpers.php: replace internal array_get() calls inside df-core's
    own helper functions with Illuminate\Support\Arr::get() so the
    package's helpers do not require the laravel/helpers polyfill at
    runtime. The 346 helper sites elsewhere in df-core stay polyfilled
    (out of scope for this PR).

Verified:
  - composer validate --strict: passes.
  - composer install (with path-repo aliases for the structural
    df-system <-> df-core circular require): resolves Laravel 13.7.0,
    PHPUnit 11.5.55, testbench v11.1.0.
  - php -l on every src/ + tests/ file: zero parse errors.
  - Smoke test: 10/10 critical df-core classes (Controller, BaseModel,
    NoDbModel, BaseSystemLookup, Builder, DfCorsService,
    SQLiteConnector, CorsServiceProvider, LaravelServiceProvider,
    TestCase) load via reflection; NoDbModel and BaseModel toArray()
    runs without fatal.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant