Table Objects

  • class Cake\ORM\Table

Table objects provide access to the collection of entities stored in a specifictable. Each table in your application should have an associated Table classwhich is used to interact with a given table. If you do not need to customizethe behavior of a given table CakePHP will generate a Table instance for you touse.

Before trying to use Table objects and the ORM, you should ensure that you haveconfigured your database connection.

Basic Usage

To get started, create a Table class. These classes live insrc/Model/Table. Tables are a type model collection specific to relationaldatabases, and the main interface to your database in CakePHP’s ORM. The mostbasic table class would look like:

  1. // src/Model/Table/ArticlesTable.php
  2. namespace App\Model\Table;
  3.  
  4. use Cake\ORM\Table;
  5.  
  6. class ArticlesTable extends Table
  7. {
  8. }

Note that we did not tell the ORM which table to use for our class. Byconvention table objects will use a table that matches the lower cased andunderscored version of the class name. In the above example the articlestable will be used. If our table class was named BlogPosts your table shouldbe named blog_posts. You can specify the table to use by using the setTable()method:

  1. namespace App\Model\Table;
  2.  
  3. use Cake\ORM\Table;
  4.  
  5. class ArticlesTable extends Table
  6. {
  7. public function initialize(array $config): void
  8. {
  9. $this->setTable('my_table');
  10. }
  11. }

No inflection conventions will be applied when specifying a table. By conventionthe ORM also expects each table to have a primary key with the name of id.If you need to modify this you can use the setPrimaryKey() method:

  1. namespace App\Model\Table;
  2.  
  3. use Cake\ORM\Table;
  4.  
  5. class ArticlesTable extends Table
  6. {
  7. public function initialize(array $config): void
  8. {
  9. $this->setPrimaryKey('my_id');
  10. }
  11. }

Customizing the Entity Class a Table Uses

By default table objects use an entity class based on naming conventions. Forexample if your table class is called ArticlesTable the entity would beArticle. If the table class was PurchaseOrdersTable the entity would bePurchaseOrder. If however, you want to use an entity that doesn’t follow theconventions you can use the setEntityClass() method to change things up:

  1. class PurchaseOrdersTable extends Table
  2. {
  3. public function initialize(array $config): void
  4. {
  5. $this->setEntityClass('App\Model\Entity\PO');
  6. }
  7. }

As seen in the examples above Table objects have an initialize() methodwhich is called at the end of the constructor. It is recommended that you usethis method to do initialization logic instead of overriding the constructor.

Getting Instances of a Table Class

Before you can query a table, you’ll need to get an instance of the table. Youcan do this by using the TableLocator class:

  1. // In a controller or table method.
  2. use Cake\ORM\TableRegistry;
  3.  
  4. $articles = TableRegistry::getTableLocator()->get('Articles');

TableLocator provides the various dependencies for constructinga table, and maintains a registry of all the constructed table instances makingit easier to build relations and configure the ORM. SeeUsing the TableLocator for more information.

If your table class is in a plugin, be sure to use the correct name for yourtable class. Failing to do so can result in validation rules, or callbacks notbeing triggered as a default class is used instead of your actual class. Tocorrectly load plugin table classes use the following:

  1. // Plugin table
  2. $articlesTable = TableRegistry::getTableLocator()->get('PluginName.Articles');
  3.  
  4. // Vendor prefixed plugin table
  5. $articlesTable = TableRegistry::getTableLocator()->get('VendorName/PluginName.Articles');

Lifecycle Callbacks

As you have seen above table objects trigger a number of events. Events areuseful if you want to hook into the ORM and add logic in without subclassing oroverriding methods. Event listeners can be defined in table or behavior classes.You can also use a table’s event manager to bind listeners in.

When using callback methods behaviors attached in theinitialize() method will have their listeners fired before the tablecallback methods are triggered. This follows the same sequencing as controllers& components.

To add an event listener to a Table class or Behavior simply implement themethod signatures as described below. See the Events System formore detail on how to use the events subsystem.

Event List

  • Model.initialize
  • Model.beforeMarshal
  • Model.beforeFind
  • Model.buildValidator
  • Model.buildRules
  • Model.beforeRules
  • Model.afterRules
  • Model.beforeSave
  • Model.afterSave
  • Model.afterSaveCommit
  • Model.beforeDelete
  • Model.afterDelete
  • Model.afterDeleteCommit

initialize

  • Cake\ORM\Table::initialize(EventInterface $event, ArrayObject $data, ArrayObject $options)

The Model.initialize event is fired after the constructor and initializemethods are called. The Table classes do not listen to this event bydefault, and instead use the initialize hook method.

To respond to the Model.initialize event you can create a listener classwhich implements EventListenerInterface:

  1. use Cake\Event\EventListenerInterface;
  2. class ModelInitializeListener implements EventListenerInterface
  3. {
  4. public function implementedEvents()
  5. {
  6. return array(
  7. 'Model.initialize' => 'initializeEvent',
  8. );
  9. }
  10.  
  11. public function initializeEvent($event): void
  12. {
  13. $table = $event->getSubject();
  14. // do something here
  15. }
  16. }

and attach the listener to the EventManager as below:

  1. use Cake\Event\EventManager;
  2. $listener = new ModelInitializeListener();
  3. EventManager::instance()->attach($listener);

This will call the initializeEvent when any Table class is constructed.

beforeMarshal

  • Cake\ORM\Table::beforeMarshal(EventInterface $event, ArrayObject $data, ArrayObject $options)

The Model.beforeMarshal event is fired before request data is convertedinto entities. See the Modifying Request Data Before Building Entities documentation for more information.

beforeFind

  • Cake\ORM\Table::beforeFind(EventInterface $event, Query $query, ArrayObject $options, $primary)

The Model.beforeFind event is fired before each find operation. By stoppingthe event and supplying a return value you can bypass the find operationentirely. Any changes done to the $query instance will be retained for the restof the find. The $primary parameter indicates whether or not this is the rootquery, or an associated query. All associations participating in a query willhave a Model.beforeFind event triggered. For associations that use joins,a dummy query will be provided. In your event listener you can set additionalfields, conditions, joins or result formatters. These options/features will becopied onto the root query.

You might use this callback to restrict find operations based on a user’s role,or make caching decisions based on the current load.

In previous versions of CakePHP there was an afterFind callback, this hasbeen replaced with the Modifying Results with Map/Reduce features and entity constructors.

buildValidator

  • Cake\ORM\Table::buildValidator(EventInterface $event, Validator $validator, $name)

The Model.buildValidator event is fired when $name validator is created.Behaviors, can use this hook to add in validation methods.

buildRules

  • Cake\ORM\Table::buildRules(EventInterface $event, RulesChecker $rules)

The Model.buildRules event is fired after a rules instance has beencreated and after the table’s buildRules() method has been called.

beforeRules

  • Cake\ORM\Table::beforeRules(EventInterface $event, EntityInterface $entity, ArrayObject $options, $operation)

The Model.beforeRules event is fired before an entity has had rules applied. Bystopping this event, you can halt the rules checking and set the resultof applying rules.

afterRules

  • Cake\ORM\Table::afterRules(EventInterface $event, EntityInterface $entity, ArrayObject $options, $result, $operation)

The Model.afterRules event is fired after an entity has rules applied. Bystopping this event, you can return the final value of the rules checkingoperation.

beforeSave

  • Cake\ORM\Table::beforeSave(EventInterface $event, EntityInterface $entity, ArrayObject $options)

The Model.beforeSave event is fired before each entity is saved. Stoppingthis event will abort the save operation. When the event is stopped the resultof the event will be returned.

afterSave

  • Cake\ORM\Table::afterSave(EventInterface $event, EntityInterface $entity, ArrayObject $options)

The Model.afterSave event is fired after an entity is saved.

afterSaveCommit

  • Cake\ORM\Table::afterSaveCommit(EventInterface $event, EntityInterface $entity, ArrayObject $options)

The Model.afterSaveCommit event is fired after the transaction in which thesave operation is wrapped has been committed. It’s also triggered for non atomicsaves where database operations are implicitly committed. The event is triggeredonly for the primary table on which save() is directly called. The event isnot triggered if a transaction is started before calling save.

beforeDelete

  • Cake\ORM\Table::beforeDelete(EventInterface $event, EntityInterface $entity, ArrayObject $options)

The Model.beforeDelete event is fired before an entity is deleted. Bystopping this event you will abort the delete operation. When the event is stopped the resultof the event will be returned.

afterDelete

  • Cake\ORM\Table::afterDelete(EventInterface $event, EntityInterface $entity, ArrayObject $options)

The Model.afterDelete event is fired after an entity has been deleted.

afterDeleteCommit

  • Cake\ORM\Table::afterDeleteCommit(EventInterface $event, EntityInterface $entity, ArrayObject $options)

The Model.afterDeleteCommit event is fired after the transaction in which thedelete operation is wrapped has been is committed. It’s also triggered for nonatomic deletes where database operations are implicitly committed. The event istriggered only for the primary table on which delete() is directly called.The event is not triggered if a transaction is started before calling delete.

Stopping Table Events

To prevent the save from continuing, simply stop event propagation in your callback:

  1. public function beforeSave(Event $event, EntityInterface $entity, ArrayObject $options)
  2. {
  3. if (...) {
  4. $event->stopPropagation();
  5. $event->setResult(false);
  6. return;
  7. }
  8. ...
  9. }

Alternatively, you can return false from the callback. This has the same effect as stopping event propagation.

Callback priorities

When using events on your tables and behaviors be aware of the priorityand the order listeners are attached. Behavior events are attached before Tableevents are. With the default priorities this means that Behavior callbacks aretriggered before the Table event with the same name.

As an example, if your Table is using TreeBehavior theTreeBehavior::beforeDelete() method will be called before your table’sbeforeDelete() method, and you will not be able to work wth the child nodesof the record being deleted in your Table’s method.

You can manage event priorities in one of a few ways:

  • Change the priority of a Behavior’s listeners using the priorityoption. This will modify the priority of all callback methods in theBehavior:
  1. // In a Table initialize() method
  2. $this->addBehavior('Tree', [
  3. // Default value is 10 and listeners are dispatched from the
  4. // lowest to highest priority.
  5. 'priority' => 2,
  6. ]);
  • Modify the priority in your Table class by using theModel.implementedEvents() method. This allows you to assign a differentpriority per callback-function:
  1. // In a Table class.
  2. public function implementedEvents()
  3. {
  4. $events = parent::implementedEvents();
  5. $events['Model.beforeDelete'] = [
  6. 'callable' => 'beforeDelete',
  7. 'priority' => 3
  8. ];
  9.  
  10. return $events;
  11. }

Behaviors

  • Cake\ORM\Table::addBehavior($name, array $options = [])

Behaviors provide an easy way to create horizontally re-usable pieces of logicrelated to table classes. You may be wondering why behaviors are regular classesand not traits. The primary reason for this is event listeners. While traitswould allow for re-usable pieces of logic, they would complicate binding events.

To add a behavior to your table you can call the addBehavior() method.Generally the best place to do this is in the initialize() method:

  1. namespace App\Model\Table;
  2.  
  3. use Cake\ORM\Table;
  4.  
  5. class ArticlesTable extends Table
  6. {
  7. public function initialize(array $config): void
  8. {
  9. $this->addBehavior('Timestamp');
  10. }
  11. }

As with associations, you can use plugin syntax and provide additionalconfiguration options:

  1. namespace App\Model\Table;
  2.  
  3. use Cake\ORM\Table;
  4.  
  5. class ArticlesTable extends Table
  6. {
  7. public function initialize(array $config): void
  8. {
  9. $this->addBehavior('Timestamp', [
  10. 'events' => [
  11. 'Model.beforeSave' => [
  12. 'created_at' => 'new',
  13. 'modified_at' => 'always'
  14. ]
  15. ]
  16. ]);
  17. }
  18. }

You can find out more about behaviors, including the behaviors provided byCakePHP in the chapter on Behaviors.

Configuring Connections

By default all table instances use the default database connection. If yourapplication uses multiple database connections you will want to configure whichtables use which connections. This is the defaultConnectionName() method:

  1. namespace App\Model\Table;
  2.  
  3. use Cake\ORM\Table;
  4.  
  5. class ArticlesTable extends Table
  6. {
  7. public static function defaultConnectionName() {
  8. return 'replica_db';
  9. }
  10. }

Note

The defaultConnectionName() method must be static.

Using the TableLocator

  • class Cake\ORM\TableLocator

As we’ve seen earlier, the TableRegistry class provides an easy way to usefactory/registry for accessing your applications table instances. It provides afew other useful features as well.

Configuring Table Objects

  • Cake\ORM\TableLocator::get($alias, $config)

When loading tables from the registry you can customize their dependencies, oruse mock objects by providing an $options array:

  1. $articles = TableRegistry::getTableLocator()->get('Articles', [
  2. 'className' => 'App\Custom\ArticlesTable',
  3. 'table' => 'my_articles',
  4. 'connection' => $connectionObject,
  5. 'schema' => $schemaObject,
  6. 'entityClass' => 'Custom\EntityClass',
  7. 'eventManager' => $eventManager,
  8. 'behaviors' => $behaviorRegistry
  9. ]);

Pay attention to the connection and schema configuration settings, they aren’tstring values but objects. The connection will take an object ofCake\Database\Connection and schema Cake\Database\Schema\Collection.

Note

If your table also does additional configuration in its initialize() method,those values will overwrite the ones provided to the registry.

You can also pre-configure the registry using the setConfig() method.Configuration data is stored per alias, and can be overridden by an object’sinitialize() method:

  1. TableRegistry::getTableLocator()->setConfig('Users', ['table' => 'my_users']);

Note

You can only configure a table before or during the first time youaccess that alias. Doing it after the registry is populated will have noeffect.

Flushing the Registry

  • Cake\ORM\TableLocator::clear()

During test cases you may want to flush the registry. Doing so is often usefulwhen you are using mock objects, or modifying a table’s dependencies:

  1. TableRegistry::getTableLocator()->clear();

Configuring the Namespace to Locate ORM classes

If you have not followed the conventions it is likely that your Table orEntity classes will not be detected by CakePHP. In order to fix this, you canset a namespace with the Cake\Core\Configure::write method. As an example:

  1. /src
  2. /App
  3. /My
  4. /Namespace
  5. /Model
  6. /Entity
  7. /Table

Would be configured with:

  1. Cake\Core\Configure::write('App.namespace', 'App\My\Namespace');