Say you want to organise your CakePHP models (tables) into subfolders. If you simply move the class into a subfolder, it won't be picked up automatically.
For example, here's how you can tell the TableLocator to look for OrderStatuses
in a specific subfolder Orders
under Model/Table
.
In your controller:
First, prepare the shorthand variables.
$tableRegistry = new \Cake\ORM\TableRegistry();
$tableLocator = $tableRegistry->getTableLocator();
Tell the Table Locator to look in your subfolder. Otherwise, because that subfolder is not a standard location, it won't be checked automatically.
$tableLocator->addLocation('Model/Table/Orders');
$ordersTable = $tableLocator->get('OrderStatuses');
Works for plugins too! Just use the dot notation:
$tableLocator->addLocation('Model/Table/Orders');
$ordersTable = $tableLocator->get('Cart.OrderStatuses');
(You don't have to point to the plugin when adding the location to the table locator in the first line above. Instead, you refer to the plugin using the dot notation inside the $tableLocator->get()
call.)
Using Cart.OrderStatuses
as an example, here are links to relevant code and what happens behind the scenes - provided, you added your subfolder using addLocation()
:
TableLocator::get('Cart.OrderStatuses')
once done with some preliminary checks and gotten the options ready, this method forwards the call to_getClassName
TableLocator::_getClassName('Cart.OrderStatuses', ['alias'=>'OrderStatuses'])
loops through the known locations usingclassName
to see if the class exists; this is where it checks the subfolder we added usingaddLocation()
earlierApp::className('Cart.OrderStatuses', 'Model/Table', 'Table')
picks the plugin name, if any, out of the dot notation in the first argument, and handles the slashes, then calls the below function to check if the requested class actually existsApp::_classExistsInBase('\Model\Table\OrderStatuses', 'Cart')
simply concatenates the two arguments and callsclass_exists('Cart\Model\Table\OrderStatuses')
, returning its result
Be aware that this may cause trouble if you want a table class like that to pick up a specific entity class. Unless you put your entity class into similarly named subfolder under Model/Entity
, you will be getting a default Cake/ORM/Entity
.
To understand why this happens and what are the options, let's look at the getEntityClass()
method where the tables decide which entity class to pick.
On that line, it takes the table class path, drops the last part and appends /Entity
, followed by the alias. This is built on assumption that the table class would be located in Model/Table
, it would drop Table
, append Entity
and get Model/Entity
in the end. But if our table is in Model/Table/Orders
, it ends up looking inside Model/Table/Entity
.
If your entities are actually inside Model/Entity
, than a way around this issue is to set the entity class manually:
$table->setEntityClass('Shop.User');
If you only have the table class name in plural, you can copy the approach used in getEntityClass()
to bring it to singular:
$entityName = \Cake\Utility\Inflector::classify(\Cake\Utility\Inflector::underscore($tableName));