{"action":"created","issue":{"url":"https://api.github.com/repos/flarum/core/issues/871","repository_url":"https://api.github.com/repos/flarum/core","labels_url":"https://api.github.com/repos/flarum/core/issues/871/labels{/name}","comments_url":"https://api.github.com/repos/flarum/core/issues/871/comments","events_url":"https://api.github.com/repos/flarum/core/issues/871/events","html_url":"https://github.com/flarum/core/issues/871","id":141785847,"number":871,"title":"Extract JavaScript data layer (Store and Model) into external package","user":{"login":"tobscure","id":128862,"avatar_url":"https://avatars.githubusercontent.com/u/128862?v=3","gravatar_id":"","url":"https://api.github.com/users/tobscure","html_url":"https://github.com/tobscure","followers_url":"https://api.github.com/users/tobscure/followers","following_url":"https://api.github.com/users/tobscure/following{/other_user}","gists_url":"https://api.github.com/users/tobscure/gists{/gist_id}","starred_url":"https://api.github.com/users/tobscure/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/tobscure/subscriptions","organizations_url":"https://api.github.com/users/tobscure/orgs","repos_url":"https://api.github.com/users/tobscure/repos","events_url":"https://api.github.com/users/tobscure/events{/privacy}","received_events_url":"https://api.github.com/users/tobscure/received_events","type":"User","site_admin":false},"labels":[{"url":"https://api.github.com/repos/flarum/core/labels/API","name":"API","color":"5319e7"},{"url":"https://api.github.com/repos/flarum/core/labels/Cleanup","name":"Cleanup","color":"0052cc"}],"state":"open","locked":false,"assignee":null,"milestone":null,"comments":1,"created_at":"2016-03-18T06:19:59Z","updated_at":"2016-03-19T10:21:25Z","closed_at":null,"body":"Part of #262.\r\n\r\nWould be called tobscure/json-api-client or something.\r\n\r\nProposal for a refined API:\r\n\r\n```js\r\nimport Store from 'tobscure/json-api-client/Store';\r\nimport Model from 'tobscure/json-api-client/Model';\r\n\r\n// We would define a custom adapter as part of Flarum to handle AJAX requests.\r\n// Adapters must simply implement `request(method, url, data)`\r\nconst store = new Store({\r\n  request(method, url, data) {\r\n    return app.request({\r\n      method: method,\r\n      url: baseUrl + url,\r\n      data: data\r\n    });\r\n  }\r\n});\r\n\r\n// Load a preloaded JSON-API document into the store.\r\nstore.sync({\r\n  data: {\r\n    type: 'posts',\r\n    id: '456',\r\n    attributes: {\r\n      content: 'world'\r\n    }\r\n  }\r\n});\r\n\r\n// Push a preloaded resource into the store.\r\nstore.push({\r\n  type: 'discussions',\r\n  id: '123',\r\n  attributes: {\r\n    title: 'hello',\r\n    commentsCount: 1\r\n  },\r\n  relationships: {\r\n    posts: {\r\n      data: [\r\n        {type: 'posts', id: '456'}\r\n      ]\r\n    },\r\n    startUser: {\r\n      data: {type: 'users', id: '789'}\r\n    }\r\n  }\r\n});\r\n\r\n// Now retrieve it locally. (find returns null if the record isn't already\r\n// loaded.) Find by ID by default, but allow finding by other fields too.\r\nconst discussion = store.find('discussions', '123');\r\nconst discussion = store.find('discussions', 'title', 'hello');\r\n\r\n// discussion is a Model instance, which is set up so that all the attributes\r\n// and relationships present in the resource are accessible via getters.\r\nconsole.log(discussion.id); // \"123\"\r\nconsole.log(discussion.type); // \"discussions\"\r\nconsole.log(discussion.title); // \"hello\"\r\nconsole.log(discussion.posts[0].content); // \"world\"\r\n\r\n// Optionally define Model subclasses for the Store to use, so we can specify\r\n// date fields and add custom computed attributes.\r\nclass Discussion extends Model {\r\n  static dates() {\r\n    return ['startTime', 'lastTime'];\r\n  }\r\n\r\n  get repliesCount() {\r\n    return Math.max(0, this.commentsCount - 1);\r\n  }\r\n}\r\n\r\nstore.model('discussions', Discussion);\r\n\r\nconst discussion = store.find('discussions', '123');\r\nconsole.log(discussion.startTime); // Date object\r\nconsole.log(discussion.repliesCount); // 0\r\n\r\n// Remove the given discussion from the local store.\r\nstore.unload(discussion);\r\n\r\n// Can also fetch an array of all loaded records of any given type.\r\nconst discussions = store.all('discussions');\r\n\r\n// Load a discussion into the store by querying the JSON-API. Returns an A+ Promise.\r\nstore.load('discussions', '123').then(discussion => console.log(discussion));\r\n\r\n// Save attributes and relationships for a model.\r\nstore.save(discussion, attributes, relationships);\r\n\r\n// Delete a model.\r\nstore.delete(discussion);\r\n\r\n// Create a new model.\r\nstore.create('discussions', attributes, relationships);\r\n```\r\n\r\nThoughts?"},"comment":{"url":"https://api.github.com/repos/flarum/core/issues/comments/198680400","html_url":"https://github.com/flarum/core/issues/871#issuecomment-198680400","issue_url":"https://api.github.com/repos/flarum/core/issues/871","id":198680400,"user":{"login":"franzliedke","id":249125,"avatar_url":"https://avatars.githubusercontent.com/u/249125?v=3","gravatar_id":"","url":"https://api.github.com/users/franzliedke","html_url":"https://github.com/franzliedke","followers_url":"https://api.github.com/users/franzliedke/followers","following_url":"https://api.github.com/users/franzliedke/following{/other_user}","gists_url":"https://api.github.com/users/franzliedke/gists{/gist_id}","starred_url":"https://api.github.com/users/franzliedke/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/franzliedke/subscriptions","organizations_url":"https://api.github.com/users/franzliedke/orgs","repos_url":"https://api.github.com/users/franzliedke/repos","events_url":"https://api.github.com/users/franzliedke/events{/privacy}","received_events_url":"https://api.github.com/users/franzliedke/received_events","type":"User","site_admin":false},"created_at":"2016-03-19T10:21:25Z","updated_at":"2016-03-19T10:21:25Z","body":"Makes sense. Any big changes compared to the current API?\r\n\r\nAnd is this connected to Mithril at all?"}}