Skip to content

Commit aa6060f

Browse files
committed
Simplify migration guide, link to detailed docs
1 parent 8c6adc7 commit aa6060f

1 file changed

Lines changed: 24 additions & 201 deletions

File tree

docs/en/appendices/5-4-migration-guide.md

Lines changed: 24 additions & 201 deletions
Original file line numberDiff line numberDiff line change
@@ -18,30 +18,14 @@ bin/cake upgrade rector --rules cakephp54 <path/to/app/src>
1818

1919
### I18n
2020

21-
``Number::parseFloat()`` now returns ``null`` instead of ``0.0`` when parsing
22-
fails. Previously, when ``NumberFormatter::parse()`` failed it returned ``false``,
23-
which was cast to ``0.0``. This silently converted invalid input like ``"abc"``
24-
to ``0.0``, making it impossible to distinguish from valid ``"0"`` input.
25-
26-
This also affects ``FloatType`` and ``DecimalType`` database types which use
27-
``Number::parseFloat()`` internally. Invalid locale-formatted form input will
28-
now result in ``null`` entity values instead of ``0``.
21+
`Number::parseFloat()` now returns `null` instead of `0.0` when parsing
22+
fails. This also affects `FloatType` and `DecimalType` database types.
2923

3024
### ORM
3125

3226
The default eager loading strategy for `HasMany` and `BelongsToMany` associations
33-
has changed from ``select`` to ``subquery``. The ``subquery`` strategy performs
34-
better for larger datasets as it avoids packet size limits from large ``WHERE IN``
35-
clauses and reduces PHP memory usage by keeping IDs in the database.
36-
37-
If you need the previous behavior, you can explicitly set the strategy when
38-
defining associations:
39-
40-
```php
41-
$this->hasMany('Comments', [
42-
'strategy' => 'select',
43-
]);
44-
```
27+
has changed from `select` to `subquery`. If you need the previous behavior,
28+
explicitly set `'strategy' => 'select'` when defining associations.
4529

4630
## Deprecations
4731

@@ -51,199 +35,38 @@ $this->hasMany('Comments', [
5135

5236
### Controller
5337

54-
#### RequestToDto Attribute
55-
56-
A new ``#[RequestToDto]`` attribute enables automatic mapping of request data to
57-
Data Transfer Objects in controller actions. This provides a clean way to handle
58-
form data with type safety:
59-
60-
```php
61-
use Cake\Controller\Attribute\RequestToDto;
62-
63-
class UsersController extends AppController
64-
{
65-
public function create(#[RequestToDto] UserCreateDto $dto): void
66-
{
67-
// $dto is automatically populated from request data
68-
$user = $this->Users->newEntity([
69-
'email' => $dto->email,
70-
'name' => $dto->name,
71-
]);
72-
}
73-
}
74-
```
75-
76-
Your DTO class must implement a static ``createFromArray()`` method:
77-
78-
```php
79-
class UserCreateDto
80-
{
81-
public function __construct(
82-
public string $email,
83-
public string $name,
84-
) {
85-
}
86-
87-
public static function createFromArray(array $data): self
88-
{
89-
return new self(
90-
email: $data['email'] ?? '',
91-
name: $data['name'] ?? '',
92-
);
93-
}
94-
}
95-
```
96-
97-
The attribute supports configuring the data source:
98-
99-
```php
100-
use Cake\Controller\Attribute\Enum\RequestToDtoSource;
101-
102-
// Use query string parameters
103-
public function search(
104-
#[RequestToDto(source: RequestToDtoSource::Query)] SearchDto $dto
105-
): void {}
106-
107-
// Use POST body data
108-
public function create(
109-
#[RequestToDto(source: RequestToDtoSource::Body)] CreateDto $dto
110-
): void {}
111-
112-
// Merge query and body (body takes precedence)
113-
public function update(
114-
#[RequestToDto(source: RequestToDtoSource::Request)] UpdateDto $dto
115-
): void {}
116-
117-
// Auto-detect based on request method (default)
118-
public function handle(
119-
#[RequestToDto(source: RequestToDtoSource::Auto)] DataDto $dto
120-
): void {}
121-
```
122-
123-
#### FormProtection Convenience Methods
124-
125-
``FormProtectionComponent`` now has convenience methods for unlocking actions
126-
and fields:
127-
128-
```php
129-
// In your controller's beforeFilter()
130-
$this->FormProtection->unlockActions(['api', 'webhook']);
131-
$this->FormProtection->unlockFields(['dynamic_field', 'optional_field']);
132-
133-
// With merge option (default is true)
134-
$this->FormProtection->unlockActions('newAction', merge: true);
135-
$this->FormProtection->unlockFields(['field1', 'field2'], merge: false);
136-
```
38+
- Added `#[RequestToDto]` attribute for automatic mapping of request data to
39+
Data Transfer Objects in controller actions.
40+
See [Request to DTO Mapping](../development/dependency-injection#request-to-dto-mapping).
41+
- Added `unlockActions()` and `unlockFields()` convenience methods to
42+
`FormProtectionComponent`.
43+
See [Form Protection Component](../controllers/components/form-protection).
13744

13845
### Database
13946

140-
#### Query Expression Methods
141-
142-
New convenience methods have been added to ``QueryExpression``:
143-
144-
- ``notBetween()`` for ``NOT BETWEEN`` expressions:
145-
146-
```php
147-
$query = $articles->find()
148-
->where(function (QueryExpression $exp) {
149-
return $exp->notBetween('view_count', 100, 1000);
150-
});
151-
// WHERE view_count NOT BETWEEN 100 AND 1000
152-
```
153-
154-
- ``inOrNull()`` for ``(field IN (...) OR field IS NULL)`` patterns:
155-
156-
```php
157-
$query = $articles->find()
158-
->where(function (QueryExpression $exp) {
159-
return $exp->inOrNull('category_id', [1, 2, 3]);
160-
});
161-
// WHERE (category_id IN (1, 2, 3) OR category_id IS NULL)
162-
```
163-
164-
- ``isDistinctFrom()`` and ``isNotDistinctFrom()`` for null-safe comparisons:
165-
166-
```php
167-
$query = $articles->find()
168-
->where(function (QueryExpression $exp) {
169-
// True when values differ, treating NULL as a comparable value
170-
return $exp->isDistinctFrom('status', 'published');
171-
});
172-
// WHERE status IS DISTINCT FROM 'published'
173-
// MySQL uses: NOT (status <=> 'published')
174-
175-
$query = $articles->find()
176-
->where(function (QueryExpression $exp) {
177-
// True when values are equal, treating NULL = NULL as true
178-
return $exp->isNotDistinctFrom('category_id', null);
179-
});
180-
// WHERE category_id IS NOT DISTINCT FROM NULL
181-
// MySQL uses: category_id <=> NULL
182-
```
47+
- Added `notBetween()` method for `NOT BETWEEN` expressions.
48+
See [Query Builder](../orm/query-builder#advanced-conditions).
49+
- Added `inOrNull()` and `notInOrNull()` methods for combining `IN` conditions with `IS NULL`.
50+
- Added `isDistinctFrom()` and `isNotDistinctFrom()` methods for null-safe comparisons.
18351

18452
### I18n
18553

186-
- `Number::toReadableSize()` now calculates decimal units (KB, MB, GB and TB)
187-
using an exponent of ten, meaning that 1 KB is 1000 Bytes. The units from the
188-
previous calculation method, where 1024 Bytes equaled 1 KB, have been changed
189-
to KiB, MiB, GiB, and TiB as defined in ISO/IEC 80000-13. It is possible to
190-
switch between the two units using a new optional boolean parameter in
191-
`Number::toReadableSize()`, as well as the new global setter `Number::setUseIecUnits()`.
54+
- `Number::toReadableSize()` now uses decimal units (KB = 1000 bytes) by default.
55+
Binary units (KiB = 1024 bytes) can be enabled via parameter or `Number::setUseIecUnits()`.
19256

19357
### ORM
19458

195-
#### Nested Array Format for Marshalling
196-
197-
The ``associated`` option in ``newEntity()`` and ``patchEntity()`` now supports
198-
the same nested array format as ``contain()``:
199-
200-
```php
201-
// Nested arrays (new in 5.4)
202-
$entity = $articles->newEntity($data, [
203-
'associated' => [
204-
'Tags',
205-
'Comments' => [
206-
'Users',
207-
'Attachments',
208-
],
209-
],
210-
]);
211-
212-
// Mixed with options
213-
$entity = $articles->newEntity($data, [
214-
'associated' => [
215-
'Tags' => ['onlyIds' => true],
216-
'Comments' => [
217-
'Users',
218-
'validate' => 'special',
219-
],
220-
],
221-
]);
222-
```
223-
224-
CakePHP distinguishes associations from options using naming conventions:
225-
226-
- Association names use PascalCase (e.g., ``Users``, ``Comments``)
227-
- Option keys use camelCase (e.g., ``onlyIds``, ``validate``)
59+
- The `associated` option in `newEntity()` and `patchEntity()` now supports
60+
nested array format matching `contain()` syntax.
61+
See [Converting Request Data into Entities](../orm/saving-data#converting-request-data-into-entities).
22862

22963
### Utility
23064

231-
- New `Cake\Utility\Fs\Finder` class provides a fluent, iterator-based API for
232-
discovering files and directories with support for pattern matching, depth
233-
control, and custom filters. The `Cake\Utility\Fs\Path` class offers
234-
cross-platform utilities for path manipulation.
65+
- Added `Cake\Utility\Fs\Finder` class for fluent file discovery with pattern matching,
66+
depth control, and custom filters. Added `Cake\Utility\Fs\Path` for cross-platform
67+
path manipulation.
23568

23669
### View
23770

238-
#### FormHelper Template Variables
239-
240-
The ``inputContainer`` and ``error`` templates now receive an ``{{inputId}}``
241-
variable containing the input element's HTML id attribute. This is useful for
242-
generating related element IDs for ARIA attributes or custom JavaScript:
243-
244-
```php
245-
$this->Form->setTemplates([
246-
'inputContainer' => '<div class="input {{type}}{{required}}" id="{{inputId}}-container">{{content}}</div>',
247-
'error' => '<div class="error" id="{{inputId}}-error">{{content}}</div>',
248-
]);
249-
```
71+
- Added `{{inputId}}` template variable to `inputContainer` and `error` templates
72+
in FormHelper. See [Built-in Template Variables](../views/helpers/form#built-in-template-variables).

0 commit comments

Comments
 (0)