├── README.md └── favorite ├── .gitignore ├── config └── install │ ├── field.field.user.user.field_favorites.yml │ └── field.storage.user.field_favorites.yml ├── favorite.info.yml ├── favorite.libraries.yml ├── favorite.module ├── favorite.routing.yml ├── js ├── .babelrc ├── favorite.bundle.js ├── favorite.js ├── package.json └── webpack.config.js └── src └── Controller └── FavoriteController.php /README.md: -------------------------------------------------------------------------------- 1 | # react-drupal-example 2 | An example Drupal 8 module with React integration 3 | 4 | The 'favorite' module adds an entity reference field to user accounts to store nodes that they have favorited. (You can remove the field from form displaying on account edit at /admin/config/people/accounts/form-display ). 5 | 6 | Using React.js nodes will get a Favorite/Unfavorite link. 7 | 8 | To work with the js file favorite/js/favorite.js, you need to run 'npm install' in the js directory to initialize. You also need to install webpack on your machine: 'sudo npm install webpack -g' 9 | 10 | When you make changes to favorite.js, run 'webpack' in the js directory to compile to favorite.bundle.js. 11 | -------------------------------------------------------------------------------- /favorite/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | *.log 4 | -------------------------------------------------------------------------------- /favorite/config/install/field.field.user.user.field_favorites.yml: -------------------------------------------------------------------------------- 1 | uuid: bd33923d-b550-4e8a-b5ca-9177ccd6e590 2 | langcode: en 3 | status: true 4 | dependencies: 5 | config: 6 | - field.storage.user.field_favorites 7 | - node.type.article 8 | - node.type.page 9 | module: 10 | - user 11 | id: user.user.field_favorites 12 | field_name: field_favorites 13 | entity_type: user 14 | bundle: user 15 | label: Favorites 16 | description: '' 17 | required: false 18 | translatable: false 19 | default_value: { } 20 | default_value_callback: '' 21 | settings: 22 | handler: 'default:node' 23 | handler_settings: 24 | target_bundles: 25 | article: article 26 | page: page 27 | sort: 28 | field: _none 29 | auto_create: false 30 | auto_create_bundle: article 31 | field_type: entity_reference 32 | -------------------------------------------------------------------------------- /favorite/config/install/field.storage.user.field_favorites.yml: -------------------------------------------------------------------------------- 1 | uuid: d88ef6f4-1896-48ce-998b-baf8584dcb93 2 | langcode: en 3 | status: true 4 | dependencies: 5 | module: 6 | - node 7 | - user 8 | id: user.field_favorites 9 | field_name: field_favorites 10 | entity_type: user 11 | type: entity_reference 12 | settings: 13 | target_type: node 14 | module: core 15 | locked: false 16 | cardinality: -1 17 | translatable: true 18 | indexes: { } 19 | persist_with_no_fields: false 20 | custom_storage: false 21 | -------------------------------------------------------------------------------- /favorite/favorite.info.yml: -------------------------------------------------------------------------------- 1 | core: 8.x 2 | type: module 3 | name: Favorite 4 | description: 'Example React integration module.' 5 | dependencies: 6 | - jsonapi -------------------------------------------------------------------------------- /favorite/favorite.libraries.yml: -------------------------------------------------------------------------------- 1 | favorite: 2 | js: 3 | js/favorite.bundle.js: {} 4 | -------------------------------------------------------------------------------- /favorite/favorite.module: -------------------------------------------------------------------------------- 1 | '
', 12 | ); 13 | } 14 | } -------------------------------------------------------------------------------- /favorite/favorite.routing.yml: -------------------------------------------------------------------------------- 1 | favorite.data: 2 | path: '/favorite/data/{node}' 3 | defaults: 4 | _title: 'Get favorite data' 5 | _controller: '\Drupal\favorite\Controller\FavoriteController::getData' 6 | requirements: 7 | _access: 'TRUE' 8 | node: \d+ -------------------------------------------------------------------------------- /favorite/js/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["es2015", "stage-2", "react"] 3 | } 4 | -------------------------------------------------------------------------------- /favorite/js/favorite.js: -------------------------------------------------------------------------------- 1 | import React, {Component} from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import 'whatwg-fetch'; // https://www.npmjs.com/package/whatwg-fetch 4 | 5 | class Favorite extends Component { 6 | 7 | constructor() { 8 | super(); 9 | this.toggleFavorite = this.toggleFavorite.bind(this); 10 | this.saveFavorite = this.saveFavorite.bind(this); 11 | this.getData = this.getData.bind(this); 12 | 13 | this.state = { 14 | favorited: '', 15 | user_uuid: '', 16 | user_uid: '0', 17 | node_type: '', 18 | node_uuid: '' 19 | } 20 | this.getData(); 21 | } 22 | 23 | getData() { 24 | var path = drupalSettings.path.currentPath; 25 | var nid = path.split('/')[1]; // This assumes the path is like node/123 26 | 27 | fetch('/favorite/data/' + nid, { 28 | method: 'GET', 29 | credentials: 'include', 30 | headers: { 31 | 'Content-Type': 'application/vnd.api+json' 32 | } 33 | }).then((response) => { 34 | if (response.ok) { 35 | response.json().then((data) => { 36 | this.setState({ 37 | favorited: data.favorited, 38 | user_uid: data.user_uid, 39 | user_uuid: data.user_uuid, 40 | node_type: data.node_type, 41 | node_uuid: data.node_uuid 42 | }); 43 | }); 44 | } 45 | else { 46 | console.log('error getting data'); 47 | } 48 | }); 49 | } 50 | 51 | toggleFavorite() { 52 | var favorited = !this.state.favorited; 53 | this.saveFavorite(favorited); 54 | } 55 | 56 | saveFavorite(favorited) { 57 | var endpoint = '/jsonapi/user/user/' + this.state.user_uuid + '/relationships/field_favorites'; 58 | var method = 'POST'; 59 | if (!favorited) { 60 | method = 'DELETE'; 61 | } 62 | fetch(endpoint, { 63 | method: method, 64 | credentials: 'include', 65 | headers: { 66 | 'Accept': 'application/vnd.api+json', 67 | 'Content-Type': 'application/vnd.api+json' 68 | }, 69 | body: JSON.stringify({ 70 | "data": [ 71 | {"type": 'node--' + this.state.node_type, "id": this.state.node_uuid} 72 | ] 73 | }) 74 | }).then((response) => { 75 | if (response.ok) { 76 | response.json().then((data) => { 77 | this.setState({ 78 | favorited: favorited 79 | }); 80 | }); 81 | } 82 | else { 83 | console.log('error favoriting node'); 84 | } 85 | }); 86 | } 87 | 88 | render() { 89 | if (this.state.user_uid == "0") { 90 | return null; 91 | } 92 | var linkClass = 'unfavorited'; 93 | var text = 'Favorite'; 94 | if (this.state.favorited) { 95 | linkClass = 'favorited'; 96 | text = 'Unfavorite'; 97 | } 98 | return ( 99 | {text} 100 | ); 101 | } 102 | } 103 | 104 | ReactDOM.render(, document.getElementById('favorite')); 105 | 106 | -------------------------------------------------------------------------------- /favorite/js/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "favorite", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "react": "^15.6.1", 13 | "react-dom": "^15.6.1", 14 | "whatwg-fetch": "^2.0.3" 15 | }, 16 | "devDependencies": { 17 | "babel-core": "^6.25.0", 18 | "babel-loader": "^7.0.0", 19 | "babel-preset-es2015": "^6.24.1", 20 | "babel-preset-react": "^6.24.1", 21 | "babel-preset-stage-2": "^6.24.1", 22 | "babel-register": "^6.24.1", 23 | "css-loader": "^0.28.4", 24 | "style-loader": "^0.18.2", 25 | "webpack": "^2.6.1" 26 | } 27 | } -------------------------------------------------------------------------------- /favorite/js/webpack.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | entry: ['whatwg-fetch', './favorite.js'], 3 | output: { 4 | path: __dirname, 5 | filename: 'favorite.bundle.js' 6 | }, 7 | module: { 8 | loaders: [ 9 | {test: /\.js$/, exclude: /node_modules/, loader: 'babel-loader'}, 10 | { test: /\.css$/, loader: "style-loader!css-loader" } 11 | ] 12 | } 13 | }; 14 | -------------------------------------------------------------------------------- /favorite/src/Controller/FavoriteController.php: -------------------------------------------------------------------------------- 1 | currentUser()->id()); 14 | $data['user_uid'] = $user->id(); 15 | $data['user_uuid'] = $user->uuid(); 16 | $data['node_type'] = $node->getType(); 17 | $data['node_uuid'] = $node->uuid(); 18 | 19 | $data['favorited'] = FALSE; 20 | $favorites = $user->field_favorites; 21 | if ($favorites) { 22 | foreach ($favorites as $favorite) { 23 | if ($favorite->entity->id() == $node->id()) { 24 | $data['favorited'] = TRUE; 25 | break; 26 | } 27 | } 28 | } 29 | return new JsonResponse($data); 30 | } 31 | } 32 | --------------------------------------------------------------------------------