Skip to content

Support Laravel 12 - #207

Open
LukeTowers wants to merge 231 commits into
developfrom
wip/1.3
Open

LukeTowers wants to merge 231 commits into
developfrom
wip/1.3

Conversation

@LukeTowers

@LukeTowers LukeTowers commented Feb 25, 2025 •

Copy link
Copy Markdown
Member

Replaces #173, continuing the work done by @mjauvin @bennothommo & @wverhoogt

Summary by CodeRabbit

  • New Features

    • Added PHP 8.2+ support and modern framework capabilities.
    • Added MariaDB connectivity and improved database support across major platforms.
    • Added configurable authentication password attributes and fluent authentication setup.
    • Added optional pagination totals and improved expression-based searching.
    • Added broader translation path and locale fallback handling.
    • Added customizable application path generation.
  • Bug Fixes

    • Improved schema column-change handling across database platforms.
    • Prevented invalid file records from producing malformed results.
    • Improved form value handling and month formatting.

@mjauvin
mjauvin removed their request for review October 6, 2026 20:04
@JonasPardon

Copy link
Copy Markdown

"I don't understand the problem, striping the extra quotes was the whole reason to override getDefaultValue() in the first place."

— @mjauvin on Discord, about item 1 of my notes on wintercms/winter#1366

Agreed, stripping the extra quotes is needed: some databases report a column's default as a quoted SQL literal, so a default copied back from getColumns() would otherwise be quoted twice. My point was only about the old implementation, which removed every ', including apostrophes inside the value. So ->default("O'Brien") on a new column became 'OBrien', and on MySQL, which reports defaults unquoted, ->change() rewrote an existing O'Brien default to OBrien.

Your 0f69f95 fixes both. One case is still left: databases that report the default as an SQL literal also escape the quotes inside it, for example 'O''Brien'. Removing only the outer pair leaves O''Brien, which parent::getDefaultValue() escapes again.

Here's your current override run on Laravel 12.69.3, next to the same override with the inner quotes unescaped as well:

Input to getDefaultValue() Current (0f69f95) With str_replace("''", "'", …)
O'Brien (from a migration, or MySQL getColumns()) 'O''Brien' 'O''Brien'
'foo' (MariaDB/SQLite getColumns()) 'foo' 'foo'
'O''Brien' (MariaDB/SQLite getColumns()) 'O''''Brien', so the stored value becomes O''Brien 'O''Brien'
'O'Brian' (your test input) 'O''Brian' 'O''Brian'

Suggested change, which keeps your tests passing:

$value = str_replace("''", "'", substr($value, 1, -1));

and a test case with default("'O''Brian'") next to the existing "'O'Brian'" one.

I checked that SQLite's getColumns() returns 'O''Brien', but I haven't run MariaDB. Its information_schema documents the same escaped format. The SQLite ->change() path already keeps such a default intact, so this mainly matters for MariaDB through MySqlBasedGrammar::compileChange().

🤖 Generated with Claude Code

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
@mjauvin

mjauvin commented Oct 8, 2026 •

Copy link
Copy Markdown
Member

@LukeTowers @JonasPardon I don't think we need to override Mail::fake(), it doesn't differ from the base Laravel version.

Or is there something special about our MailFake maybe that we need ?

We should at least drop the argument, it's not needed and Laravel doesn't have it either.

@JonasPardon

Copy link
Copy Markdown

I don't think we need to override Mail::fake(), it doesn't differ from the base Laravel version.

@mjauvin It does differ, even though the body mirrors Laravel's:

  1. Storm's Mail facade extends the base Facade, not Illuminate\Support\Facades\Mail, so without the override there is no fake(): Mail::fake() is forwarded to the mailer and throws Method Illuminate\Mail\Mailer::fake does not exist.
  2. Its accessor is mailer, while Laravel's is mail.manager. Laravel's fake() passes the facade root to MailFake, which requires a MailManager, so extending Laravel's facade instead fails with MailFake::__construct(): Argument #1 ($manager) must be of type Illuminate\Mail\MailManager, Illuminate\Mail\Mailer given. The override resolves mail.manager itself.
  3. It creates Winter's MailFake, which turns Mail::send('acme.blog::mail.welcome', $data, $callback) into a mailable. Laravel's MailFake ignores anything that isn't a Mailable, so Mail::assertSent() wouldn't see Winter-style sends.

I checked 1 and 2 by removing the override on wip/1.3 and running tests/Support/MailFacadeTest.php.

@mjauvin

mjauvin commented Oct 8, 2026

Copy link
Copy Markdown
Member

@JonasPardon I agree with your answer above. But I think we can drop the mailmanager argument.

austinderrick and others added 13 commits October 10, 2026 12:15
Add Laravel's InvokeDeferredCallbacks middleware to the HTTP kernel so
callbacks registered with defer() run after the response is sent.
# Conflicts:
#	composer.json
)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Luke Towers <github@luketowers.ca>
Co-authored-by: Luke Towers <git@luketowers.ca>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

With a non-UTC app.timezone, a UNIX timestamp assigned to a date attribute is stored shifted by the UTC offset on wip/1.3:

// app.timezone = 'Europe/Brussels'
$model->published_at = 1700000000; // 2023-11-14 22:13:20 UTC

// 1.2: stored as '2023-11-14 23:13:20'
// wip/1.3: stored as '2023-11-14 22:13:20', which reads back an hour earlier
Model::asDateTime() calls Argon::createFromTimestamp($value) without a timezone. Carbon 2 used PHP's default timezone for that, which Storm sets from app.timezone. Carbon 3 uses UTC instead. Laravel hit the same regression and fixed it the same way in laravel/framework@8c61f50 (#50751), which is still how HasAttributes::asDateTime() reads on 12.x. Projects on the default UTC timezone are not affected.

Pass date_default_timezone_get(), like Laravel.
Add a test that assigns a timestamp with Europe/Brussels as the default timezone and checks that the stored value and the timestamp read back are correct. It fails without the change.
asDateTime() is the only numeric-timestamp cast in Storm. Laravel's commit also covered its other createFromTimestamp() call sites; the equivalent ones in Winter core are fixed in wintercms/winter#1579.
LukeTowers and others added 3 commits October 22, 2026 22:55
# Conflicts:
#	phpstan.neon
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Luke Towers <git@luketowers.ca>
Co-authored-by: Luke Towers <github@luketowers.ca>

With app.loadDiscoveredPackages enabled, no discovered package provider gets registered on wip/1.3.

0a6b24e ("Laravel 10 changes") dropped the brackets around the manifest's providers in Application::registerConfiguredProviders():

$providers->splice(1, 0, $this->make(PackageManifest::class)->providers());
That splices each provider class in as a separate string, between the Illuminate\ group and the other providers. collapse() only flattens arrays and skips anything else, so those strings never reach ProviderRepository::load(). Laravel 12 still passes them as one group ([…->providers()]), which is what this restores. The default (loadDiscoveredPackages => false) is unaffected.

Restore the brackets.
Add a test that boots an Application with a discovered provider and checks that it gets registered. It fails without the change.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement PRs that implement a new feature or substantial change

Projects

None yet

Development

Successfully merging this pull request may close these issues.

10 participants