What's new in CakePHP 5.4: ORM changes, DTO mapping and a built-in container
CakePHP 5.4 changes default eager loading, enforces entity-table ownership and adds request-to-DTO mapping, an optional built-in DI container, typed unhydrated queries and JSON streaming. Here is what to check before upgrading.
8 min read

In this article
CakePHP 5.4.0 was released on 19 July 2026, and the 5.4 line had reached 5.4.3 by 3 October 2026. It is a minor release, so the project's release policy applies: it is backwards compatible with 5.0, it can deprecate features but not remove them, and anything deprecated in 5.x is scheduled for removal in 6.0.
"Backwards compatible" does not mean "no behaviour changes", though. The 5.4 migration guide lists several defaults that changed, two of them in the ORM, and those are the ones most likely to surprise a team that upgrades with a plain composer update. This article summarises what changed, what is new, and the order in which to upgrade. Every item below comes from the official 5.4 migration guide and related pages of the CakePHP book, checked on 7 October 2026.
If you are still on CakePHP 4, read our separate upgrade checklist first: security support for 4.x ended on 10 September 2026.

Where 5.4 sits in the support picture
The supported-versions page of the CakePHP book (last updated in August 2026) gives this picture:
| Line | Supported minors | PHP | Active support | Security fixes |
|---|---|---|---|---|
| 5.x | 5.2 to 5.4 | 8.1 to 8.5 (5.3 and later need 8.2+) | 5.4 and later | Yes |
| 4.x | 4.4 to 4.6 | 7.2 to 8.3 | No | Ended 10 September 2026 |
| 3.x and older | None | Up to 7.4 | No | No |
Two policy rules matter for planning. Active support for a minor branch ends as soon as the next minor ships, so 5.3 now receives security fixes only. And a minor keeps security fixes until three newer minors have been published or the major's support ends, whichever comes first. In practice, staying within one or two minors of the latest release keeps you covered.
CakePHP 5.3 raised the minimum PHP version to 8.2, and 5.4 keeps it there; the installation guide lists PHP 8.2 as the minimum and 8.5 as supported.
Behaviour changes to check before upgrading
ORM: subquery eager loading by default
The default eager-loading strategy for HasMany and BelongsToMany associations changed from select to subquery. Results should be the same, but the SQL is different, so query plans, query logs and any test that asserts on SQL text may change. If a particular association performs worse, pin the old strategy:
// src/Model/Table/ArticlesTable.php
public function initialize(array $config): void
{
parent::initialize($config);
$this->hasMany('Comments', [
'strategy' => 'select',
]);
}ORM: entities must belong to the table
save(), delete(), patchEntity(), patchEntities() and loadInto() now throw an exception if the entity passed in does not belong to that table. The guide describes this as protection against accidental data corruption and deleted records. Code that, for example, saves a User entity through a different table instance will now fail loudly. Fix the code if you can; the escape hatch is $this->disableEntityClassAssertion(); in the table's initialize().
Console and events
BaseCommand::initialize()now runs after arguments and options have been parsed.- Commands are added to the event manager by default, so
beforeExecuteandafterExecutehooks always fire. Remove any code that attaches a command to the event manager yourself, or the hooks will run twice. - Unknown tokens after a parent command with subcommands are now rejected instead of silently running the parent, and running
bin/cakewith no command shows help directly. - Listeners registered in
Application::events()orPlugin::events()now work in both web and CLI contexts. The guide recommends moving listeners out ofconfig/bootstrap.phpand into the neweventListeners()method, whose listener classes are resolved through the dependency injection container.
Smaller changes that can affect output
Number::parseFloat()returnsnullinstead of0.0when parsing fails, and this also affects theFloatTypeandDecimalTypedatabase types.Number::toReadableSize()now uses decimal units (1 KB = 1000 bytes) by default; binary units are available via a parameter orNumber::setUseIecUnits().FormHelperwraps hidden blocks (CSRF and form-protection fields,postLink()forms) with the HTMLhiddenattribute instead of an inlinestyle="display:none;". This helps with a strict Content Security Policy, but CSS that targets the old inline style must switch to[hidden].- Loading a component whose alias matches the controller's default table now triggers a warning.
New features worth knowing
Request-to-DTO mapping
A #[RequestToDto] attribute maps request data to a data transfer object in a controller action. The DTO class supplies a static createFromArray() method, and the source (query string, body, or both) is detected from the HTTP method unless you set it:
use Cake\Controller\Attribute\Enum\RequestToDtoSource;
use Cake\Controller\Attribute\RequestToDto;
public function search(
#[RequestToDto(source: RequestToDtoSource::Query)] SearchCriteriaDto $criteria,
): void {
$articles = $this->Articles->find()
->where(['title LIKE' => '%' . $criteria->query . '%'])
->limit($criteria->limit);
$this->set(compact('articles'));
}The mapping does not validate anything for you; createFromArray() is where you apply defaults and types, and normal table validation still applies when you build entities.
An optional built-in DI container
CakePHP now ships its own PSR-11 container in the Cake\Container namespace. league/container stays the default; you opt in by setting App.container to cake in config/app.php. The built-in container auto-wires concrete classes and caches those resolutions, while service definitions using add(), addShared(), extend(), tags and delegates work with either implementation.
// config/app.php
return [
'App' => [
'container' => 'cake',
// ...
],
];Typed array reads in the ORM
SelectQuery::disableHydration() is deprecated in favour of Table::unhydratedFind(), which returns an UnhydratedSelectQuery. The point is static analysis: tools such as PHPStan no longer see entity|array on first(), all() and toArray(). disableHydration() will be removed in 6.0, so new code should use the new method.
Query builder additions
notBetween(),inOrNull(),notInOrNull(),isDistinctFrom()andisNotDistinctFrom()for common conditions, including null-safe comparisons.FunctionsBuilder::stringAgg(), which becomesSTRING_AGGorGROUP_CONCATdepending on the driver.except()andexceptAll()for set operations (EXCEPT ALLis not supported on SQLite or SQL Server).Connection::afterCommit()callbacks and aConnection.afterCommitevent that run after the outermost transaction commits and are discarded on rollback, a safer place to dispatch emails or queue jobs.LoggedQuery::setRedactor()to scrub sensitive values from query logs.

HTTP, utilities and testing
JsonStreamResponsestreams large JSON arrays or NDJSON from a generator or unbuffered query instead of holding the whole payload in memory.- A PSR-13 hypermedia link implementation; links added to responses are emitted as HTTP
Linkheaders. - A
Cake\Lock\Lockfacade with Redis, Memcached, local file and no-op engines. Text::mask(),Text::maskValue()and regex variants for masking strings such as card or phone numbers in logs and screens.Cake\Utility\Fs\FinderandPathfor file discovery and cross-platform paths.TestCase::mockModel()for mocking model classes with Mockery.- Translator caches can be isolated per tenant with
TranslatorRegistry::setCacheKeyPrefix(), which helps multi-tenant applications.
New deprecations
Fixing deprecations now makes the eventual 6.0 upgrade far easier. The 5.4 list is short:
- Command helpers moved from
Cake\Command\HelpertoCake\Console\Helper. Mailer::$nameis unused and deprecated.SelectQuery::disableHydration()is deprecated (see above).EventAwareApplicationInterface::pluginEvents()is deprecated; plugin events are registered while each plugin bootstraps.
Inside commands you can now use $this->io and $this->args rather than the execute() arguments. The guide says this becomes the default in 6.0, when those arguments are removed from the method signature, so new commands can adopt it today.
How to upgrade from 5.3 to 5.4
- Make sure the application runs on PHP 8.2 or later and that the test suite passes on your current 5.x version.
- Run the upgrade tool's rector rules before changing
composer.json:bin/cake upgrade rector --rules cakephp54 <path/to/app/src>(run from a checkout of thecakephp/upgradetool). - Set the constraint to
"cakephp/cakephp": "5.4.*", which the installation guide recommends because it accepts patch releases only, then runcomposer update -W. - Search the codebase for the behaviour changes above: has-many and belongs-to-many associations in performance-sensitive paths, cross-table saves, commands attached to the event manager, CSS that targets
display:nonewrappers, and code that relies onparseFloat()returning0.0. - Compare your
src/Application.phpand config files with the currentcakephp/appskeleton. The 5.x skeleton, for example, now validates the Host header againstApp.fullBaseUrlin production. - Run the full test suite with deprecation warnings visible and fix what it reports.
Who should upgrade now
If you run any 5.x application, upgrading to 5.4 is the default recommendation, because it is the only minor receiving new bug fixes. Teams on 5.2 should plan it soon: under the release policy, a minor stops getting security fixes once three newer minors have shipped, so 5.2 will lose coverage when 5.5 is released. Applications with heavy reporting pages or complex associations should allow extra testing time for the eager-loading change. Applications still on 4.x should not try to jump straight into 5.4 features; follow the 5.0 upgrade guide first, then apply each minor's rector rules.
Summary
CakePHP 5.4 is a sensible upgrade for any 5.x application: it is the only minor with active support, and it brings practical additions such as DTO mapping, typed unhydrated queries, after-commit callbacks and JSON streaming. Treat it as a behaviour-change release for the ORM, though. Test association-heavy pages and any code that moves entities between tables, run rector first, and pin 5.4.* so that future minors arrive only when you choose them.
Frequently asked questions
Is CakePHP 5.4 backwards compatible with 5.3?
The migration guide says 5.4.0 is backwards compatible with 5.0, but it also lists behaviour changes, notably subquery eager loading for HasMany and BelongsToMany associations and an exception when an entity is saved through a table it does not belong to. Test those areas before deploying.
Which PHP version does CakePHP 5.4 require?
PHP 8.2 or later. The installation guide lists PHP 8.5 as supported.
Do I have to switch to the new built-in container?
No.
league/containerremains the default. The built-in container is opt-in throughApp.containerset tocake, and service definitions work with either implementation.Is CakePHP 4 still supported?
No. According to the CakePHP supported-versions page, security support for 4.x ended on 10 September 2026.
Your next customer could start right here.
Introduce your tool, product or business to readers exploring technology.
Explore placements and request a private quote
Sources
- 5.4 Migration Guide — CakePHP Book (5.x)
- Supported Versions — CakePHP Book (5.x)
- Release Policy — CakePHP Book (5.x)
- 5.3 Migration Guide — CakePHP Book (5.x)
- Dependency Injection — CakePHP Book (5.x)
- Installation — CakePHP Book (5.x)
- CakePHP 5.4.0 release — CakePHP on GitHub, 2026-07-19
- CakePHP 5.4.3 release — CakePHP on GitHub, 2026-10-03
Written with substantial AI assistance. Facts checked against cited sources. Editorial policy.
Comments
No comments yet. Be the first to join the conversation.
Leave a comment
Every comment is read by a moderator before it appears. Your email address is never published; it is used only to contact you about this comment.
More from Cloudsoft Zone

Resource guideCakePHP
CakePHP 5 production deployment checklist: security, performance and releases
A practical checklist for running CakePHP 5.4 in production: debug and secrets, App.fullBaseUrl and host-header checks, webroot as document root, HTTPS, CSRF and security headers, caching, a deploy script and monitoring.
By Cloudsoft Zone

Resource guideCakePHP
CakePHP 4 security support has ended: a checklist for upgrading to CakePHP 5.4
CakePHP 4.x stopped receiving security fixes on 10 September 2026. This step-by-step checklist covers deprecations, the rector upgrade tool, composer and skeleton changes, the main breaking changes and PHPUnit updates for moving to CakePHP 5.4.
By Cloudsoft Zone

TutorialCakePHP
How to build a JSON REST API with CakePHP 5.4
A step-by-step CakePHP 5.4 tutorial: resource routes under an /api prefix, JsonView serialisation, bounded pagination, 201/204/422 status codes, JSON error bodies, CSRF handling for stateless clients and an integration test.
By Cloudsoft Zone
