11---
22title : " 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
1111objects, entities represent individual rows or domain objects in your
1212application. Entities contain methods to manipulate and
1313access 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
1517Entities are created for you each time you iterate the query instance returned
1618by ` 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+
174257If you often partially load entities you should enable strict-property access
175258behavior to ensure you're not using properties that haven't been loaded. On
176259a 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
195278words are joined together to a single word with the first letter of each word
196279capitalized) 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
202286namespace App\Model\Entity;
@@ -402,9 +486,9 @@ into an entity allows the user to modify any and all columns. When using
402486anonymous entity classes or creating the entity class with the [ Bake Console] ( ../bake )
403487CakePHP 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
410494namespace App\Model\Entity;
@@ -413,7 +497,7 @@ use Cake\ORM\Entity;
413497
414498class 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
431515class 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
472555When using the ` newEntity() ` and ` patchEntity() ` methods in the ` Table `
473556objects 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
619702class 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
643726class User extends Entity
644727{
645- protected array $_hidden = ['password'];
728+ protected array $hidden = ['password'];
646729}
647730```
648731
0 commit comments