{"id":1553,"date":"2024-02-13T19:37:53","date_gmt":"2024-02-13T19:37:53","guid":{"rendered":"https:\/\/editjournal.redakt.eu\/faxmodem\/?p=1553"},"modified":"2024-02-15T12:12:58","modified_gmt":"2024-02-15T12:12:58","slug":"cakephp-model-subfolder","status":"publish","type":"post","link":"https:\/\/editjournal.redakt.eu\/faxmodem\/blog\/development\/cakephp\/cakephp-model-subfolder\/","title":{"rendered":"Structuring CakePHP models in subfolders"},"content":{"rendered":"<p>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. <\/p>\n<p>For example, here's how you can tell the TableLocator to look for <code>OrderStatuses<\/code> in a specific subfolder <code>Orders<\/code> under <code>Model\/Table<\/code>.<\/p>\n<p>In your controller:<\/p>\n<p>First, prepare the shorthand variables.<\/p>\n<pre><code>$tableRegistry = new \\Cake\\ORM\\TableRegistry();\n$tableLocator = $tableRegistry-&gt;getTableLocator();<\/code><\/pre>\n<p>Tell the Table Locator to look in your subfolder. Otherwise, because that subfolder is not a standard location, it won't be checked automatically.<\/p>\n<pre><code>$tableLocator-&gt;addLocation(&#039;Model\/Table\/Orders&#039;);\n$ordersTable = $tableLocator-&gt;get(&#039;OrderStatuses&#039;);<\/code><\/pre>\n<p>Works for plugins too! Just use the dot notation:<\/p>\n<pre><code>$tableLocator-&gt;addLocation(&#039;Model\/Table\/Orders&#039;);\n$ordersTable = $tableLocator-&gt;get(&#039;Cart.OrderStatuses&#039;);<\/code><\/pre>\n<p>(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 <code>$tableLocator-&gt;get()<\/code> call.)<\/p>\n<p>Using <code>Cart.OrderStatuses<\/code> as an example, here are links to relevant code and what happens behind the scenes - provided, you added your subfolder using <code>addLocation()<\/code>:<\/p>\n<ol>\n<li><a href=\"https:\/\/github.com\/cakephp\/cakephp\/blob\/ffe95db83c37e1039c97664125d604363faf1a08\/src\/ORM\/Locator\/TableLocator.php#L198\"><code>TableLocator::get(&#039;Cart.OrderStatuses&#039;)<\/code><\/a> once done with some preliminary checks and gotten the options ready, this method forwards the call to <code>_getClassName<\/code><\/li>\n<li><a href=\"https:\/\/github.com\/cakephp\/cakephp\/blob\/ffe95db83c37e1039c97664125d604363faf1a08\/src\/ORM\/Locator\/TableLocator.php#L265\"><code>TableLocator::_getClassName(&#039;Cart.OrderStatuses&#039;, [&#039;alias&#039;=&gt;&#039;OrderStatuses&#039;])<\/code><\/a> loops through the known locations using <code>className<\/code> to see if the class exists; this is where it checks the subfolder we added using <code>addLocation()<\/code> earlier<\/li>\n<li><a href=\"https:\/\/github.com\/cakephp\/cakephp\/blob\/ffe95db83c37e1039c97664125d604363faf1a08\/src\/Core\/App.php#L52\"><code>App::className(&#039;Cart.OrderStatuses&#039;, &#039;Model\/Table&#039;, &#039;Table&#039;)<\/code><\/a> 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 exists<\/li>\n<li><a href=\"https:\/\/github.com\/cakephp\/cakephp\/blob\/ffe95db83c37e1039c97664125d604363faf1a08\/src\/Core\/App.php#L149\"><code>App::_classExistsInBase(&#039;\\Model\\Table\\OrderStatuses&#039;, &#039;Cart&#039;)<\/code><\/a> simply concatenates the two arguments and calls <code>class_exists(&#039;Cart\\Model\\Table\\OrderStatuses&#039;)<\/code>, returning its result<\/li>\n<\/ol>\n<p>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 <code>Model\/Entity<\/code>, you will be getting a default <code>Cake\/ORM\/Entity<\/code>.<\/p>\n<p>To understand why this happens and what are the options, <a href=\"https:\/\/github.com\/cakephp\/cakephp\/blob\/ffe95db83c37e1039c97664125d604363faf1a08\/src\/ORM\/Table.php#L795\">let's look at the <code>getEntityClass()<\/code> method<\/a> where the tables decide which entity class to pick.<\/p>\n<p>On that line, it takes the table class path, drops the last part and appends <code>\/Entity<\/code>, followed by the alias. This is built on assumption that the table class would be located in <code>Model\/Table<\/code>, it would drop <code>Table<\/code>, append <code>Entity<\/code> and get <code>Model\/Entity<\/code> in the end. But if our table is in <code>Model\/Table\/Orders<\/code>, it ends up looking inside <code>Model\/Table\/Entity<\/code>.<\/p>\n<p>If your entities are actually inside <code>Model\/Entity<\/code>, than a way around this issue is to set the entity class manually:<\/p>\n<pre><code>$table-&gt;setEntityClass(&#039;Shop.User&#039;);<\/code><\/pre>\n<p>If you only have the table class name in plural, you can copy the approach used in <code>getEntityClass()<\/code> to bring it to singular:<\/p>\n<pre><code>$entityName = \\Cake\\Utility\\Inflector::classify(\\Cake\\Utility\\Inflector::underscore($tableName));<\/code><\/pre>\n","protected":false},"excerpt":{"rendered":"<p>Say you want to organise your CakePHP models (tables) into subfolders. If you simply move the class into a subfolder, it won&#8217;t be picked up automatically. For example, here&#8217;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&hellip; <a class=\"more-link\" href=\"https:\/\/editjournal.redakt.eu\/faxmodem\/blog\/development\/cakephp\/cakephp-model-subfolder\/\">Continue reading <span class=\"screen-reader-text\">Structuring CakePHP models in subfolders<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1047],"tags":[715,1050],"class_list":["post-1553","post","type-post","status-publish","format-standard","hentry","category-cakephp","tag-cakephp","tag-cakephp-plugin-development","entry"],"_links":{"self":[{"href":"https:\/\/editjournal.redakt.eu\/faxmodem\/wp-json\/wp\/v2\/posts\/1553","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/editjournal.redakt.eu\/faxmodem\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/editjournal.redakt.eu\/faxmodem\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/editjournal.redakt.eu\/faxmodem\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/editjournal.redakt.eu\/faxmodem\/wp-json\/wp\/v2\/comments?post=1553"}],"version-history":[{"count":6,"href":"https:\/\/editjournal.redakt.eu\/faxmodem\/wp-json\/wp\/v2\/posts\/1553\/revisions"}],"predecessor-version":[{"id":1560,"href":"https:\/\/editjournal.redakt.eu\/faxmodem\/wp-json\/wp\/v2\/posts\/1553\/revisions\/1560"}],"wp:attachment":[{"href":"https:\/\/editjournal.redakt.eu\/faxmodem\/wp-json\/wp\/v2\/media?parent=1553"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/editjournal.redakt.eu\/faxmodem\/wp-json\/wp\/v2\/categories?post=1553"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/editjournal.redakt.eu\/faxmodem\/wp-json\/wp\/v2\/tags?post=1553"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}