Entities
- class
Cake\ORM\
Entity
- While Table Objects represent and provide access to a collection ofobjects, entities represent individual rows or domain objects in yourapplication. Entities contain methods to manipulate andaccess the data they contain. Fields can also be accessed as properties on the object.
Entities are created for you each time you use find()
on a tableobject.
Creating Entity Classes
You don’t need to create entity classes to get started with the ORM in CakePHP.However, if you want to have custom logic in your entities you will need tocreate classes. By convention entity classes live in src/Model/Entity/. Ifour application had an articles
table we could create the following entity:
- // src/Model/Entity/Article.php
- namespace App\Model\Entity;
- use Cake\ORM\Entity;
- class Article extends Entity
- {
- }
Right now this entity doesn’t do very much. However, when we load data from ourarticles table, we’ll get instances of this class.
Note
If you don’t define an entity class CakePHP will use the basic Entity class.
Creating Entities
Entities can be directly instantiated:
- use App\Model\Entity\Article;
- $article = new Article();
When instantiating an entity you can pass the fields with the data you wantto store in them:
- use App\Model\Entity\Article;
- $article = new Article([
- 'id' => 1,
- 'title' => 'New Article',
- 'created' => new DateTime('now')
- ]);
The preferred way of getting new entities is using the newEntity()
method from theTable
objects:
- use Cake\ORM\TableRegistry;
- $article = TableRegistry::get('Articles')->newEntity();
- $article = TableRegistry::get('Articles')->newEntity([
- 'id' => 1,
- 'title' => 'New Article',
- 'created' => new DateTime('now')
- ]);
$article
will be an instance of App\Model\Entity\Article
or fallback toCake\ORM\Entity
instance if you haven’t created the Article
class.
Accessing Entity Data
Entities provide a few ways to access the data they contain. Most commonly youwill access the data in an entity using object notation:
- use App\Model\Entity\Article;
- $article = new Article;
- $article->title = 'This is my first post';
- echo $article->title;
You can also use the get()
and set()
methods:
- $article->set('title', 'This is my first post');
- echo $article->get('title');
When using set()
you can update multiple fields at once using an array:
- $article->set([
- 'title' => 'My first post',
- 'body' => 'It is the best ever!'
- ]);
Warning
When updating entities with request data you should whitelist which fieldscan be set with mass assignment.
You can check if fields are defined in your entities with has()
:
- $article = new Article([
- 'title' => 'First post',
- 'user_id' => null
- ]);
- $article->has('title'); // true
- $article->has('user_id'); // false
- $article->has('undefined'); // false.
The has()
method will return true
if a field is defined and hasa non-null value. You can use isEmpty()
and hasValue()
to check ifa field contains a ‘non-empty’ value:
- $article = new Article([
- 'title' => 'First post',
- 'user_id' => null
- ]);
- $article->isEmpty('title'); // false
- $article->hasValue('title'); // true
- $article->isEmpty('user_id'); // true
- $article->hasValue('user_id'); // false
New in version 3.6.0: The hasValue()
and isEmpty()
methods were added in 3.6.0
Accessors & Mutators
In addition to the simple get/set interface, entities allow you to provideaccessors and mutator methods. These methods let you customize how fieldsare read or set.
Accessors use the convention of _get
followed by the CamelCased version ofthe field name.
Cake\ORM\Entity::
get
($field)- They receive the basic value stored in the
_properties
arrayas their only argument. Accessors will be used when saving entities, so becareful when defining methods that format data, as the formatted data will bepersisted. For example:
- namespace App\Model\Entity;
- use Cake\ORM\Entity;
- class Article extends Entity
- {
- protected function _getTitle($title)
- {
- return ucwords($title);
- }
- }
The accessor would be run when getting the field through any of these two ways:
- echo $article->title;
- echo $article->get('title');
Note
Code in your accessors is executed each time you reference the field. You canuse a local variable to cache it if you are performing a resource-intensiveoperation in your accessor like this: $myEntityProp = $entity->my_property.
You can customize how fields get set by defining a mutator:
Cake\ORM\Entity::
set
($field = null, $value = null)- Mutator methods should always return the value that should be stored in thefield. As you can see above, you can also use mutators to set othercalculated fields. When doing this, be careful to not introduce any loops,as CakePHP will not prevent infinitely looping mutator methods.
Mutators allow you to convert fields as they are set, or create calculateddata. Mutators and accessors are applied when fields are read using propertyaccess, or using get()
and set()
. For example:
- namespace App\Model\Entity;
- use Cake\ORM\Entity;
- use Cake\Utility\Text;
- class Article extends Entity
- {
- protected function _setTitle($title)
- {
- return Text::slug($title);
- }
- }
The mutator would be run when setting the field through any of these twoways:
- $user->title = 'foo'; // slug is set as well
- $user->set('title', 'foo'); // slug is set as well
Warning
Accessors are also run before entities are persisted to the database.If you want to transform fields but not persist that transformation,we recommend using virtual fields as those are not persisted.
Creating Virtual Fields
By defining accessors you can provide access to fields that do notactually exist. For example if your users table has first_name
andlast_name
you could create a method for the full name:
- namespace App\Model\Entity;
- use Cake\ORM\Entity;
- class User extends Entity
- {
- protected function _getFullName()
- {
- return $this->first_name . ' ' . $this->last_name;
- }
- }
You can access virtual fields as if they existed on the entity. The propertyname will be the lower case and underscored version of the method (full_name
):
- echo $user->full_name;
Do bear in mind that virtual fields cannot be used in finds. If you wantthem to be part of JSON or array representations of your entities,see Exposing Virtual Fields.
Checking if an Entity Has Been Modified
Cake\ORM\Entity::
dirty
($field = null, $dirty = null)- You may want to make code conditional based on whether or not fields havechanged in an entity. For example, you may only want to validate fields whenthey change:
- // See if the title has been modified.
- // Prior to 3.5 use dirty()
- $article->isDirty('title');
You can also flag fields as being modified. This is handy when appending intoarray fields as this wouldn’t automatically mark the field as dirty, onlyexchanging completely would.:
- // Add a comment and mark the field as changed.
- // Prior to 3.5 use dirty()
- $article->comments[] = $newComment;
- $article->setDirty('comments', true);
In addition you can also base your conditional code on the original fieldvalues by using the getOriginal()
method. This method will either returnthe original value of the field if it has been modified or its actual value.
You can also check for changes to any field in the entity:
- // See if the entity has changed
- // Prior to 3.5 use dirty()
- $article->isDirty();
To remove the dirty mark from fields in an entity, you can use the clean()
method:
- $article->clean();
When creating a new entity, you can avoid the fields from being marked as dirtyby passing an extra option:
- $article = new Article(['title' => 'New Article'], ['markClean' => true]);
To get a list of all dirty fields of an Entity
you may call:
- $dirtyFields = $entity->getDirty();
New in version 3.4.3: getDirty()
has been added.
New in version 3.5.0: isDirty()
, setDirty()
were added.
Validation Errors
After you save an entity any validation errors will bestored on the entity itself. You can access any validation errors using thegetErrors()
, getError()
or hadErrors()
methods:
- // Get all the errors
- $errors = $user->getErrors();
- // Prior to 3.4.0
- $errors = $user->errors();
- // Get the errors for a single field.
- $errors = $user->getError('password');
- // Prior to 3.4.0
- $errors = $user->errors('password');
- // Does the entity or any nested entity have an error.
- $user->hasErrors();
- // Does only the root entity have an error
- $user->hasErrors(false);
The setErrors()
or setError()
method can also be used to set the errorson an entity, making it easier to test code that works with error messages:
- $user->setError('password', ['Password is required']);
- $user->setErrors([
- 'password' => ['Password is required'],
- 'username' => ['Username is required']
- ]);
- // Prior to 3.4.0
- $user->errors('password', ['Password is required.']);
New in version 3.7.0: hasErrors()
was added.
Mass Assignment
While setting fields to entities in bulk is simple and convenient, it cancreate significant security issues. Bulk assigning user data from the requestinto an entity allows the user to modify any and all columns. When usinganonymous entity classes or creating the entity class with the Bake ConsoleCakePHP does not protect against mass-assignment.
The _accessible
property allows you to provide a map of fields andwhether or not they can be mass-assigned. The values true
and false
indicate whether a field can or cannot be mass-assigned:
- namespace App\Model\Entity;
- use Cake\ORM\Entity;
- class Article extends Entity
- {
- protected $_accessible = [
- 'title' => true,
- 'body' => true
- ];
- }
In addition to concrete fields there is a special *
field which defines thefallback behavior if a field is not specifically named:
- namespace App\Model\Entity;
- use Cake\ORM\Entity;
- class Article extends Entity
- {
- protected $_accessible = [
- 'title' => true,
- 'body' => true,
- '*' => false,
- ];
- }
Note
If the *
field is not defined it will default to false
.
Avoiding Mass Assignment Protection
When creating a new entity using the new
keyword you can tell it to notprotect itself against mass assignment:
- use App\Model\Entity\Article;
- $article = new Article(['id' => 1, 'title' => 'Foo'], ['guard' => false]);
Modifying the Guarded Fields at Runtime
You can modify the list of guarded fields at runtime using the accessible
method:
- // Make user_id accessible.
- $article->accessible('user_id', true);
- // Make title guarded.
- $article->accessible('title', false);
Note
Modifying accessible fields affects only the instance the method is calledon.
When using the newEntity()
and patchEntity()
methods in the Table
objects you can customize mass assignment protection with options. Please referto the Changing Accessible Fields section for more information.
Bypassing Field Guarding
There are some situations when you want to allow mass-assignment to guardedfields:
- $article->set($fields, ['guard' => false]);
By setting the guard
option to false
, you can ignore the accessiblefield list for a single call to set()
.
Checking if an Entity was Persisted
It is often necessary to know if an entity represents a row that is alreadyin the database. In those situations use the isNew()
method:
- if (!$article->isNew()) {
- echo 'This article was saved already!';
- }
If you are certain that an entity has already been persisted, you can useisNew()
as a setter:
- $article->isNew(false);
- $article->isNew(true);
Lazy Loading Associations
While eager loading associations is generally the most efficient way to accessyour associations, there may be times when you need to lazily load associateddata. Before we get into how to lazy load associations, we should discuss thedifferences between eager loading and lazy loading associations:
- Eager loading
- Eager loading uses joins (where possible) to fetch data from thedatabase in as few queries as possible. When a separate query is required,like in the case of a HasMany association, a single query is emitted tofetch all the associated data for the current set of objects.
- Lazy loading
- Lazy loading defers loading association data until it is absolutelyrequired. While this can save CPU time because possibly unused data is nothydrated into objects, it can result in many more queries being emitted tothe database. For example looping over a set of articles & their commentswill frequently emit N queries where N is the number of articles beingiterated.
While lazy loading is not included by CakePHP’s ORM, you can just use one of thecommunity plugins to do so. We recommend the LazyLoad Plugin
After adding the plugin to your entity, you will be able to do the following:
- $article = $this->Articles->findById($id);
- // The comments property was lazy loaded
- foreach ($article->comments as $comment) {
- echo $comment->body;
- }
Creating Re-usable Code with Traits
You may find yourself needing the same logic in multiple entity classes. PHP’straits are a great fit for this. You can put your application’s traits insrc/Model/Entity. By convention traits in CakePHP are suffixed withTrait
so they can be discernible from classes or interfaces. Traits areoften a good complement to behaviors, allowing you to provide functionality forthe table and entity objects.
For example if we had SoftDeletable plugin, it could provide a trait. This traitcould give methods for marking entities as ‘deleted’, the method softDelete
could be provided by a trait:
- // SoftDelete/Model/Entity/SoftDeleteTrait.php
- namespace SoftDelete\Model\Entity;
- trait SoftDeleteTrait
- {
- public function softDelete()
- {
- $this->set('deleted', true);
- }
- }
You could then use this trait in your entity class by importing it and includingit:
- namespace App\Model\Entity;
- use Cake\ORM\Entity;
- use SoftDelete\Model\Entity\SoftDeleteTrait;
- class Article extends Entity
- {
- use SoftDeleteTrait;
- }
Converting to Arrays/JSON
When building APIs, you may often need to convert entities into arrays or JSONdata. CakePHP makes this simple:
- // Get an array.
- // Associations will be converted with toArray() as well.
- $array = $user->toArray();
- // Convert to JSON
- // Associations will be converted with jsonSerialize hook as well.
- $json = json_encode($user);
When converting an entity to an JSON, the virtual & hidden field lists areapplied. Entities are recursively converted to JSON as well. This means that if youeager loaded entities and their associations CakePHP will correctly handleconverting the associated data into the correct format.
Exposing Virtual Fields
By default virtual fields are not exported when converting entities toarrays or JSON. In order to expose virtual fields you need to make themvisible. When defining your entity class you can provide a list of virtualfield that should be exposed:
- namespace App\Model\Entity;
- use Cake\ORM\Entity;
- class User extends Entity
- {
- protected $_virtual = ['full_name'];
- }
This list can be modified at runtime using virtualProperties
:
- $user->virtualProperties(['full_name', 'is_admin']);
Hiding Fields
There are often fields you do not want exported in JSON or array formats. Forexample it is often unwise to expose password hashes or account recoveryquestions. When defining an entity class, define which fields should behidden:
- namespace App\Model\Entity;
- use Cake\ORM\Entity;
- class User extends Entity
- {
- protected $_hidden = ['password'];
- }
This list can be modified at runtime using hiddenProperties
:
- $user->hiddenProperties(['password', 'recovery_question']);
Storing Complex Types
Accessor & Mutator methods on entities are not intended to contain the logic forserializing and unserializing complex data coming from the database. Refer tothe Saving Complex Types section to understand how your application canstore more complex data types like arrays and objects.