Skip to main content

Cloudsoft Zone Build, deploy and scale with practical guides to PHP, Node.js, React, DevOps and AI automation. Explore the guides

Cloudsoft Zone
CakePHPIndustry update

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.

Cloudsoft Zone

8 min read

A sliced strawberry sponge cake with a handwritten Cake PHP label on a white buffet table
Photograph: Noriko YAMAMOTO from Tokyo, JAPAN / CC BY 2.0, via Wikimedia Commons
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.

The CakePHP framework wordmark logo in red lettering
The CakePHP wordmark. Logo file by Alex devel1, CC BY-SA 4.0.

Where 5.4 sits in the support picture

The supported-versions page of the CakePHP book (last updated in August 2026) gives this picture:

LineSupported minorsPHPActive supportSecurity fixes
5.x5.2 to 5.48.1 to 8.5 (5.3 and later need 8.2+)5.4 and laterYes
4.x4.4 to 4.67.2 to 8.3NoEnded 10 September 2026
3.x and olderNoneUp to 7.4NoNo

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:

PHP
// 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 beforeExecute and afterExecute hooks 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/cake with no command shows help directly.
  • Listeners registered in Application::events() or Plugin::events() now work in both web and CLI contexts. The guide recommends moving listeners out of config/bootstrap.php and into the new eventListeners() method, whose listener classes are resolved through the dependency injection container.

Smaller changes that can affect output

  • Number::parseFloat() returns null instead of 0.0 when parsing fails, and this also affects the FloatType and DecimalType database types.
  • Number::toReadableSize() now uses decimal units (1 KB = 1000 bytes) by default; binary units are available via a parameter or Number::setUseIecUnits().
  • FormHelper wraps hidden blocks (CSRF and form-protection fields, postLink() forms) with the HTML hidden attribute instead of an inline style="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:

PHP
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.

PHP
// 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() and isNotDistinctFrom() for common conditions, including null-safe comparisons.
  • FunctionsBuilder::stringAgg(), which becomes STRING_AGG or GROUP_CONCAT depending on the driver.
  • except() and exceptAll() for set operations (EXCEPT ALL is not supported on SQLite or SQL Server).
  • Connection::afterCommit() callbacks and a Connection.afterCommit event 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.
Conference attendees sitting on the grass outside a brick building during a PHP conference break
A break at a PHP community conference. Photo: Alberto Varela, CC BY 2.0.

HTTP, utilities and testing

  • JsonStreamResponse streams 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 Link headers.
  • A Cake\Lock\Lock facade 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\Finder and Path for 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\Helper to Cake\Console\Helper.
  • Mailer::$name is 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

  1. Make sure the application runs on PHP 8.2 or later and that the test suite passes on your current 5.x version.
  2. 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 the cakephp/upgrade tool).
  3. Set the constraint to "cakephp/cakephp": "5.4.*", which the installation guide recommends because it accepts patch releases only, then run composer update -W.
  4. 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:none wrappers, and code that relies on parseFloat() returning 0.0.
  5. Compare your src/Application.php and config files with the current cakephp/app skeleton. The 5.x skeleton, for example, now validates the Host header against App.fullBaseUrl in production.
  6. 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/container remains the default. The built-in container is opt-in through App.container set to cake, 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.

Advertise on CloudSoftZone

Your next customer could start right here.

Introduce your tool, product or business to readers exploring technology.

Advertise with us

Explore placements and request a private quote

Sources

  1. 5.4 Migration Guide — CakePHP Book (5.x)
  2. Supported Versions — CakePHP Book (5.x)
  3. Release Policy — CakePHP Book (5.x)
  4. 5.3 Migration Guide — CakePHP Book (5.x)
  5. Dependency Injection — CakePHP Book (5.x)
  6. Installation — CakePHP Book (5.x)
  7. CakePHP 5.4.0 release — CakePHP on GitHub, 2026-07-19
  8. 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.

Never published. Used only to contact you about this comment.

Loading the security check…

Comments are moderated before publication.

  • Code editor window showing PHP source code with syntax highlighting on a dark background

    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