<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":29,"date":"2015-05-08T18:14:41","date_gmt":"2015-05-09T00:14:41","guid":{"rendered":"http:\/\/www.munderwood.ca\/?p=29"},"modified":"2015-05-28T14:56:04","modified_gmt":"2015-05-28T20:56:04","slug":"29","status":"publish","type":"post","link":"https:\/\/www.munderwood.ca\/index.php\/2015\/05\/08\/29\/","title":{"rendered":"Arrays of joined ids"},"content":{"rendered":"<p>Thanks to a helpful\u00a0<a href=\"http:\/\/stackoverflow.com\/a\/29919974\/607408\">stack overflow response<\/a>, I now have an improved solution to my problem of needing to add arrays of joined ids to JSON output from CakePHP 3. The output from a test containing Widget models that can each have many Foos looks something like this:<\/p>\n<pre class=\"lang:js decode:true  \" title=\"Sample output\">{\r\n    \"widgets\": [\r\n        {\r\n            \"id\": 1,\r\n            \"foo_ids\": [ 1 ]\r\n        },\r\n        {\r\n            \"id\": 3,\r\n            \"foo_ids\": [ ]\r\n        },\r\n        {\r\n            \"id\": 2,\r\n            \"foo_ids\": [ 1, 2, 3 ]\r\n        }\r\n    ]\r\n}<\/pre>\n<p>&nbsp;<\/p>\n<p>To easily and automatically add this capability to any model I wanted, I created a new class extending Cake&#8217;s\u00a0<span class=\"theme:tomorrow lang:php decode:true  crayon-inline \">Table<\/span>\u00a0, which I called \u201cApiTable\u201d:<\/p>\n<pre class=\"lang:default decode:true\" title=\"ApiTable class skeleton\">class ApiTable extends Table {\r\n  public function findForApi (...) {\r\n    ...\r\n  }\r\n\r\n  public function formatWithJoinedIds (...) {\r\n    ...\r\n  }\r\n}<\/pre>\n<p>The\u00a0<span class=\"theme:tomorrow lang:php decode:true  crayon-inline\">formatWithJoinedIds()<\/span>\u00a0method adds an array called\u00a0<span class=\"theme:tomorrow lang:php decode:true  crayon-inline\">model_ids<\/span>\u00a0for each model, from an input list, that is associated with this one through a &#8216;BelongsToMany&#8217; or &#8216;HasMany&#8217; join, without including the contents of the associated models. It is intended to be called from the\u00a0<span class=\"theme:tomorrow lang:php decode:true  crayon-inline \">findApi()<\/span>\u00a0method, which simply performs a query containing the passed-in desired list of models and formats the results.<\/p>\n<p>The <a href=\"http:\/\/book.cakephp.org\/3.0\/en\/orm\/retrieving-data-and-resultsets.html#custom-finder-methods\">custom finder<\/a> is straightforward:<\/p>\n<pre class=\"lang:php decode:true\" title=\"A custom finder\">public function findForApi (Query $query, array $includeIdsFor) {\r\n  $query\r\n    -&gt;contain($includeIdsFor)\r\n    -&gt;formatResults([$this, 'formatWithJoinedIds']);\r\n  return $query;\r\n}<\/pre>\n<p>The array\u00a0<span class=\"theme:tomorrow lang:php decode:true  crayon-inline \">$includeIdsFor<\/span>\u00a0should contain strings identifying the associated properties whose joined ids should be included. For instance, if a Widget has many associated Foos, Bars, and Bazzes, the ids of the foos and bars would be included in the output by passing\u00a0<span class=\"theme:tomorrow lang:php decode:true  crayon-inline\">[&#8216;Foos&#8217;, &#8216;Bars&#8217;]<\/span>.<\/p>\n<p>The result formatter, used to add the\u00a0<a href=\"http:\/\/book.cakephp.org\/3.0\/en\/orm\/query-builder.html#adding-calculated-fields\">calculated fields<\/a>\u00a0containing the id arrays, is a little longer but not much more complicated:<\/p>\n<pre class=\"lang:php decode:true\" title=\"Result formatter to return id arrays\">public function formatWithJoinedIds ($results, $keepJoinedData) {\r\n  \/\/ Array of all associations this model has many models of\r\n  $joins = array_merge(\r\n    $this-&gt;associations()-&gt;type('BelongsToMany'),\r\n    $this-&gt;associations()-&gt;type('HasMany'),\r\n    []\r\n  );\r\n  \/\/ Map each result row to itself, with the array id fields\r\n  \/\/ added, and optionally foreign model data removed\r\n  return $results-&gt;map(\r\n    function ($row) use ($joins, $keepJoinedData) {\r\n      foreach ($joins as $join) {\r\n        $property = $join-&gt;property();\r\n        if (isset($row[$property])) {\r\n          \/\/ Start a new id array for this property\r\n          $ids = [];\r\n          foreach ($row[$property] as $joinedToRow) {\r\n              $ids[] = $joinedToRow['id'];\r\n          }\r\n          \/\/ Turn e.g. 'foos' into 'foo_ids', and add this\r\n          \/\/ as a new property of the row\r\n          $row[Inflector::singularize($property) . '_ids'] = $ids;\r\n          if (!$keepJoinedData) {\r\n            \/\/ Optionally remove the associated data\r\n            unset($row[$property]);\r\n          }\r\n        }\r\n      }\r\n      return $row;\r\n    }\r\n  );\r\n}<\/pre>\n<p>This routine generates an array of all properties of the current model that point to many of another model. It then iterates over these properties, and for each one iterates over all of the foreign models to collect an array of their ids. The resulting array is added as a new property to the row, and the foreign models themselves are unset if they are not to be included in the output.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Thanks to a helpful\u00a0stack overflow response, I now have an improved solution to my problem of needing to add arrays of joined ids to JSON output from CakePHP 3. The output from a test containing Widget models that can each have many Foos looks something like this: { &#8220;widgets&#8221;: [ { &#8220;id&#8221;: 1, &#8220;foo_ids&#8221;: [ &#8230; <span class=\"more\"><a class=\"more-link\" href=\"https:\/\/www.munderwood.ca\/index.php\/2015\/05\/08\/29\/\">[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":[1],"tags":[],"_links":{"self":[{"href":"https:\/\/www.munderwood.ca\/index.php\/wp-json\/wp\/v2\/posts\/29"}],"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=29"}],"version-history":[{"count":9,"href":"https:\/\/www.munderwood.ca\/index.php\/wp-json\/wp\/v2\/posts\/29\/revisions"}],"predecessor-version":[{"id":43,"href":"https:\/\/www.munderwood.ca\/index.php\/wp-json\/wp\/v2\/posts\/29\/revisions\/43"}],"wp:attachment":[{"href":"https:\/\/www.munderwood.ca\/index.php\/wp-json\/wp\/v2\/media?parent=29"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.munderwood.ca\/index.php\/wp-json\/wp\/v2\/categories?post=29"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.munderwood.ca\/index.php\/wp-json\/wp\/v2\/tags?post=29"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}