Warning: The magic method SFML_Singleton::__wakeup() must have public visibility in /home/public/wp-content/plugins/sf-move-login/inc/classes/class-sfml-singleton.php on line 72

Warning: Cannot modify header information - headers already sent by (output started at /home/public/wp-content/plugins/sf-move-login/inc/classes/class-sfml-singleton.php:72) in /home/public/wp-includes/rest-api/class-wp-rest-server.php on line 1642

Warning: Cannot modify header information - headers already sent by (output started at /home/public/wp-content/plugins/sf-move-login/inc/classes/class-sfml-singleton.php:72) in /home/public/wp-includes/rest-api/class-wp-rest-server.php on line 1642

Warning: Cannot modify header information - headers already sent by (output started at /home/public/wp-content/plugins/sf-move-login/inc/classes/class-sfml-singleton.php:72) in /home/public/wp-includes/rest-api/class-wp-rest-server.php on line 1642

Warning: Cannot modify header information - headers already sent by (output started at /home/public/wp-content/plugins/sf-move-login/inc/classes/class-sfml-singleton.php:72) in /home/public/wp-includes/rest-api/class-wp-rest-server.php on line 1642

Warning: Cannot modify header information - headers already sent by (output started at /home/public/wp-content/plugins/sf-move-login/inc/classes/class-sfml-singleton.php:72) in /home/public/wp-includes/rest-api/class-wp-rest-server.php on line 1642

Warning: Cannot modify header information - headers already sent by (output started at /home/public/wp-content/plugins/sf-move-login/inc/classes/class-sfml-singleton.php:72) in /home/public/wp-includes/rest-api/class-wp-rest-server.php on line 1642

Warning: Cannot modify header information - headers already sent by (output started at /home/public/wp-content/plugins/sf-move-login/inc/classes/class-sfml-singleton.php:72) in /home/public/wp-includes/rest-api/class-wp-rest-server.php on line 1642

Warning: Cannot modify header information - headers already sent by (output started at /home/public/wp-content/plugins/sf-move-login/inc/classes/class-sfml-singleton.php:72) in /home/public/wp-includes/rest-api/class-wp-rest-server.php on line 1642
{"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":"

Thanks to a helpful\u00a0stack 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

{\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>\n

To easily and automatically add this capability to any model I wanted, I created a new class extending Cake’s\u00a0Table<\/span>\u00a0, which I called \u201cApiTable\u201d:<\/p>\n

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

The\u00a0formatWithJoinedIds()<\/span>\u00a0method adds an array called\u00a0model_ids<\/span>\u00a0for each model, from an input list, that is associated with this one through a ‘BelongsToMany’ or ‘HasMany’ join, without including the contents of the associated models. It is intended to be called from the\u00a0findApi()<\/span>\u00a0method, which simply performs a query containing the passed-in desired list of models and formats the results.<\/p>\n

The custom finder<\/a> is straightforward:<\/p>\n

public function findForApi (Query $query, array $includeIdsFor) {\r\n  $query\r\n    ->contain($includeIdsFor)\r\n    ->formatResults([$this, 'formatWithJoinedIds']);\r\n  return $query;\r\n}<\/pre>\n

The array\u00a0$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[‘Foos’, ‘Bars’]<\/span>.<\/p>\n

The result formatter, used to add the\u00a0calculated fields<\/a>\u00a0containing the id arrays, is a little longer but not much more complicated:<\/p>\n

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->associations()->type('BelongsToMany'),\r\n    $this->associations()->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->map(\r\n    function ($row) use ($joins, $keepJoinedData) {\r\n      foreach ($joins as $join) {\r\n        $property = $join->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

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":"

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: { “widgets”: [ { “id”: 1, “foo_ids”: [ … [Read more…]<\/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}]}}