Skip to content

Commit 0058375

Browse files
committed
Update entity docs for 6.0 properties
1 parent 5fe7c50 commit 0058375

10 files changed

Lines changed: 147 additions & 57 deletions

File tree

‎docs/en/appendices/6-0-migration-guide.md‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,13 @@ Some properties have also been renamed to better reflect their purpose. These ar
122122

123123
## New Features
124124

125+
### ORM
126+
127+
- Entities can now map fields to real class properties, including typed
128+
properties and PHP 8.4 asymmetric visibility/property hooks. When changing
129+
declared fields inside entity methods, continue to use `set()`/`patch()` if
130+
you need dirty tracking and related entity bookkeeping.
131+
125132
### Router
126133

127134
- Attribute routing is now available via `RouteBuilder::connectAttributes()` and the

‎docs/en/index.md‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -315,7 +315,7 @@ use Cake\ORM\Entity;
315315

316316
class Article extends Entity
317317
{
318-
protected array $_accessible = [
318+
protected array $patchable = [
319319
'title' => true,
320320
'slug' => true,
321321
'body' => true,
@@ -627,7 +627,7 @@ declare(strict_types=1);
627627
// ✓ Mass assignment (protected fields)
628628

629629
$article = $this->Articles->newEntity($data);
630-
// Only $_accessible fields can be set
630+
// Only patchable fields can be set
631631
```
632632

633633
```bash [Code Generation]

‎docs/en/orm/behaviors/translate.md‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -544,8 +544,8 @@ $this->Articles->save($article);
544544
```
545545

546546
This will result in your article, the french and spanish translations all being
547-
persisted. You'll need to remember to add `_translations` into the
548-
`$_accessible` fields of your entity as well.
547+
persisted. You'll need to remember to add `_translations` to the `patchable`
548+
fields of your entity as well.
549549

550550
### Validating Translated Entities
551551

‎docs/en/orm/entities.md‎

Lines changed: 104 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
---
22
title: "Entities"
3-
description: "Manage CakePHP entities: access data, implement accessors/mutators, handle mass assignment, virtual fields, and custom entity logic."
3+
description: "Manage CakePHP entities: access data, use typed properties and property hooks, handle mass assignment, virtual fields, and custom entity logic."
44
---
55

66
# Entities
@@ -11,6 +11,8 @@ While [Table Objects](../orm/table-objects) represent and provide access to a co
1111
objects, entities represent individual rows or domain objects in your
1212
application. Entities contain methods to manipulate and
1313
access the data they contain. Fields can also be accessed as properties on the object.
14+
In CakePHP 6.0 those fields can be stored as dynamic fields or mapped onto
15+
real class properties in your entity class.
1416

1517
Entities are created for you each time you iterate the query instance returned
1618
by `find()` of a table object or when you call `all()` or `first()` method
@@ -171,6 +173,87 @@ $article->has('links'); // true
171173
$article->hasValue('links'); // false
172174
```
173175

176+
### Declaring Concrete Properties
177+
178+
In CakePHP 6.0 entity fields can be mapped onto real class properties instead
179+
of being stored only as dynamic fields. This lets you use native PHP types in
180+
your entities while continuing to use CakePHP's entity features such as
181+
`get()`, `set()`, `patch()`, dirty tracking, original values, and mass
182+
assignment.
183+
184+
```php
185+
namespace App\Model\Entity;
186+
187+
use Cake\ORM\Entity;
188+
189+
class User extends Entity
190+
{
191+
public protected(set) int $id;
192+
public protected(set) string $email;
193+
public protected(set) ?string $first_name;
194+
public protected(set) ?string $last_name;
195+
public protected(set) bool $is_active;
196+
}
197+
```
198+
199+
Using `public protected(set)` lets outside code read the property directly
200+
while writes still go through the entity API. If you use `protected`
201+
properties instead, both reads and writes from outside the entity continue to
202+
use CakePHP's magic property handling.
203+
204+
Properties can also use PHP property hooks:
205+
206+
```php
207+
namespace App\Model\Entity;
208+
209+
use Cake\ORM\Entity;
210+
211+
class User extends Entity
212+
{
213+
public protected(set) ?string $password {
214+
set (?string $value) {
215+
$this->password = $value === null
216+
? null
217+
: password_hash($value, PASSWORD_DEFAULT);
218+
}
219+
}
220+
}
221+
```
222+
223+
Property `set` hooks are bypassed when the ORM hydrates database rows with
224+
setters disabled, so persisted values are assigned without being transformed a
225+
second time.
226+
227+
You can also use `get` hooks to define virtual computed properties without
228+
backing storage:
229+
230+
```php
231+
namespace App\Model\Entity;
232+
233+
use Cake\ORM\Entity;
234+
235+
class User extends Entity
236+
{
237+
public protected(set) string $first_name;
238+
public protected(set) string $last_name;
239+
240+
public string $full_name {
241+
get => trim($this->first_name . ' ' . $this->last_name);
242+
}
243+
}
244+
```
245+
246+
> [!WARNING]
247+
> When changing declared fields from inside entity methods, use `$this->set()`
248+
> or `$this->patch()` instead of assigning `$this->field` directly. Direct
249+
> assignment inside the entity bypasses CakePHP's dirty tracking and original
250+
> value bookkeeping.
251+
252+
> [!NOTE]
253+
> If a database column name conflicts with a built-in `Entity` property such as
254+
> `patchable`, `dirty`, or `errors`, keep that column as a dynamic field instead
255+
> of declaring it as a concrete property.
256+
174257
If you often partially load entities you should enable strict-property access
175258
behavior to ensure you're not using properties that haven't been loaded. On
176259
a per-entity basis you can enable this behavior:
@@ -195,8 +278,9 @@ Accessors let you customize how fields are read. They use the convention of
195278
words are joined together to a single word with the first letter of each word
196279
capitalized) of the field name.
197280

198-
They receive the basic value stored in the `_fields` array as their only
199-
argument. For example:
281+
They receive the basic value stored for the field as their only argument,
282+
whether that value comes from a concrete property or a dynamic field. For
283+
example:
200284

201285
```php
202286
namespace App\Model\Entity;
@@ -402,9 +486,9 @@ into an entity allows the user to modify any and all columns. When using
402486
anonymous entity classes or creating the entity class with the [Bake Console](../bake)
403487
CakePHP does not protect against mass-assignment.
404488

405-
The `_accessible` property allows you to provide a map of fields and
406-
whether or not they can be mass-assigned. The values `true` and `false`
407-
indicate whether a field can or cannot be mass-assigned:
489+
The `patchable` property allows you to provide a map of fields and whether or
490+
not they can be mass-assigned. The values `true` and `false` indicate whether
491+
a field can or cannot be mass-assigned:
408492

409493
```php
410494
namespace App\Model\Entity;
@@ -413,7 +497,7 @@ use Cake\ORM\Entity;
413497

414498
class Article extends Entity
415499
{
416-
protected array $_accessible = [
500+
protected array $patchable = [
417501
'title' => true,
418502
'body' => true,
419503
];
@@ -430,7 +514,7 @@ use Cake\ORM\Entity;
430514

431515
class Article extends Entity
432516
{
433-
protected array $_accessible = [
517+
protected array $patchable = [
434518
'title' => true,
435519
'body' => true,
436520
'*' => false,
@@ -452,26 +536,25 @@ use App\Model\Entity\Article;
452536
$article = new Article(['id' => 1, 'title' => 'Foo'], ['guard' => false]);
453537
```
454538

455-
### Modifying the Guarded Fields at Runtime
539+
### Modifying the Patchable Fields at Runtime
456540

457-
You can modify the list of guarded fields at runtime using the `setAccess()`
458-
method:
541+
You can modify the list of patchable fields at runtime using the
542+
`setPatchable()` method:
459543

460544
```php
461-
// Make user_id accessible.
462-
$article->setAccess('user_id', true);
545+
// Make user_id patchable.
546+
$article->setPatchable('user_id', true);
463547

464548
// Make title guarded.
465-
$article->setAccess('title', false);
549+
$article->setPatchable('title', false);
466550
```
467551

468552
> [!NOTE]
469-
> Modifying accessible fields affects only the instance the method is called
470-
> on.
553+
> Modifying patchable fields affects only the instance the method is called on.
471554
472555
When using the `newEntity()` and `patchEntity()` methods in the `Table`
473556
objects you can customize mass assignment protection with options. Please refer
474-
to the [Changing Accessible Fields](../orm/saving-data#changing-accessible-fields) section for more information.
557+
to the [Changing Patchable Fields](../orm/saving-data#changing-patchable-fields) section for more information.
475558

476559
### Bypassing Field Guarding
477560

@@ -482,8 +565,8 @@ fields:
482565
$article->patch($fields, ['guard' => false]);
483566
```
484567

485-
By setting the `guard` option to `false`, you can ignore the accessible
486-
field list for a single call to `patch()`.
568+
By setting the `guard` option to `false`, you can ignore the patchable field
569+
list for a single call to `patch()`.
487570

488571
### Checking if an Entity was Persisted
489572

@@ -618,7 +701,7 @@ use Cake\ORM\Entity;
618701

619702
class User extends Entity
620703
{
621-
protected array $_virtual = ['full_name'];
704+
protected array $virtual = ['full_name'];
622705
}
623706
```
624707

@@ -642,7 +725,7 @@ use Cake\ORM\Entity;
642725

643726
class User extends Entity
644727
{
645-
protected array $_hidden = ['password'];
728+
protected array $hidden = ['password'];
646729
}
647730
```
648731

‎docs/en/orm/retrieving-data-and-resultsets.md‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -341,10 +341,10 @@ list finds.
341341

342342
```php
343343
// In your Authors Entity create a virtual field to be used as the displayField:
344-
protected function _getLabel()
344+
protected function _getLabel(): string
345345
{
346-
return $this->_fields['first_name'] . ' ' . $this->_fields['last_name']
347-
. ' / ' . __('User ID %s', $this->_fields['user_id']);
346+
return $this->first_name . ' ' . $this->last_name
347+
. ' / ' . __('User ID %s', $this->user_id);
348348
}
349349
```
350350

‎docs/en/orm/saving-data.md‎

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -144,9 +144,9 @@ $entity = $articles->newEntity($this->request->getData());
144144
```
145145

146146
> [!NOTE]
147-
> If you are using newEntity() and the resulting entities are missing some or
148-
> all the data they were passed, double check that the columns you want to
149-
> set are listed in the `$_accessible` property of your entity. See [Entities Mass Assignment](../orm/entities#entities-mass-assignment).
147+
> If you are using `newEntity()` and the resulting entities are missing some or
148+
> all the data they were passed, double check that the columns you want to set
149+
> are listed in the `patchable` property of your entity. See [Entities Mass Assignment](../orm/entities#entities-mass-assignment).
150150
151151
The request data should follow the structure of your entities. For example if
152152
you have an article, which belonged to a user, and had many comments, your
@@ -430,12 +430,12 @@ $articles->saveMany($entities);
430430
$articles->saveManyOrFail($entities);
431431
```
432432

433-
### Changing Accessible Fields
433+
### Changing Patchable Fields
434434

435-
It's also possible to allow `newEntity()` to write into non accessible fields.
436-
For example, `id` is usually absent from the `_accessible` property. In
437-
such case, you can use the `accessibleFields` option. It could be useful to
438-
keep ids of associated entities:
435+
It's also possible to allow `newEntity()` to write into non-patchable fields.
436+
For example, `id` is usually absent from the `patchable` property. In that
437+
case, you can use the `patchableFields` option. It can be useful to keep ids
438+
of associated entities:
439439

440440
```php
441441
// In a controller
@@ -446,7 +446,7 @@ $entity = $articles->newEntity($this->request->getData(), [
446446
'Tags', 'Comments' => [
447447
'associated' => [
448448
'Users' => [
449-
'accessibleFields' => ['id' => true],
449+
'patchableFields' => ['id' => true],
450450
],
451451
],
452452
],
@@ -458,9 +458,9 @@ The above will keep the association unchanged between Comments and Users for the
458458
concerned entity.
459459

460460
> [!NOTE]
461-
> If you are using newEntity() and the resulting entities are missing some or
462-
> all the data they were passed, double check that the columns you want to
463-
> set are listed in the `$_accessible` property of your entity. See
461+
> If you are using `newEntity()` and the resulting entities are missing some or
462+
> all the data they were passed, double check that the columns you want to set
463+
> are listed in the `patchable` property of your entity. See
464464
> [Entities Mass Assignment](../orm/entities#entities-mass-assignment).
465465
466466
### Merging Request Data Into Entities
@@ -553,14 +553,14 @@ The same can be said about hasMany and belongsToMany associations, with
553553
an important caveat:
554554

555555
> [!NOTE]
556-
> For belongsToMany associations, ensure the relevant entity has
557-
> a property accessible for the associated entity.
556+
> For belongsToMany associations, ensure the relevant entity has a patchable
557+
> property for the associated entity.
558558
559559
If a Product belongsToMany Tag:
560560

561561
```php
562562
// in the Product Entity
563-
protected array $_accessible = [
563+
protected array $patchable = [
564564
// .. other properties
565565
'tags' => true,
566566
];

‎docs/en/tutorials-and-examples/cms/articles-model.md‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ use Cake\ORM\Entity;
6565

6666
class Article extends Entity
6767
{
68-
protected array $_accessible = [
68+
protected array $patchable = [
6969
'user_id' => true,
7070
'title' => true,
7171
'slug' => true,
@@ -79,7 +79,7 @@ class Article extends Entity
7979
}
8080
```
8181

82-
Right now, our entity is quite slim; we've only set up the `_accessible`
82+
Right now, our entity is quite slim; we've only set up the `patchable`
8383
property, which controls how properties can be modified by
8484
[Entities Mass Assignment](../../orm/entities#entities-mass-assignment).
8585

‎docs/en/tutorials-and-examples/cms/authorization.md‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -249,7 +249,7 @@ public function edit($slug)
249249
if ($this->request->is(['post', 'put'])) {
250250
$this->Articles->patchEntity($article, $this->request->getData(), [
251251
// Added: Disable modification of user_id.
252-
'accessibleFields' => ['user_id' => false],
252+
'patchableFields' => ['user_id' => false],
253253
]);
254254
if ($this->Articles->save($article)) {
255255
$this->Flash->success(__('Your article has been updated.'));
@@ -264,7 +264,7 @@ public function edit($slug)
264264
```
265265

266266
Here we're modifying which properties can be mass-assigned, via the options
267-
for `patchEntity()`. See the [Changing Accessible Fields](../../orm/saving-data#changing-accessible-fields) section for
267+
for `patchEntity()`. See the [Changing Patchable Fields](../../orm/saving-data#changing-patchable-fields) section for
268268
more information. Remember to remove the `user_id` control from
269269
**templates/Articles/edit.php** as we no longer need it.
270270

0 commit comments

Comments
 (0)