翻译进度
9
分块数量
2
参与人数

升级指南

这是一篇协同翻译的文章,你可以点击『我来翻译』按钮来参与翻译。


升级指南

  • 从 12.x 升级到 13.0
    • 利用人工智能进行升级

高影响变更

  • 更新依赖项
  • 更新 Laravel 安装程序
  • 请求伪造防护

中等影响变更

  • 缓存 serializable_classes 配置项
  • 在 MySQL 或 MariaDB 中使用数据库的 upsert 方法

低影响变更

  • 缓存前缀与会话 Cookie 名称
  • 集合模型序列化恢复预加载关联
  • Container::call 与可空类默认值
  • 域名路由注册优先级
  • JobAttempted 事件异常负载
  • 管理器的 extend 回调绑定
  • 支持使用 JOIN、ORDER BY 和 LIMIT 执行 DELETE 操作
  • 分页 Bootstrap 视图名称
  • 多态中间表名称生成规则
  • QueueBusy 事件属性重命名
  • 测试中自动重置 Str Factories

从 12.x 升级到 13.0

预计升级时间:10 分钟

[!NOTE]
我们尝试记录每一个可能的重大变更。由于这些变更中的一些位于框架的偏僻部分,只有一部分变更可能实际影响您的应用程序。想节省时间吗?您可以使用 Laravel Shift 来帮助自动化您的应用程序升级。Shift 是一个由社区维护的服务,可以自动化完成 Laravel 的升级过程。

使用 AI 进行升级

您可以使用 Laravel Boost 来完成自动化升级。 Boost 是一个官方 MCP 服务器,旨在为您的 AI 助手提供引导式升级提示 —— 将它安装到任意 Laravel 12 应用后,在 Claude Code、Cursor、OpenCode、Gemini 或 VS Code 中使用 /upgrade-laravel-v13 命令,即可开始升级至 Laravel 13。该命令要求 Laravel Boost 版本为 ^2.0。

Daisy2023 翻译于 4天前

Updating Dependencies

Likelihood Of Impact: High

You should update the following dependencies in your application's composer.json file:

  • laravel/framework to ^13.0
  • laravel/boost to ^2.0
  • laravel/tinker to ^3.0
  • phpunit/phpunit to ^12.0
  • pestphp/pest to ^4.0

Updating the Laravel Installer

If you are using the Laravel installer CLI tool to create new Laravel applications, you should update your installer installation for Laravel 13.x compatibility.

If you installed the Laravel installer via composer global require, you may update the installer using composer global update:

composer global update laravel/installer

Or, if you are using Laravel Herd's bundled copy of the Laravel installer, you should update your Herd installation to the latest release.

Cache

Cache Prefixes and Session Cookie Names

Likelihood Of Impact: Low

Laravel's default cache and Redis key prefixes now use hyphenated suffixes.

In most applications, this change will not apply because application-level configuration files already define these values. This primarily affects applications that rely on framework-level fallback configuration when corresponding application config values are not present.

If your application relies on these generated defaults, cache keys and session cookie names may change after upgrading:

// Laravel <= 12.x
Str::slug((string) env('APP_NAME', 'laravel'), '_').'_cache_';
Str::slug((string) env('APP_NAME', 'laravel'), '_').'_database_';
Str::slug((string) env('APP_NAME', 'laravel'), '_').'_session';

// Laravel >= 13.x
Str::slug((string) env('APP_NAME', 'laravel')).'-cache-';
Str::slug((string) env('APP_NAME', 'laravel')).'-database-';
Str::slug((string) env('APP_NAME', 'laravel')).'-session';

To retain previous behavior, explicitly configure CACHE_PREFIX, REDIS_PREFIX, and SESSION_COOKIE in your environment.

Store and Repository Contracts: touch

Likelihood Of Impact: Very Low

The cache contracts now include a touch method for extending item TTLs. If you maintain custom cache store implementations, you should add this method:

// Illuminate\Contracts\Cache\Store
public function touch($key, $seconds);

Cache serializable_classes Configuration

Likelihood Of Impact: Medium

The default application cache configuration now includes a serializable_classes option set to false. This hardens cache unserialization behavior to help prevent PHP deserialization gadget chain attacks if your application's APP_KEY is leaked. If your application intentionally stores PHP objects in cache, you should explicitly list the classes that may be unserialized:

'serializable_classes' => [
    App\Data\CachedDashboardStats::class,
    App\Support\CachedPricingSnapshot::class,
],

If your application previously relied on unserializing arbitrary cached objects, you will need to migrate that usage to explicit class allow-lists or to non-object cache payloads (such as arrays).

Container

Container::call and Nullable Class Defaults

Likelihood Of Impact: Low

Container::call now respects nullable class parameter defaults when no binding exists, matching constructor injection behavior introduced in Laravel 12:

$container->call(function (?Carbon $date = null) {
    return $date;
});

// Laravel <= 12.x: Carbon instance
// Laravel >= 13.x: null

If your method-call injection logic depended on the previous behavior, you may need to update it.

Contracts

Dispatcher Contract: dispatchAfterResponse

Likelihood Of Impact: Very Low

The Illuminate\Contracts\Bus\Dispatcher contract now includes the dispatchAfterResponse($command, $handler = null) method.

If you maintain a custom dispatcher implementation, add this method to your class.

ResponseFactory Contract: eventStream

Likelihood Of Impact: Very Low

The Illuminate\Contracts\Routing\ResponseFactory contract now includes an eventStream signature.

If you maintain a custom implementation of this contract, you should add this method.

MustVerifyEmail Contract: markEmailAsUnverified

Likelihood Of Impact: Very Low

The Illuminate\Contracts\Auth\MustVerifyEmail contract now includes markEmailAsUnverified().

If you provide a custom implementation of this contract, add this method to remain compatible.

Database

Database upsert With MySQL or MariaDB

Likelihood Of Impact: Medium

Laravel now validates that the caller provides a non-empty value for uniqueBy, and will throw an InvalidArgumentException instead of generating invalid SQL.

Although the MariaDB and MySQL database drivers ignore the uniqueBy value and always use the table's primary and unique indexes to detect existing records, the validation still applies. An InvalidArgumentException will be thrown if uniqueBy is empty.

MySQL DELETE Queries With JOIN, ORDER BY, and LIMIT

Likelihood Of Impact: Low

Laravel now compiles full DELETE ... JOIN queries including ORDER BY and LIMIT for MySQL grammar.

In previous versions, ORDER BY / LIMIT clauses could be silently ignored on joined deletes. In Laravel 13, these clauses are included in the generated SQL. As a result, database engines that do not support this syntax (such as standard MySQL / MariaDB variants) may now throw a QueryException instead of executing an unbounded delete.

Eloquent

Model Booting and Nested Instantiation

Likelihood Of Impact: Very Low

Creating a new model instance while that model is still booting is now disallowed and throws a LogicException.

This affects code that instantiates models from inside model boot methods or trait boot* methods:

protected static function boot()
{
    parent::boot();

    // No longer allowed during booting...
    (new static())->getTable();
}

Move this logic outside the boot cycle to avoid nested booting.

Polymorphic Pivot Table Name Generation

Likelihood Of Impact: Low

When table names are inferred for polymorphic pivot models using custom pivot model classes, Laravel now generates pluralized names.

If your application depended on the previous singular inferred names for morph pivot tables and used custom pivot classes, you should explicitly define the table name on your pivot model.

Collection Model Serialization Restores Eager-Loaded Relations

Likelihood Of Impact: Low

When Eloquent model collections are serialized and restored (such as in queued jobs), eager-loaded relations are now restored for the collection's models.

If your code depended on relations not being present after deserialization, you may need to adjust that logic.

HTTP Client

HTTP Client Response::throw and throwIf Signatures

Likelihood Of Impact: Very Low

The HTTP client response methods now declare their callback parameters in the method signatures:

public function throw($callback = null);
public function throwIf($condition, $callback = null);

If you override these methods in custom response classes, ensure your method signatures are compatible.

Notifications

Default Password Reset Subject

Likelihood Of Impact: Very Low

Laravel's default password reset mail subject has changed:

// Laravel <= 12.x
Reset Password Notification

// Laravel >= 13.x
Reset your password

If your tests, assertions, or translation overrides depend on the previous default string, update them accordingly.

Queued Notifications and Missing Models

Likelihood Of Impact: Very Low

Queued notifications now respect the #[DeleteWhenMissingModels] attribute and $deleteWhenMissingModels property defined on the notification class.

In previous versions, missing models could still cause queued notification jobs to fail in cases where you expected them to be deleted.

Queue

JobAttempted Event Exception Payload

Likelihood Of Impact: Low

The Illuminate\Queue\Events\JobAttempted event now exposes the exception object (or null) via $exception, replacing the previous boolean $exceptionOccurred property:

// Laravel <= 12.x
$event->exceptionOccurred;

// Laravel >= 13.x
$event->exception;

If you listen for this event, update your listener code accordingly.

QueueBusy Event Property Rename

Likelihood Of Impact: Low

The Illuminate\Queue\Events\QueueBusy event property $connection has been renamed to $connectionName for consistency with other queue events.

If your listeners reference $connection, update them to $connectionName.

Queue Contract Method Additions

Likelihood Of Impact: Very Low

The Illuminate\Contracts\Queue\Queue contract now includes queue size inspection methods that were previously only declared in docblocks.

If you maintain custom queue driver implementations of this contract, add implementations for:

  • pendingSize
  • delayedSize
  • reservedSize
  • creationTimeOfOldestPendingJob

Routing

Domain Route Registration Precedence

Likelihood Of Impact: Low

Routes with an explicit domain are now prioritized before non-domain routes in route matching.

This allows catch-all subdomain routes to behave consistently even when non-domain routes are registered earlier. If your application relied on previous registration precedence between domain and non-domain routes, review route matching behavior.

Scheduling

withScheduling Registration Timing

Likelihood Of Impact: Very Low

Schedules registered via ApplicationBuilder::withScheduling() are now deferred until Schedule is resolved.

If your application relied on immediate schedule registration timing during bootstrap, you may need to adjust that logic.

Security

Request Forgery Protection

Likelihood Of Impact: High

Laravel's CSRF middleware has been renamed from VerifyCsrfToken to PreventRequestForgery, and now includes request-origin verification using the Sec-Fetch-Site header.

VerifyCsrfToken and ValidateCsrfToken remain as deprecated aliases, but direct references should be updated to PreventRequestForgery, especially when excluding middleware in tests or route definitions:

use Illuminate\Foundation\Http\Middleware\PreventRequestForgery;
use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken;

// Laravel <= 12.x
->withoutMiddleware([VerifyCsrfToken::class]);

// Laravel >= 13.x
->withoutMiddleware([PreventRequestForgery::class]);

The middleware configuration API now also provides preventRequestForgery(...).

Support

Manager extend Callback Binding

Likelihood Of Impact: Low

Custom driver closures registered via manager extend methods are now bound to the manager instance.

If you previously relied on another bound object (such as a service provider instance) as $this inside these callbacks, you should move those values into closure captures using use (...).

Str Factories Reset Between Tests

Likelihood Of Impact: Low

Laravel now resets custom Str factories during test teardown.

If your tests depended on custom UUID / ULID / random string factories persisting between test methods, you should set them in each relevant test or setup hook.

Js::from Uses Unescaped Unicode By Default

Likelihood Of Impact: Very Low

Illuminate\Support\Js::from now uses JSON_UNESCAPED_UNICODE by default.

If your tests or frontend output comparisons depended on escaped Unicode sequences (for example \u00e8), update your expectations.

Utilities

Symfony PHP 8.5 Polyfill and Global Function Conflicts

Likelihood Of Impact: Low

Laravel 13 introduces a dependency on symfony/polyfill-php85. On PHP versions below 8.5, this polyfill defines global functions such as array_first() and array_last() unless they have already been defined earlier during bootstrap.

These functions may conflict with legacy helper packages like laravel/helpers or custom global helpers using the same names. For example, the historical array_first() helper accepted a callback to return the first matching element, while the polyfilled version only returns the first element of the array.

To avoid conflicts and ensure consistent behavior across PHP versions, you should prefer the Illuminate\Support\Arr methods:

use Illuminate\Support\Arr;

Arr::first($array, function ($value) {
  return /* condition */;
});

Views

Pagination Bootstrap View Names

Likelihood Of Impact: Low

The internal pagination view names for Bootstrap 3 defaults are now explicit:

// Laravel <= 12.x
pagination::default
pagination::simple-default

// Laravel >= 13.x
pagination::bootstrap-3
pagination::simple-bootstrap-3

If your application references the old pagination view names directly, update those references.

Miscellaneous

We also encourage you to view the changes in the laravel/laravel GitHub repository. While many of these changes are not required, you may wish to keep these files in sync with your application. Some of these changes will be covered in this upgrade guide, but others, such as changes to configuration files or comments, will not be. You can easily view the changes with the GitHub comparison tool and choose which updates are important to you.

本文章首发在 LearnKu.com 网站上。

本文中的所有译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。

《L04 微信小程序从零到发布》
从小程序个人账户申请开始,带你一步步进行开发一个微信小程序,直到提交微信控制台上线发布。
《L05 电商实战》
从零开发一个电商项目,功能包括电商后台、商品 & SKU 管理、购物车、订单管理、支付宝支付、微信支付、订单退款流程、优惠券等
贡献者:2
讨论数量: 0
发起讨论 只看当前版本


暂无话题~